Find vertex from a object by using vertex detection - algorithm

I would like to find all vertex (e.g. return x, y positions) for the black object.
I will use Java and JavaCV to implements. Is there any API or algorithm can help?
Sorry for not enough reputation to post images. I post the link here.
The original image like this:
http://i.stack.imgur.com/geubs.png
The expected result like this:
http://i.stack.imgur.com/MA7uq.png

Here is what you should do (for explanation, see comments with code),
CODE
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
// Load the image
String path = "/home/bikz05/Desktop/geubs.png";
Mat original = Highgui.imread(path);
Mat image = new Mat();
Imgproc.cvtColor(original, image, Imgproc.COLOR_BGR2GRAY);
// Threshold the image
Mat threshold = new Mat();
Imgproc.threshold(image, threshold, 127, 255, 1);
// Find the contours
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Imgproc.findContours(threshold, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
// Get contour index with largest area
double max_area = -1;
int index = 0;
for(int i=0; i< contours.size();i++) {
if (Imgproc.contourArea(contours.get(i)) > max_area) {
max_area = Imgproc.contourArea(contours.get(i));
index = i;
}
}
// Approximate the largest contour
MatOfPoint2f approxCurve = new MatOfPoint2f();
MatOfPoint2f oriCurve = new MatOfPoint2f( contours.get(index).toArray() );
Imgproc.approxPolyDP(oriCurve, approxCurve, 6.0, true);
// Draw contour points on the original image
Point [] array = approxCurve.toArray();
for(int i=0; i < array.length;i++) {
Core.circle(original, array[i], 2, new Scalar(0, 0 ,255), 2);
}
INPUT IMAGE
OUTPUT IMAGE

OpenCV allows you to take a binary image and carry out contour analysis.
http://docs.opencv.org/doc/tutorials/imgproc/shapedescriptors/find_contours/find_contours.html
You could use findContours to find all of the contours (all of the edge points) then simply average them or pick and choose the ones that suit your purpose.
Here is a good example for JavaCV..
opencv/javacv: How to iterate over contours for shape identification?

Related

How can I discard an extra line from a contour shape in opencv

Is there a way to remove small line segments from a contour?
For example, in this image, the largest contour is a bounding box but we also have a line segment connected to the box.
As contour is a set of Points, I guess we can do something to remove segment of contours that is not part of the box. For example by detecting and removing small lines or small sub contours or another way. But I do not know how I can do it.
Please remember I want to remove them after finding contour and not before that. Do you know how I can remove them? Or any idea?
//After edge detection with canny.
//canny variable has the edge mat
std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours( canny, contours, hierarchy, RETR_EXTERNAL,CHAIN_APPROX_SIMPLE, cv::Point(0, 0) );
Mat draw = Mat::zeros(canny.size(), CV_8UC3);
for (int i = 0; i< contours.size(); i++){
double a = contourArea(contours[i], false); // Find the area of contour
if (a>largest_area){
largest_area = a;
largest_contour_index = i; //Store the index of largest contour
}
}
drawContours(draw, contours, largest_contour_index, Scalar(255, 255, 255), 0, 8, hierarchy);
imshow("Contours", draw);

Real-time object recognition in complex background

I'm trying to make a real-time advertisement billboard detection in road using android smartphone. The goal is to crop the area of the advertisement billboard object (regions of interest) and save it to database.
For example:
enter image description here
enter image description here
For preprocessing, I used grayscaling and Canny Edge Detection (Otsu thresholding is used to set the upper and lower threshold). Then, I used contour-based method to detect whether the object is rectangular by checking the point. I use Java OpenCV in android studio for implementation. When I run the program, it only detect rectangular object in plain background and if the rectangular having a high contrast from the background. Currently, it can only detect rectangle with 90 degree and it failed to detect object with rounded rectangle shape. Furthermore, my program failed completely to detect rectangular object in a more complex background, like road scene where the object I'm trying to detect is having similar color to the background/low contrast or there are many occlusions like tree, traffic light, and cables which caused the detection to fail.
This is the code I use for edge detection
Mat destination = new Mat(oriMat.rows(), oriMat.cols(), oriMat.type());
Imgproc.cvtColor(oriMat, destination, Imgproc.COLOR_RGBA2GRAY);
Imgproc.GaussianBlur(destination, destination, new Size(3,3), 0, 0, Imgproc.BORDER_DEFAULT);
double otsuThresholdValue = Imgproc.threshold(destination, destination, 0, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);
double lowerThreshold = otsuThresholdValue*0.5;
double upperThreshold = otsuThresholdValue;
Mat canny = new Mat();
Imgproc.Canny(destination, canny, lowerThreshold, upperThreshold);
Mat abs = new Mat();
Core.convertScaleAbs(canny, abs);
Mat result = new Mat();
Core.addWeighted(abs, 0.5, abs, 0.5, 0, result);
Here is the code I use for contour-based detection
ArrayList<MatOfPoint> contours = new ArrayList<>();
// find contours and store them all as a list
Imgproc.findContours(matData.monoChrome.clone(), contours, new Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
final int width = matData.monoChrome.rows();
final int height = matData.monoChrome.cols();
int matArea = width * height;
for (int i = 0; i < contours.size(); i++) {
double contoursArea = Imgproc.contourArea(contours.get(i));
MatOfPoint2f approx = new MatOfPoint2f();
MatOfPoint2f contour = new MatOfPoint2f(contours.get(i).toArray());
double epsilon = Imgproc.arcLength(contour, true) * 0.1;
// Imgproc.minAreaRect(contour);
// approximate contour with accuracy proportional to the contour perimeter
Imgproc.approxPolyDP(contour, approx, epsilon, true);
if (Math.abs(contoursArea) < matArea * 0.01 ||
!Imgproc.isContourConvex(new MatOfPoint(approx.toArray()))) {
continue;
}
Imgproc.drawContours(matData.resizeMat, contours, i, new Scalar(0, 255, 0));
List<Point> points = approx.toList();
int pointCount = points.size();
LinkedList<Double> cos = new LinkedList<>();
for (int j = 2; j < pointCount + 1; j++) {
cos.addLast(angle(points.get(j % pointCount), points.get(j - 2), points.get(j - 1)));
}
Collections.sort(cos, (lhs, rhs) -> lhs.intValue() - rhs.intValue());
double mincos = cos.getFirst();
double maxcos = cos.getLast();
if (points.size() == 4 && mincos >= -0.3 && maxcos <= 0.5) {
for (int j = 0; j < points.size(); j++) {
Core.circle(matData.resizeMat, points.get(j), 6, new Scalar(255, 0, 0), 6);
}
matData.points = points;
break;
}
}
Is there any method I can use to recognize advertisement billboard in road?
I would appreciate any answers and ideas. Thank you!

Emgu CV draw rotated rectangle

I'm looking for few days a solution to draw rectangle on image frame. Basically I'm using CvInvoke.cvRectangle method to draw rectangle on image because I need antialiased rect.
But problem is when I need to rotate a given shape for given angle. I can't find any good solution.
I have tryed to draw rectangle on separate frame then rotate hole frame and apply this new image on top of my base frame. But in this solution there is a problem with antialiasing. It's not working.
I'm working on simple application that should allow draw few kinds of shape, resize them and rotation for given angle.
Any idea how to achive this?
The best way I found to draw a minimum enclosing rectangle on the contour is using the Polylines() function which uses vertices that are returned from MinAreaRect() function. There are surely other ways to do it as well. Here is the code walk down:
// Find contours
var contours = new Emgu.CV.Util.VectorOfVectorOfPoint();
Mat hierarchy = new Mat();
CvInvoke.FindContours(image, contours, hierarchy, RetrType.Tree, ChainApproxMethod.ChainApproxSimple);
// According to your metric, get an index of the contour you want to find the min enclosing rectangle for
int index = 2; // Say, 2nd index works for you.
var rectangle = CvInvoke.MinAreaRect(contours[index]);
Point[] vertices = Array.ConvertAll(rectangle.GetVertices(), Point.Round);
CvInvoke.Polylines(image, vertices, true, new MCvScalar(0, 0, 255), 5);
The result can be visualized in the image below, in red is the minimum enclosing rectangle.
I use C# and EMGU.CV(4.1), and I think this code will not be difficult to transfer to any platform.
Add function in the in your helper:
public static Mat DrawRect(Mat input, RotatedRect rect, MCvScalar color = default(MCvScalar),
int thickness = 1, LineType lineType = LineType.EightConnected, int shift = 0)
{
var v = rect.GetVertices();
var prevPoint = v[0];
var firstPoint = prevPoint;
var nextPoint = prevPoint;
var lastPoint = nextPoint;
for (var i = 1; i < v.Length; i++)
{
nextPoint = v[i];
CvInvoke.Line(input, Point.Round(prevPoint), Point.Round(nextPoint), color, thickness, lineType, shift);
prevPoint = nextPoint;
lastPoint = prevPoint;
}
CvInvoke.Line(input, Point.Round(lastPoint), Point.Round(firstPoint), color, thickness, lineType, shift);
return input;
}
This draws roteted rectangle by points. Here used rounding points by method Point.Round becose RotatedRect has points in float coordinates and CvInvoke.Line takes points as integer.
Use:
var mat = Mat.Zeros(200, 200, DepthType.Cv8U, 3);
mat.GetValueRange();
var rRect = new RotatedRect(new PointF(100, 100), new SizeF(100, 50), 30);
DrawRect(mat, rRect,new MCvScalar(255,0,0));
var brect = CvInvoke.BoundingRectangle(new VectorOfPointF(rRect.GetVertices()));
CvInvoke.Rectangle(mat, brect, new MCvScalar(0,255,0), 1, LineType.EightConnected, 0);
Result:
You should read the OpenCV documentation.
There is a RotatedRectangle class that you can use for your task. You can specify the angle by which the rectangle will be rotated.
Here is a sample code (taken from the docs) for drawing a rotated rectangle:
Mat image(200, 200, CV_8UC3, Scalar(0));
RotatedRect rRect = RotatedRect(Point2f(100,100), Size2f(100,50), 30);
Point2f vertices[4];
rRect.points(vertices);
for (int i = 0; i < 4; i++)
line(image, vertices[i], vertices[(i+1)%4], Scalar(0,255,0));
Rect brect = rRect.boundingRect();
rectangle(image, brect, Scalar(255,0,0));
imshow("rectangles", image);
waitKey(0);
Here is the result:

Estimate Image line gradient ( not pixel gradient)

I have a problem whereby I want to estimate the gradient of the line on the contour. Please note that I dont need the pixel gradient but the rate of change of line.
If you see the attached image, you will see a binary image with green contour. I want to label each pixel based on the gradient of the pixel on the contour.
Why I need the gradient is because I want to compute the points where the gradient orientation changes from + to - or from - to +.
I cannot think of a good method, to estimate this point on the image. Could someone help me with suggestion on how I can estimate this points.
Here is a small program that computes the tangent at each contour pixel location in a very simple way (there exist other and probably better ways! the easy ones are: http://en.wikipedia.org/wiki/Finite_difference#Forward.2C_backward.2C_and_central_differences):
for a contour pixel c_{i} get the neighbors c_{i-1} and c_{i+1}
tangent direction at c_i is (c_{i-1} - c_{i+1}
So this is all on CONTOUR PIXELS but maybe you could so something similar if you compute the orthogonal to the full image pixel gradient... not sure about that ;)
here's the code:
int main()
{
cv::Mat input = cv::imread("../inputData/ContourTangentBin.png");
cv::Mat gray;
cv::cvtColor(input,gray,CV_BGR2GRAY);
// binarize
cv::Mat binary = gray > 100;
// find contours
std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;
findContours( binary.clone(), contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE ); // CV_CHAIN_APPROX_NONE to get each single pixel of the contour!!
for( int i = 0; i< contours.size(); i++ )
{
std::vector<cv::Point> & cCont = contours[i];
std::vector<cv::Point2f> tangents;
if(cCont.size() < 3) continue;
// 1. compute tangent for first point
cv::Point2f cPoint = cCont.front();
cv::Point2f tangent = cCont.back() - cCont.at(1); // central tangent => you could use another method if you like to
tangents.push_back(tangent);
// display first tangent
cv::Mat tmpOut = input.clone();
cv::line(tmpOut, cPoint + 10*tangent, cPoint-10*tangent, cv::Scalar(0,0,255),1);
cv::imshow("tangent",tmpOut);
cv::waitKey(0);
for(unsigned int j=1; j<cCont.size(); ++j)
{
cPoint = cCont[j];
tangent = cCont[j-1] - cCont[(j+1)%cCont.size()]; // central tangent => you could use another method if you like to
tangents.push_back(tangent);
//display current tangent:
tmpOut = input.clone();
cv::line(tmpOut, cPoint + 10*tangent, cPoint-10*tangent, cv::Scalar(0,0,255),1);
cv::imshow("tangent",tmpOut);
cv::waitKey(0);
//if(cv::waitKey(0) == 's') cv::imwrite("../outputData/ContourTangentTangent.png", tmpOut);
}
// now there are all the tangent directions in "tangents", do whatever you like with them
}
for( int i = 0; i< contours.size(); i++ )
{
drawContours( input, contours, i, cv::Scalar(0,255,0), 1, 8, hierarchy, 0 );
}
cv::imshow("input", input);
cv::imshow("binary", binary);
cv::waitKey(0);
return 0;
}
I used this image:
and got outputs like:
in the result you get a vector with a 2D tangent information (line direction) for each pixel of that contour.

Prospective algorithmic approach for this image in OpenCV

I am looking for advice from people having extensive experience with computer vision. I have a collection of ultrasonographic B&W images like the one below (without the stars and dotted line):
What I would like to do is detect the contour of a blood vessel (for example, the one highlighted by the yellow star). Of course my first step would be to define the ROI and maximize the contrast. But what would then be the best algorithm to use? Segmentation with the watershed algorithm? Something else?
I am little unsettled because of the image blur...
Edit:
As requested in the comments, here would be an example of source and result images:
Following is a simple approach to your problem, if I understood you correctly. My result is shown below.
And here is the code
int max_area_threshold = 10000;
int min_area_threshold = 1000;
float rational_threshold = 0.7;
cv::Mat img = cv::imread("sample.jpg", CV_8UC1);
cv::Mat img_binary;
//Create binary imae by tresholding
cv::threshold(img, img_binary, 25, 255, CV_THRESH_BINARY);
//Invert black-white
cv::bitwise_not(img_binary, img_binary);
//Eliminating small segments
cv::erode(img_binary, img_binary, cv::Mat(), cv::Point(-1, -1), 2, 1, 1);
cv::dilate(img_binary, img_binary, cv::Mat(), cv::Point(-1, -1), 1, 1, 1);
//Find contours
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours( img_binary, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
for( int i = 0; i< contours.size(); i++ )
{
if(contours[i].size() < 5)
continue;
//Fit ellipse to contour
cv::RotatedRect boundRect = cv::fitEllipse(contours[i]);
//Check the squareness of the bounding box
if(abs((boundRect.size.width / (float)boundRect.size.height)-1.0) > rational_threshold)
continue;
//Elliminate too big segments
if(boundRect.boundingRect().area() > max_area_threshold)
continue;
//Elliminate too small segments
if(boundRect.boundingRect().area() < min_area_threshold)
continue;
drawContours(img, contours, i, cv::Scalar(255), 0.2, 8, hierarchy, 0, cv::Point() );
}
cv::imwrite("result.jpg", img);
I hope it helps.

Resources