I'm trying to detect ruler on the image, and I'm going to follow the next process:
1) prepare image (blur,Canny, ect.)
2) detect lines
3) prepare set of parallel lines
so, I have an image:
that app converts to this:
next I've tried HoughLinesP
method and looks I cannot apply it in my case, because I don't know the angle of lines, so it isn't found ruler vertical lines, but found horizontal (for example) and every ruler line consists of many thin lines, that will be a problem to process:
the code:
std::vector<cv::Vec4i> lines_std;
cv::HoughLinesP( grayMat, lines_std, 1, CV_PI/90, 50, 10, 0 );
// drawing lines (with random color)
for( size_t i = 0; i < lines_std.size(); i++ )
{
cv::line( originalMat, cv::Point(lines_std[i][0], lines_std[i][1]),
cv::Point(lines_std[i][2], lines_std[i][3]), cv::Scalar(arc4random_uniform(155)+100,
arc4random_uniform(155)+100,
arc4random_uniform(155)+100), 1);
}
also I've tried LineSegmentDetector
, and got more closer result I expected:
code:
vector<Vec4f> lines_std;
Ptr<LineSegmentDetector> ls = createLineSegmentDetector(LSD_REFINE_NONE);
ls->detect(grayMat, lines_std);
but here I faced with some problems (and looks there is no way to customize createLineSegmentDetector
) :
not all lines were detected;lines detects not in center but on the sides and some times in left or right side only, but I need to get the center of bold line, because this will be used in calculations next.
So, what is the right way to find all lines (and every line only one time at the center of bold line)?
Update
tried HoughLines
also:
vector lines;
cv::HoughLines(grayMat, lines, 1, CV_PI/90, 100 , 100, 0 );
for( size_t i = 0; i < lines.size(); i++ )
{
float rho = lines[i][0], theta = lines[i][1];
cv::Point pt1, pt2;
double a = cos(theta), b = sin(theta);
double x0 = a*rho, y0 = b*rho;
pt1.x = cvRound(x0 + 1000*(-b));
pt1.y = cvRound(y0 + 1000*(a));
pt2.x = cvRound(x0 - 1000*(-b));
pt2.y = cvRound(y0 - 1000*(a));
cv::line( originalMat, pt1, pt2, cv::Scalar(0,255,0), 3, CV_AA);
}
but the result also looks strange (and calculations takes a lot of time):
Before going into the lines road detection, we need to understand using opencv what is a line and what isn’t a line. The Houg lines transform is an algorythm used to detect straight lines.
The OpenCV line detection function is a very useful function that is utilized in a plethora of applications especially related to image detection. This inbuilt function is used to isolate specific features with respect to uh particular shape, which is present within the image specified for processing.
The HoughLines () function of OpenCV is used to detect lines present in an image using the standard Hough line transform. To detect the lines present in an image, we have to read the image using the imread () function and convert it into grayscale if it’s not in grayscale already.
For every line in the picture you get 2 detected lines in the output. This even happens if the original line width is 1 pixel, i.e. previous thinning/skeletonization does not help. How to adapt that instead of 2 lines only the central line of them is returned? Here is my pipeline, maybe it can give you some help.
Guess I found the way I should follow for:
1) make lines thin as possible (after Canny transformation):
cv::Mat skel(grayMat.size(), CV_8UC1, cv::Scalar(0));
cv::Mat temp(grayMat.size(), CV_8UC1);
cv::Mat elementSkel = cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(3, 3));
bool done;
do
{
cv::morphologyEx(grayMat, temp, cv::MORPH_OPEN, elementSkel);
cv::bitwise_not(temp, temp);
cv::bitwise_and(grayMat, temp, temp);
cv::bitwise_or(skel, temp, skel);
cv::erode(grayMat, grayMat, elementSkel);
double max;
cv::minMaxLoc(grayMat, 0, &max);
done = (max == 0);
} while (!done);
it looks like this:
2) detect lines with LineSigmentDetector
:
vector<Vec4f> lines_std;
Ptr<LineSegmentDetector> ls = createLineSegmentDetector(LSD_REFINE_NONE);
ls->detect(skel, lines_std);
3)calculate line angle and group ids by angle:
NSMutableDictionary *testHashMap = [[NSMutableDictionary alloc]init];
for( size_t i = 0; i < lines_std.size(); i++ )
{
cv::Point p1 = cv::Point(lines_std[i][0], lines_std[i][1]);
cv::Point p2 = cv::Point(lines_std[i][2], lines_std[i][3]);
int angle = abs(atan2(p1.y - p2.y, p1.x - p2.x)); // int for rounding (for test only)
NSMutableArray *idArray=testHashMap[[NSString stringWithFormat:@"%i", angle]];
if(idArray == nil) {
idArray = [[NSMutableArray alloc] init];
}
[idArray addObject:[NSNumber numberWithInt:i]];
[testHashMap setObject:idArray forKey:[NSString stringWithFormat:@"%i", angle] ];
}
4) found the ruler line set and draw it:
for( NSInteger i = 0; i < [rulerIds count]; i++ )
{
int itemId = [[rulerIds objectAtIndex:i] integerValue];
cv::Point p1 = cv::Point(lines_std[itemId][0], lines_std[itemId][1]);
cv::Point p2 = cv::Point(lines_std[itemId][2], lines_std[itemId][3]);
cv::line( originalMat, p1 , p2, cv::Scalar(0,255,0), 1);
}
result I got:
Update
but if we zoom this image well still see duplicated lines to remove duplications I've made simple logic that merges lines by founding average value for each point, for instance in case of 3 lines(green) we have 3 dots on the end:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With