1

Currently I am trying to get rid of all background noises from my images.

I'm pretty sure I have to use some kind of Dilation or Erosion algorithm but I also like to achieve coding the filter routine in Swift.

This is my unfiltered noisy image:

unfiltered Image

And this could be what It should look like after applying the filter:

filtered Image

Note: Using openCV the code should look maybe like this (but like I'd mentioned- I'd like to use Swift instead):

img = cv2.imread("img.png")
bggray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
labelnum, labelimg, contours, GoCs = cv2.connectedComponentsWithStats(bggray)
for label in xrange(1, labelnum):
    x,y,w,h,size = contours[label]
    if size <= 50:
         img[y:y+h, x:x+w] = 0
cv2.imwrite("img.png", img)

Note: This is the Swift code I got so far but it is obviously not working this way:

class CleanupFilter: CIFilter {
    var inputImage : CIImage?
    var threshold1: Float = 0.5
    var threshold2: Float = 0.7
    var thresholdKernel =  CIColorKernel(source:
        "kernel vec4 thresholdKernel(sampler image, float threshold1, float threshold2) {" +
            "vec4 pixel = sample(image, samplerCoord(image));" +
            "const vec3 rgbToIntensity = vec3(0.114, 0.587, 0.299);" +
            "float intensity = dot(pixel.rgb, rgbToIntensity);" +
            "if (intensity < threshold1) {return vec4(0, 0, 0, 0)}" +
            "if (intensity < threshold1 && intensity > threshold2) {return vec4(1, 1, 1, 1)}" +
            "else {return vec4(0, 0, 0, 0)}" + "}")
    override var outputImage: CIImage! {
        guard let inputImage = inputImage,
            let thresholdKernel = thresholdKernel else {
                return nil
        }
        let extent = inputImage.extent
        let arguments : [Any] = [inputImage, threshold1, threshold2]
        return thresholdKernel.apply(extent: extent, arguments: arguments)
    }
}

Any help how to edit thresholdKernel to achieve a working filter routine would be very appreciated.

2 Answers2

2

Try this:

extension UIImage {
    var noiseReducted: UIImage? {
        guard let openGLContext = EAGLContext(api: .openGLES2) else { return self }
        let ciContext = CIContext(eaglContext: openGLContext)

        guard let noiseReduction = CIFilter(name: "CINoiseReduction") else { return self }
        noiseReduction.setValue(CIImage(image: self), forKey: kCIInputImageKey)
        noiseReduction.setValue(0.02, forKey: "inputNoiseLevel")
        noiseReduction.setValue(0.40, forKey: "inputSharpness")

        if let output = noiseReduction.outputImage,
            let cgImage = ciContext.createCGImage(output, from: output.extent) {
            return UIImage(cgImage: cgImage, scale: scale, orientation: imageOrientation)
        }

        return nil
    }
}
Torongo
  • 1,021
  • 1
  • 7
  • 14
1

You should remove noise before binarization because as you can see your noise is quite big and really not very well distinguishable from important data. That is done by blurring your image a bit before processing by any FIR filter.

If you really need to remove noise from input like yours then there are few possibilities each with its quirks:

  1. morphology operators

    you can apply erosion few times which will shrink all objects. It is usable If the noise thickness is smaller than your objects so your objects still stays there but distorted a bit of coarse. In your case That would work on the smaller "dots" only

  2. segmentate & threshold

    you can segmentate all objects on the image and detect/remove noise. In your case area of noise is far smaller than the object you want to preserve. So count the area (number of set pixels) for each object (for example by flood fill) and if the object has smaller area than threshold delete it (flood fill with background color).

    You can threshold any property you want like surface area, aspect ratio, bounding box size,circumference length to area ratio, etc. This approach does not distort details but is considerably slower and in case of not properly handled recursive fills it is a potential stack overflow nightmare for higher resolutions.

Spektre
  • 49,595
  • 11
  • 110
  • 380
  • Do you get some example code for the second method "**segmentate & threshold**? Would be very appreciated to see some partly working code ;) –  Mar 11 '18 at 12:22
  • @jonas I do not code for your platform. Anyway IIRC I already have an C++ implementation of this as an answer here on SO/SE but cant find it now ... maybe question was deleted or just cant remember the title keywords as the SO/SE search engine is not very good for this. Anyway here is flood fill I based the stuff on [C++ Flood fill](https://stackoverflow.com/a/37810355/2521214) you just add counter that increments for each recolored pixel. – Spektre Mar 11 '18 at 13:20
  • @jonas now to segment you search image for first set pixel (not background color `c0` nor specific color `c1` ). If found flood fill it by some specific color `c1` (that is not present in the original image) and count how many pixels was filled. if noise flood fill again with background color `c0`. If not continue search. If you increment your specific color than you effectively segmented you r image with labeling *the color will be label of your object) but for that you will need to know safe range for specific colors not coliding with original image colors ... – Spektre Mar 11 '18 at 13:24
  • @jonas just found it :) [Feature detection technique to measure the length of curved tubes in an image](https://stackoverflow.com/a/45501854/2521214) it use A* fill instead of Flood fill but that does not matter in your case. What you want is `_floodfill_n` You will find there both thresholding and filling implementation in C++ – Spektre Mar 11 '18 at 13:48