SixLabors / ImageSharp

:camera: A modern, cross-platform, 2D Graphics library for .NET
https://sixlabors.com/products/imagesharp/
Other
7.35k stars 850 forks source link

Feature request: Add Median filter #814

Closed dejanberic closed 2 years ago

dejanberic commented 5 years ago

This is a simple filter which can be used to filter out noise.

Here is one paper on fast Median and Bilateral filtering: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.93.1608&rep=rep1&type=pdf

michasacuer commented 5 years ago

Referring to gitter talk witn @antonfirsov i take that issue. I will start implementing it in february

antonfirsov commented 5 years ago

@michasacuer great news! Thanks! 👍

Jarak-Jakar commented 5 years ago

Just in case it helps (probably not, but I don't think it can hurt to mention it) I was recently working on basic median filtering, i.e. simply taking the median of a square window. You can see the most up-to-date version of the fastest program I came up with here - feel free to look at everything else in the repo, but I think that'll be by far the most useful part of it. It is written in F# though, so might not be of as much use to you. Moreover, I have undoubtedly done something silly, or somehow not used ImageSharp effectively. I also didn't get around to implementing more interesting types of median filters.

Based on some quick profiling of my program, it appears to spend about 60% of its time in the Array.sort function. I tried implementing some alternatives like Insertion Sort and Shell Sort (I did think about Radix sort, but I can't remember actually testing it), but nothing came close to being as fast as the built-in .NET sort once the arrays got bigger than about 10 elements. Someone suggested that I move from sorting the array to using a selection algorithm like QuickSelect, but I never got around to implementing it/trying it out.

I hope that this helps 😃

EDIT: Just remembered something else that might be of interest: I created a Golang version of the F# program to have a quick comparison (hidden away elsewhere in that repo). When running them both sequentially, the Golang version runs about 1.75x faster, though I never could determine exactly why that should be the case. .NET provides a much easier way to parallelise array operations however (I literally just need to change Array.map to Array.Parallel.map on one line), and when I use that, the F# version runs about twice as fast on my quad-core machine.

michasacuer commented 5 years ago

Thanks for help! I will start to implementing this after my exams (i think it will be int the next one, two weeks)

FransBouma commented 4 years ago

Isn't this a 9 tap tent filter, as described by Smith? https://pdfs.semanticscholar.org/cdf7/1f6ea2178cfa210a30c5870c235f3acdc42a.pdf

In HLSL:

    void PS_TentFilter(float4 vpos : SV_Position, float2 texcoord : TEXCOORD, out float4 fragment : SV_Target0)
    {
        float4 coord = BUFFER_PIXEL_SIZE.xyxy * float4(1, 1, -1, 0);
        float4 average;
        average = tex2D(SamplerCDBuffer2, texcoord - coord.xy);
        average += tex2D(SamplerCDBuffer2, texcoord - coord.wy) * 2;
        average += tex2D(SamplerCDBuffer2, texcoord - coord.zy);
        average += tex2D(SamplerCDBuffer2, texcoord + coord.zw) * 2;
        average += tex2D(SamplerCDBuffer2, texcoord) * 4;
        average += tex2D(SamplerCDBuffer2, texcoord + coord.xw) * 2;
        average += tex2D(SamplerCDBuffer2, texcoord + coord.zy);
        average += tex2D(SamplerCDBuffer2, texcoord + coord.wy) * 2;
        average += tex2D(SamplerCDBuffer2, texcoord + coord.xy);
        fragment = average / 16;
    }

not sure how fast this is in CPU space however.

JimBobSquarePants commented 2 years ago

Fixed via #2219 !