Reputation: 1096
I have a images like below
Here there is a line. This line is not straight everywhere. In the middle part of the line there is a curve. My work is to calculate that how much the line is straight or how much the line is curved ??? How can i do that using C# . I searched everywhere but cant get any idea how to do that. Have anyone any idea how to do that?? My aim is not only to detect a line but also to calculate how much curve the line is??
Upvotes: 3
Views: 1151
Reputation: 207550
My approach would probably be more basic and I would use ready-made tools where possible. So I would go the following way:
Step 1. Use ImageMagick convert
to threshold and grayscale the image and write it out as PGM (Portable Grey Map) so I didn't have to understand DIBs and JPEGs.
convert EuJpb.jpg -threshold 10% -compress none 1.pgm
Contents of resulting file "1.pgm"
P2
1200 400
255
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
...
... 255 255 255 ... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
... 0 0 255 255 ... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
...
I have had to render it as a JPEG for display here, but this is what you get:
Step 2
Write some pretty simple C (or more likely awk or Perl) to generate a list of points from the easy-to-read PGM.
Something like this:
#!/bin/bash
# First put each sample on its own line to make processing easier
cat 1.pgm | tr ' ' '\n' | sed '/^$/d' | awk '
NR==2{w=$1;x=0;y=0} # Line 2 is the image width
NR==3{h=$1} # Line 3 is the image height
NR>=5{ # Line 5 onwards are the pixel values
if($1>0){ # If pixel non-zero, output point
printf "%d,%d\n",x,y
}
# Calculate new x and y for next point
if(x++==w){
x=0;y++
}
}' > points.txt
Step 3
Put the points (in points.txt
) into gnuplot
and fit a line of the format y=ax + b and look at the standard deviations of the residuals to get a measure of straightness.
Save this as plotcmds
set title 'Plotted with Gnuplot'
set ylabel 'y-axis (inverted)'
set xlabel 'x-axis'
set yrange [0:400]
set xrange [0:1200]
f(x)=a*x+b
fit f(x) 'points.txt' using 1:2 via a,b
plot 'points.txt',f(x) with lines linestyle 3
set terminal postscript color landscape dashed enhanced 'Times-Roman'
set output 'file.eps'
set size 1,0.5
replot
Then run using:
gnuplot plotcmds
The statistics are output here:
resultant parameter values
a = -0.0331951
b = 218.852
After 5 iterations the fit converged.
final sum of squares of residuals : 2.39695e+06
rel. change during last iteration : -1.14297e-11
degrees of freedom (FIT_NDF) : 3931
rms of residuals (FIT_STDFIT) = sqrt(WSSR/ndf) : 24.6932
variance of residuals (reduced chisquare) = WSSR/ndf : 609.755
Final set of parameters Asymptotic Standard Error
======================= ==========================
a = -0.0331951 +/- 0.001672 (5.035%)
b = 218.852 +/- 0.8006 (0.3658%)
correlation matrix of the fit parameters:
a b
a 1.000
b -0.871 1.000
As you can see, the best line is y=-0.033x + 218 (my origin is top-left by the way, so essentially upside down). Now you can compare the residuals to get an idea of the measure of deviation from a straight line.
Here is the plot, with the calculated points in red and the best-fit straight line in blue(ish).
Upvotes: 2
Reputation: 1743
This is just a sample using the EmguCV wrapper over OpenCV image processing library detecting the lines. Of course this is not 100% accurate, you will have to tweak a little more. When you get the lines you may calculate the curve.
Or another way is to implement your own algorithm to detect the lines.
Using this algorithm I got 16 lines from the provided image. With some tweak you can get more accurate number.
var bmp = new Bitmap(pathToImage);
//Load the image from file and resize it for display
using (Image<Bgr, Byte> img = new Image<Bgr, byte>(bmp))
{
//Convert the image to grayscale and filter out the noise
using (Image<Gray, Byte> gray = img.Convert<Gray, Byte>().PyrDown().PyrUp())
{
Gray cannyThreshold = new Gray(180);
#region Canny and edge detection
Gray cannyThresholdLinking = new Gray(120);
Image<Gray, Byte> cannyEdges = gray.Canny(cannyThreshold, cannyThresholdLinking);
LineSegment2D[] lines = cannyEdges.HoughLinesBinary(
1, //Distance resolution in pixel-related units
Math.PI/60.0, //Angle resolution measured in radians.
20, //threshold
30, //min Line width
10 //gap between lines
)[0]; //Get the lines from the first channel
#endregion
}
}
Upvotes: 2