Basic image median filter
up vote
0
down vote
favorite
Recently I implemented the most basic moving window transform version of a median filter that I could (it was actually just to have a comparison for another method, so I wasn't trying to optimise it heavily at the time), simply using the Array.Parallel higher-order functions to get some parallelism. The full code for it is available at this GitHub repo, but the only parts that I think are particularly relevant for performance purposes are the following two functions:
let processWindow clampedArrayFunc windowSize x y =
let mutable intensities = List.Empty
let posBound = (windowSize - 1) / 2
let negBound = -posBound
for z in negBound..posBound do
for w in negBound..posBound do
intensities <- (clampedArrayFunc (x + z) (y + w)) :: intensities
List.choose id intensities |> findMedian
let medianFilter intensities width height windowSize =
let ac = accessClampedArray intensities width height
let pw = processWindow ac windowSize
let outputPixels = Array.Parallel.map (fun i ->
let x = i % width
let y = i / width
pw x y |> makeRgb24
) [|0..intensities.Length-1|]
Image.LoadPixelData(outputPixels, width, height)
In this case, the 'intensities' are the pixel values of the grayscale image, stored as unsigned 8-bit integers in a 1D array. The clampedArrayFunc
is a partially applied function that looks up given coordinates in an image, and if they are within an image returns the pixel value inside a Some
, otherwise it returns None
- I did this as the easiest way I could think of to reliably ignore any requested pixel locations that are outside the image.
findMedian
simply sorts an array in place then takes the middle value. makeRgb24
recreates a full 24-bit pixel out of the selected intensity value, for further use with ImageSharp to store the image.
I have no doubts that I have made some sub-optimal choices here. In the interests of learning to be a better programmer, I would like to learn anything and everything I can about how to improve the runtime performance of it. Any and all constructive feedback is, of course, welcome though!
For reference, I did a very quick benchmark of it the other day, and running on a Core i7 7700 in release mode, it was managing about 0.88s on a 1.09 megapixel image, excluding IO.
performance reinventing-the-wheel f# memory-optimization .net-core
New contributor
add a comment |
up vote
0
down vote
favorite
Recently I implemented the most basic moving window transform version of a median filter that I could (it was actually just to have a comparison for another method, so I wasn't trying to optimise it heavily at the time), simply using the Array.Parallel higher-order functions to get some parallelism. The full code for it is available at this GitHub repo, but the only parts that I think are particularly relevant for performance purposes are the following two functions:
let processWindow clampedArrayFunc windowSize x y =
let mutable intensities = List.Empty
let posBound = (windowSize - 1) / 2
let negBound = -posBound
for z in negBound..posBound do
for w in negBound..posBound do
intensities <- (clampedArrayFunc (x + z) (y + w)) :: intensities
List.choose id intensities |> findMedian
let medianFilter intensities width height windowSize =
let ac = accessClampedArray intensities width height
let pw = processWindow ac windowSize
let outputPixels = Array.Parallel.map (fun i ->
let x = i % width
let y = i / width
pw x y |> makeRgb24
) [|0..intensities.Length-1|]
Image.LoadPixelData(outputPixels, width, height)
In this case, the 'intensities' are the pixel values of the grayscale image, stored as unsigned 8-bit integers in a 1D array. The clampedArrayFunc
is a partially applied function that looks up given coordinates in an image, and if they are within an image returns the pixel value inside a Some
, otherwise it returns None
- I did this as the easiest way I could think of to reliably ignore any requested pixel locations that are outside the image.
findMedian
simply sorts an array in place then takes the middle value. makeRgb24
recreates a full 24-bit pixel out of the selected intensity value, for further use with ImageSharp to store the image.
I have no doubts that I have made some sub-optimal choices here. In the interests of learning to be a better programmer, I would like to learn anything and everything I can about how to improve the runtime performance of it. Any and all constructive feedback is, of course, welcome though!
For reference, I did a very quick benchmark of it the other day, and running on a Core i7 7700 in release mode, it was managing about 0.88s on a 1.09 megapixel image, excluding IO.
performance reinventing-the-wheel f# memory-optimization .net-core
New contributor
add a comment |
up vote
0
down vote
favorite
up vote
0
down vote
favorite
Recently I implemented the most basic moving window transform version of a median filter that I could (it was actually just to have a comparison for another method, so I wasn't trying to optimise it heavily at the time), simply using the Array.Parallel higher-order functions to get some parallelism. The full code for it is available at this GitHub repo, but the only parts that I think are particularly relevant for performance purposes are the following two functions:
let processWindow clampedArrayFunc windowSize x y =
let mutable intensities = List.Empty
let posBound = (windowSize - 1) / 2
let negBound = -posBound
for z in negBound..posBound do
for w in negBound..posBound do
intensities <- (clampedArrayFunc (x + z) (y + w)) :: intensities
List.choose id intensities |> findMedian
let medianFilter intensities width height windowSize =
let ac = accessClampedArray intensities width height
let pw = processWindow ac windowSize
let outputPixels = Array.Parallel.map (fun i ->
let x = i % width
let y = i / width
pw x y |> makeRgb24
) [|0..intensities.Length-1|]
Image.LoadPixelData(outputPixels, width, height)
In this case, the 'intensities' are the pixel values of the grayscale image, stored as unsigned 8-bit integers in a 1D array. The clampedArrayFunc
is a partially applied function that looks up given coordinates in an image, and if they are within an image returns the pixel value inside a Some
, otherwise it returns None
- I did this as the easiest way I could think of to reliably ignore any requested pixel locations that are outside the image.
findMedian
simply sorts an array in place then takes the middle value. makeRgb24
recreates a full 24-bit pixel out of the selected intensity value, for further use with ImageSharp to store the image.
I have no doubts that I have made some sub-optimal choices here. In the interests of learning to be a better programmer, I would like to learn anything and everything I can about how to improve the runtime performance of it. Any and all constructive feedback is, of course, welcome though!
For reference, I did a very quick benchmark of it the other day, and running on a Core i7 7700 in release mode, it was managing about 0.88s on a 1.09 megapixel image, excluding IO.
performance reinventing-the-wheel f# memory-optimization .net-core
New contributor
Recently I implemented the most basic moving window transform version of a median filter that I could (it was actually just to have a comparison for another method, so I wasn't trying to optimise it heavily at the time), simply using the Array.Parallel higher-order functions to get some parallelism. The full code for it is available at this GitHub repo, but the only parts that I think are particularly relevant for performance purposes are the following two functions:
let processWindow clampedArrayFunc windowSize x y =
let mutable intensities = List.Empty
let posBound = (windowSize - 1) / 2
let negBound = -posBound
for z in negBound..posBound do
for w in negBound..posBound do
intensities <- (clampedArrayFunc (x + z) (y + w)) :: intensities
List.choose id intensities |> findMedian
let medianFilter intensities width height windowSize =
let ac = accessClampedArray intensities width height
let pw = processWindow ac windowSize
let outputPixels = Array.Parallel.map (fun i ->
let x = i % width
let y = i / width
pw x y |> makeRgb24
) [|0..intensities.Length-1|]
Image.LoadPixelData(outputPixels, width, height)
In this case, the 'intensities' are the pixel values of the grayscale image, stored as unsigned 8-bit integers in a 1D array. The clampedArrayFunc
is a partially applied function that looks up given coordinates in an image, and if they are within an image returns the pixel value inside a Some
, otherwise it returns None
- I did this as the easiest way I could think of to reliably ignore any requested pixel locations that are outside the image.
findMedian
simply sorts an array in place then takes the middle value. makeRgb24
recreates a full 24-bit pixel out of the selected intensity value, for further use with ImageSharp to store the image.
I have no doubts that I have made some sub-optimal choices here. In the interests of learning to be a better programmer, I would like to learn anything and everything I can about how to improve the runtime performance of it. Any and all constructive feedback is, of course, welcome though!
For reference, I did a very quick benchmark of it the other day, and running on a Core i7 7700 in release mode, it was managing about 0.88s on a 1.09 megapixel image, excluding IO.
performance reinventing-the-wheel f# memory-optimization .net-core
performance reinventing-the-wheel f# memory-optimization .net-core
New contributor
New contributor
New contributor
asked yesterday
Jarak
101
101
New contributor
New contributor
add a comment |
add a comment |
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
Jarak is a new contributor. Be nice, and check out our Code of Conduct.
Jarak is a new contributor. Be nice, and check out our Code of Conduct.
Jarak is a new contributor. Be nice, and check out our Code of Conduct.
Jarak is a new contributor. Be nice, and check out our Code of Conduct.
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f208856%2fbasic-image-median-filter%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown