Tergiver
Tergiver

Reputation: 14517

Very Simple Edge Detection

I have a simple, high-contrast, black & white video image on which I need to find the X value of a vertical edge and the Y value of a horizontal edge. I also need to know the direction of the edges (white->black or black->white).

There will be:

  1. Zero or one horizontal edge
  2. Zero or one vertical edge.
  3. The edges are perfectly straight.
  4. The edges will be defined by a sharp(ish) transition from black to white or white to black.

I've been reading about edge detection algorithms and understanding very little of it, but what I do understand is that they are all far more complex than my requirements. They are all designed to produce an image of simple edges from a complex image. My requirements (my images) are much simpler, I only need the X and a Y value of zero to two edges.

If I plot say one row of pixels on a graph (X=pixel index, Y=pixel intensity), I will get something like a line, a rising slope, and another line. The center point of the slope would be what I need. But the lines are not perfect. They are noisy and there can be small bumps or dips, or it can be (mostly) flat if no edge is visible.

So is there a simple algorithm to smooth that plot and produce both the direction and the center point of "the slope"?

Sample Image

Upvotes: 4

Views: 3650

Answers (3)

A.3dhgi
A.3dhgi

Reputation: 95

If you want to smooth the edges, the 2D-Convolution filter from OpenCV could help you. I don't know if you are already using OpenCV-libraries. That's one of the most useful libraries in Image Processing.

If you want to fill the gaps in your line, you could use the Opening Filter again from OpenCV.

To use the Edge Detection Algorithms, you don't need to understand them. The advantage is that you can simply test it and see if it gives you the result you want. I personally use the Canny Edge Detection to detect the edges in a Black White X-Ray image.

include "opencv2/imgproc/imgproc.hpp"

    //// find out largest gradient in image
    //edgeDetection( m_data->m_originalXRay , m_data->m_segmentedXRay, m_data->m_width, m_data->m_height);
    //double largestGradient = 0;
    //for (int y = 0; y<m_data->m_height; y++)
    //  for (int x = 0; x<m_data->m_width; x++) 
    //      if (m_data->m_segmentedXRay[y*m_data->m_width + x] > largestGradient)
    //          largestGradient = m_data->m_segmentedXRay[y*m_data->m_width + x];

    //upper = 0.2 * largestGradient;
    //lower = 0.08 * largestGradient;

in above I've written an Algorithm to find the upper and lower threshold of canny automaticlly what you could set of course manually.

    //cv::morphologyEx(imageVector, imageVector, cv::MORPH_OPEN,     cv::Mat(5,5,CV_8UC1));

Thats where i use the Opening Filter to fill the small gaps in the edges.

int blurSize = 3;
    cv::blur(imageVector, imageVector, cv::Size(blurSize,blurSize));

I just used the smoothing filter Blur.

    bool L2gradient = false;
    cv::Canny(imageVector , imageVector, lower, upper, kernelSize, L2gradient);

Upvotes: 2

Mark Setchell
Mark Setchell

Reputation: 207465

I would approach this using ImageMagick straight from the command line - it is installed on most Linux distros and is available for OSX and Windows too.

So, firstly, I would go to greyscale and normalise the contrast so that you are less susceptible to variations in exposure or lighting. Then I would threshold at 50% so that all the pixels are forced to either black or white.

convert line.png -colorspace gray -normalize -threshold 50% result.png

enter image description here

Then I would run a tall, thin median filter (say 100 pixels tall and 1 pixel wide) to smooth the image in the vertical direction (so I can find the horizontal edges).

convert line.png -colorspace gray -normalize -threshold 50% -virtual-pixel mirror -statistic median 1x100 result.png

enter image description here

I would then resize the image to 1 pixel wide and 100 pixels tall to reduce the volume of output and because I would say that to find the edge within 1% of the image height is as much as you can hope for with such a noisy image.

convert line.png -colorspace gray -normalize -threshold 50% -virtual-pixel mirror -statistic median 1x100 -resize 1x100! txt:

# ImageMagick pixel enumeration: 1,100,255,srgb
0,0: (32496,32496,32496)  #7E7E7E  srgb(126,126,126)
0,1: (32969,32969,32969)  #808080  srgb(128,128,128)
0,2: (33498,33498,33498)  #828282  srgb(130,130,130)
0,3: (32632,32632,32632)  #7F7F7F  srgb(127,127,127)
0,4: (32708,32708,32708)  #7F7F7F  srgb(127,127,127)
0,5: (32787,32787,32787)  #808080  srgb(128,128,128)
0,6: (32615,32615,32615)  #7F7F7F  srgb(127,127,127)
0,7: (33186,33186,33186)  #818181  srgb(129,129,129)
0,8: (33786,33786,33786)  #838383  srgb(131,131,131)
0,9: (33610,33610,33610)  #838383  srgb(131,131,131)
0,10: (32577,32577,32577)  #7F7F7F  srgb(127,127,127)
0,11: (33159,33159,33159)  #818181  srgb(129,129,129)
0,12: (33578,33578,33578)  #838383  srgb(131,131,131)
0,13: (33276,33276,33276)  #818181  srgb(129,129,129)
0,14: (33259,33259,33259)  #818181  srgb(129,129,129)
0,15: (32620,32620,32620)  #7F7F7F  srgb(127,127,127)
0,16: (32624,32624,32624)  #7F7F7F  srgb(127,127,127)
0,17: (33131,33131,33131)  #818181  srgb(129,129,129)
0,18: (33278,33278,33278)  #818181  srgb(129,129,129)
0,19: (32680,32680,32680)  #7F7F7F  srgb(127,127,127)
0,20: (33136,33136,33136)  #818181  srgb(129,129,129)
0,21: (32707,32707,32707)  #7F7F7F  srgb(127,127,127)
0,22: (33051,33051,33051)  #818181  srgb(129,129,129)
0,23: (33794,33794,33794)  #838383  srgb(131,131,131)
0,24: (33704,33704,33704)  #838383  srgb(131,131,131)
0,25: (33028,33028,33028)  #818181  srgb(129,129,129)
0,26: (33605,33605,33605)  #838383  srgb(131,131,131)
0,27: (33785,33785,33785)  #838383  srgb(131,131,131)
0,28: (33290,33290,33290)  #828282  srgb(130,130,130)
0,29: (33079,33079,33079)  #818181  srgb(129,129,129)
0,30: (33430,33430,33430)  #828282  srgb(130,130,130)
0,31: (33246,33246,33246)  #818181  srgb(129,129,129)
0,32: (33102,33102,33102)  #818181  srgb(129,129,129)
0,33: (33248,33248,33248)  #818181  srgb(129,129,129)
0,34: (33823,33823,33823)  #848484  srgb(132,132,132)
0,35: (33678,33678,33678)  #838383  srgb(131,131,131)
0,36: (33497,33497,33497)  #828282  srgb(130,130,130)
0,37: (33960,33960,33960)  #848484  srgb(132,132,132)
0,38: (33520,33520,33520)  #828282  srgb(130,130,130)
0,39: (33235,33235,33235)  #818181  srgb(129,129,129)
0,40: (32655,32655,32655)  #7F7F7F  srgb(127,127,127)
0,41: (32825,32825,32825)  #808080  srgb(128,128,128)
0,42: (32847,32847,32847)  #808080  srgb(128,128,128)
0,43: (33214,33214,33214)  #818181  srgb(129,129,129)
0,44: (32966,32966,32966)  #808080  srgb(128,128,128)
0,45: (33262,33262,33262)  #818181  srgb(129,129,129)
0,46: (33178,33178,33178)  #818181  srgb(129,129,129)
0,47: (32501,32501,32501)  #7E7E7E  srgb(126,126,126)
0,48: (32499,32499,32499)  #7E7E7E  srgb(126,126,126)
0,49: (31982,31982,31982)  #7C7C7C  srgb(124,124,124)
0,50: (32564,32564,32564)  #7F7F7F  srgb(127,127,127)
0,51: (32336,32336,32336)  #7E7E7E  srgb(126,126,126)
0,52: (30311,30311,30311)  #767676  srgb(118,118,118)
0,53: (27119,27119,27119)  #6A6A6A  srgb(106,106,106)
0,54: (13002,13002,13002)  #333333  srgb(51,51,51)
0,55: (1855,1855,1855)  #070707  srgb(7,7,7)
0,56: (187,187,187)  #010101  srgb(1,1,1)           <--- This is your edge at 56% of the image height down from the top
0,57: (72,72,72)  #000000  srgb(0,0,0)
0,58: (0,0,0)  #000000  black
0,59: (0,0,0)  #000000  black
0,60: (0,0,0)  #000000  black
0,61: (0,0,0)  #000000  black
0,62: (0,0,0)  #000000  black
0,63: (0,0,0)  #000000  black
0,64: (0,0,0)  #000000  black
0,65: (0,0,0)  #000000  black
0,66: (0,0,0)  #000000  black
0,67: (0,0,0)  #000000  black
0,68: (0,0,0)  #000000  black
0,69: (0,0,0)  #000000  black
0,70: (0,0,0)  #000000  black
0,71: (0,0,0)  #000000  black
0,72: (0,0,0)  #000000  black
0,73: (0,0,0)  #000000  black
0,74: (0,0,0)  #000000  black
0,75: (0,0,0)  #000000  black
0,76: (0,0,0)  #000000  black
0,77: (0,0,0)  #000000  black
0,78: (0,0,0)  #000000  black
0,79: (0,0,0)  #000000  black
0,80: (0,0,0)  #000000  black
0,81: (0,0,0)  #000000  black
0,82: (0,0,0)  #000000  black
0,83: (0,0,0)  #000000  black
0,84: (0,0,0)  #000000  black
0,85: (0,0,0)  #000000  black
0,86: (0,0,0)  #000000  black
0,87: (0,0,0)  #000000  black
0,88: (0,0,0)  #000000  black
0,89: (0,0,0)  #000000  black
0,90: (0,0,0)  #000000  black
0,91: (0,0,0)  #000000  black
0,92: (0,0,0)  #000000  black
0,93: (0,0,0)  #000000  black
0,94: (0,0,0)  #000000  black
0,95: (0,0,0)  #000000  black
0,96: (0,0,0)  #000000  black
0,97: (0,0,0)  #000000  black
0,98: (0,0,0)  #000000  black
0,99: (0,0,0)  #000000  black

Likewise for your vertical edge...

convert line.png -colorspace gray -normalize -threshold 50% -virtual-pixel mirror -statistic median 100x1 -resize 100x1! txt:

# ImageMagick pixel enumeration: 100,1,255,srgb
0,0: (0,0,0)  #000000  black
1,0: (0,0,0)  #000000  black
2,0: (0,0,0)  #000000  black
3,0: (0,0,0)  #000000  black
4,0: (0,0,0)  #000000  black
5,0: (0,0,0)  #000000  black
6,0: (0,0,0)  #000000  black
7,0: (0,0,0)  #000000  black
8,0: (0,0,0)  #000000  black
9,0: (0,0,0)  #000000  black
10,0: (0,0,0)  #000000  black
11,0: (0,0,0)  #000000  black
12,0: (0,0,0)  #000000  black
13,0: (0,0,0)  #000000  black
14,0: (0,0,0)  #000000  black
15,0: (0,0,0)  #000000  black
16,0: (0,0,0)  #000000  black
17,0: (0,0,0)  #000000  black
18,0: (0,0,0)  #000000  black
19,0: (0,0,0)  #000000  black
20,0: (0,0,0)  #000000  black
21,0: (0,0,0)  #000000  black
22,0: (0,0,0)  #000000  black
23,0: (0,0,0)  #000000  black
24,0: (0,0,0)  #000000  black
25,0: (0,0,0)  #000000  black
26,0: (0,0,0)  #000000  black
27,0: (0,0,0)  #000000  black
28,0: (0,0,0)  #000000  black
29,0: (0,0,0)  #000000  black
30,0: (0,0,0)  #000000  black
31,0: (0,0,0)  #000000  black
32,0: (0,0,0)  #000000  black
33,0: (0,0,0)  #000000  black
34,0: (0,0,0)  #000000  black
35,0: (0,0,0)  #000000  black
36,0: (0,0,0)  #000000  black
37,0: (0,0,0)  #000000  black
38,0: (0,0,0)  #000000  black
39,0: (0,0,0)  #000000  black
40,0: (0,0,0)  #000000  black
41,0: (0,0,0)  #000000  black
42,0: (0,0,0)  #000000  black
43,0: (0,0,0)  #000000  black
44,0: (0,0,0)  #000000  black
45,0: (7,7,7)  #000000  srgb(0,0,0)
46,0: (87,87,87)  #000000  srgb(0,0,0)
47,0: (342,342,342)  #010101  srgb(1,1,1)
48,0: (3623,3623,3623)  #0E0E0E  srgb(14,14,14)
49,0: (16943,16943,16943)  #424242  srgb(66,66,66)      <-- This is your vertical edge, 49% of the image width across from the left side
50,0: (29779,29779,29779)  #747474  srgb(116,116,116)
51,0: (33852,33852,33852)  #848484  srgb(132,132,132)
52,0: (34745,34745,34745)  #878787  srgb(135,135,135)
53,0: (35643,35643,35643)  #8B8B8B  srgb(139,139,139)
54,0: (35893,35893,35893)  #8C8C8C  srgb(140,140,140)
55,0: (35932,35932,35932)  #8C8C8C  srgb(140,140,140)
56,0: (35934,35934,35934)  #8C8C8C  srgb(140,140,140)
57,0: (36041,36041,36041)  #8C8C8C  srgb(140,140,140)
58,0: (36095,36095,36095)  #8C8C8C  srgb(140,140,140)
59,0: (35975,35975,35975)  #8C8C8C  srgb(140,140,140)
60,0: (35937,35937,35937)  #8C8C8C  srgb(140,140,140)
61,0: (35937,35937,35937)  #8C8C8C  srgb(140,140,140)
62,0: (36042,36042,36042)  #8C8C8C  srgb(140,140,140)
63,0: (36094,36094,36094)  #8C8C8C  srgb(140,140,140)
64,0: (36148,36148,36148)  #8D8D8D  srgb(141,141,141)
65,0: (36409,36409,36409)  #8E8E8E  srgb(142,142,142)
66,0: (36409,36409,36409)  #8E8E8E  srgb(142,142,142)
67,0: (36375,36375,36375)  #8E8E8E  srgb(142,142,142)
68,0: (36252,36252,36252)  #8D8D8D  srgb(141,141,141)
69,0: (36214,36214,36214)  #8D8D8D  srgb(141,141,141)
70,0: (36176,36176,36176)  #8D8D8D  srgb(141,141,141)
71,0: (36274,36274,36274)  #8D8D8D  srgb(141,141,141)
72,0: (36406,36406,36406)  #8E8E8E  srgb(142,142,142)
73,0: (36366,36366,36366)  #8E8E8E  srgb(142,142,142)
74,0: (36128,36128,36128)  #8D8D8D  srgb(141,141,141)
75,0: (36004,36004,36004)  #8C8C8C  srgb(140,140,140)
76,0: (35900,35900,35900)  #8C8C8C  srgb(140,140,140)
77,0: (35872,35872,35872)  #8C8C8C  srgb(140,140,140)
78,0: (35855,35855,35855)  #8C8C8C  srgb(140,140,140)
79,0: (35779,35779,35779)  #8B8B8B  srgb(139,139,139)
80,0: (35777,35777,35777)  #8B8B8B  srgb(139,139,139)
81,0: (35664,35664,35664)  #8B8B8B  srgb(139,139,139)
82,0: (35774,35774,35774)  #8B8B8B  srgb(139,139,139)
83,0: (35785,35785,35785)  #8B8B8B  srgb(139,139,139)
84,0: (35753,35753,35753)  #8B8B8B  srgb(139,139,139)
85,0: (35671,35671,35671)  #8B8B8B  srgb(139,139,139)
86,0: (35675,35675,35675)  #8B8B8B  srgb(139,139,139)
87,0: (35694,35694,35694)  #8B8B8B  srgb(139,139,139)
88,0: (35621,35621,35621)  #8B8B8B  srgb(139,139,139)
89,0: (35819,35819,35819)  #8B8B8B  srgb(139,139,139)
90,0: (36080,36080,36080)  #8C8C8C  srgb(140,140,140)
91,0: (36300,36300,36300)  #8D8D8D  srgb(141,141,141)
92,0: (36550,36550,36550)  #8E8E8E  srgb(142,142,142)
93,0: (36561,36561,36561)  #8E8E8E  srgb(142,142,142)
94,0: (36425,36425,36425)  #8E8E8E  srgb(142,142,142)
95,0: (36268,36268,36268)  #8D8D8D  srgb(141,141,141)
96,0: (36317,36317,36317)  #8D8D8D  srgb(141,141,141)
97,0: (36409,36409,36409)  #8E8E8E  srgb(142,142,142)
98,0: (36521,36521,36521)  #8E8E8E  srgb(142,142,142)
99,0: (36568,36568,36568)  #8E8E8E  srgb(142,142,142)

This is your median filtered image for the vertical edge:

enter image description here

And, I can draw the detected edges on in red like this:

convert line.png -stroke red -draw "line 0,268 640,268" -draw "line 313,0 313,480" result.png

enter image description here

So, in summary, the two commands you would run in Terminal, without writing or compiling and linking any code, are as follows:

# Find horizontal edges
convert line.png -colorspace gray -normalize -threshold 50% -virtual-pixel mirror -statistic median 1x100 -resize 1x100! txt:

# Find vertical edges
convert line.png -colorspace gray -normalize -threshold 50% -virtual-pixel mirror -statistic median 100x1 -resize 100x1! txt:

Upvotes: 3

Tergiver
Tergiver

Reputation: 14517

What I've come up with works quite well for this particular application so I thought I would share it.

First I perform a binary filter of the image with an adjustable threshold.

Then I run a "smoothing" operation. I'm sure there is a technical term for this I don't know. I examine every pixel's neighbors (number of neighbors is adjustable) to decide whether that pixel should be black or white.

Then I grab a rectangle area (for each top, bottom, left, and right part of the image) and note the first transition (black to white or white to black) of each row(vertical)/column(horizontal).

If the number of transitions found is significantly less than the number of rows/columns in the area, there is no line.

I generate the mean and population standard deviation of these transitions.

If deviation is greater than some adjustable value, there is no line. If deviation is less, the mean is used as the position of the line.

Upvotes: 1

Related Questions