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.










share|improve this question







New contributor




Jarak is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
























    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.










    share|improve this question







    New contributor




    Jarak is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
    Check out our Code of Conduct.






















      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.










      share|improve this question







      New contributor




      Jarak is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.











      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






      share|improve this question







      New contributor




      Jarak is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.











      share|improve this question







      New contributor




      Jarak is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.









      share|improve this question




      share|improve this question






      New contributor




      Jarak is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.









      asked yesterday









      Jarak

      101




      101




      New contributor




      Jarak is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.





      New contributor





      Jarak is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.






      Jarak is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.



























          active

          oldest

          votes











          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });






          Jarak is a new contributor. Be nice, and check out our Code of Conduct.










          draft saved

          draft discarded


















          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






























          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.










          draft saved

          draft discarded


















          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.




          draft saved


          draft discarded














          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





















































          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







          Popular posts from this blog

          Morgemoulin

          Scott Moir

          Souastre