Python edge detection of low contrast image

Viewed 840

I want to draw find the depth of the drawed region and draw the boundaries I tried to use Canny edge detection, but it is not good to get the edges. Are there ways to get the pixel of the boundary of interest? The pixel intensity (in grayscale) in the boundary line is not nuch different from the surrounding area. The region of interest is melting pool on a metal. The purpose is to find the depth of the melting pool. I tried Canny edge detection but it seems not work to solve the problem. Are there other ways using python to coordinates of boundary of melting pool boundary which I colored in red in picture 2?Original image Original imageRegion of interest (in red) Region of interest Canny edge detection Canny edge detectionThe melting pool is moving. I want to use python to get the depth change of the melting pool. I have bunch of imagesmelting pool 1melting pool 2

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('image.tif',0)
edges = cv2.Canny(img,100,20)

plt.subplots(),plt.imshow(img,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplots(),plt.imshow(edges,cmap = 'gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])

plt.show()
2 Answers

Strong horizontal lowpass filtering will improve the signal-to-noise ratio and make the top edge easy to detect.

enter image description here

Note that straight binarization of the raw image performs even better.

enter image description here

Adaptive thresholding is interesting as well, though requires some tuning.

enter image description here

I think the best way to obtain the desired information about the metal pool is to segment it. Since the image is noisy, I think graph-cut is the better option.

I estimate the pool boundary with the vertical Scharr filtering and use them to compute the graph arc-weight.

scharr

From this, I use the upper and lower bourders of the image as source and sink for the graph-cut algorithm (these pixels will belong to different labels).

Second segmentation is performed to obtain the horizontal line without the pool and compute their difference to obtain the final result.

The beta parameter must be tuned, as it increases it will adhere more to your weights (noisy boundary). I found that 50 gets good results, but you should play with it.

import numpy as np
from skimage import io, filters, measure
import skimage.morphology as morph
import matplotlib.pyplot as plt
import maxflow


def normalize(im):
    im -= im.min()
    return im / im.max()


def graph_cut(weights):
    g = maxflow.GraphFloat()
    nodeids = g.add_grid_nodes(weights.shape)
    structure = maxflow.vonNeumann_structure(ndim=2, directed=True)

    g.add_grid_edges(nodeids, weights, structure=structure, symmetric=True)
    g.add_grid_tedges(nodeids[1, :], 0, 1e16)
    g.add_grid_tedges(nodeids[-1, :], 1e16, 0)
    g.maxflow()

    return g.get_grid_segments(nodeids)


def get_largest(label):
    label = measure.label(label)
    largest = label == np.argmax(np.bincount(label.flat)[1:])+1
    return largest 


def main():
    im = io.imread("example.png")
    im = filters.median(im, morph.disk(5))

    # pool segmentation
    beta = 50  # parameter

    aux = filters.scharr_v(im)  
    aux = normalize(np.abs(aux))
    weights = np.exp(-beta * aux)
    pool = graph_cut(weights) 
    # end

    # surface segmentation
    aux = np.abs(filters.scharr(im))
    aux = normalize(aux)
    weights = np.exp(-aux)
    surf = graph_cut(weights)
    # end

    # result
    res = pool ^ surf  # xor
    res = get_largest(res)
    contours = measure.find_contours(res, 0.5)

    fig, ax = plt.subplots()

    ax.imshow(im, cmap='gray')
    for contour in contours:
        ax.plot(contour[:, 1], contour[:, 0], linewidth=1, c = 'red')

    plt.show()


if __name__ == "__main__":
    main()

Results:

res1

res2

Related