pythonimage-processingcomputer-visionedge-detectionstraight-line-detection

Finding a faint line in a very noisy image with variable background noise


I have a very noisy greyscale image where the noise level modulates along one axis. Within this image, there may be a very faint line of some unknown width. I want to be able to identify when this line is in the image. The application I have in mind requires this to be automated.

The following code can be used to generate two synthetic examples.

[Plot of back], an extreme example.

[Plot of back2], a less extreme example.

import numpy as np

def background(N, M, nl, nf, nT, r):
    a = nf*np.sin((2*np.pi/nT)*np.arange(N))
    a=a.reshape(-1,1)
    fuzz = nl + a*np.ones((N,M))
    fuzz+= r*a*np.random.normal(size=(N,M))

    return fuzz

def add_slider(B, s, v, w, a, r):
    N, M = B.shape
    amps = a+r*np.random.normal(size=M)

    x=np.arange(N)
    for i, y in enumerate(range(s, N, v)):
        if i >= M: break
        B[:,i]+=amps[i]*np.exp(-(x-y)**2/(2*w**2))

def subtracted(im):
    return im - im.mean(1).reshape(-1, 1)

B = background(1000, 300, 0, 5, 100, 1)
add_slider(B, 0, 2, 40, 1, 1)
back = subtracted(B)

# For a less extreme example:

B2 = gs.background(1000, 300, 0, 5, 100, 1)
gs.add_slider(B2, 0, 2, 40, 5, 1)
back2 = subtracted(B2)

I've tried a number of things, but none of them work very well when I change some of the parameters used to generate the image. For example, I've tried applying a Gaussian blur, which works sometimes, but getting the right width can be very finicky, and it struggles when the line is more vertical. I've also tried a rotating mask like in this post, but it seems to get thwarted by the modulating noise level. I've also done a little experimenting with radon transforms, but I can't find anything consistent there either. Most standard edge detection methods like Canny don't seem to work, because they tend to rely on local gradients, which get totally ruined by noise.

I've done some limited searching for papers on this topic, but I haven't found anything that suits this problem well. If possible, I want to find a solution that doesn't rely on knowing much about the line, like its thickness.


Solution

  • Here is another approach in Imagemagick 7 basically doing an -auto-threshold otsu followed by a median filter.

    Note that I cropped your image to remove the added borders.

    Input 1:

    enter image description here

    magick test.png -auto-threshold otsu -statistic median 21x21 test1_thresh_median.png
    

    enter image description here

    Input 2:

    enter image description here

    magick test2.png -auto-threshold otsu -statistic median 51x51 test2_thresh_median.png
    

    enter image description here