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.
Related
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);
I am writing a game for school project using Processing. I am currently dealing with a player's field of view. The player's field of view is basically a circle, but I would like the view to be blocked if there is an obstacle in front, meaning that you can't see the things behind the obstacle. The below image is the current results I have.
The link to the image
My code: http://pastie.org/10854654
The method I used is to go through every pixel in the player's field of view starting from the center, picking a path towards the circumference. As I searched outwards, if an obstacle is found on the path, I then draw a black line on the rest of the path. Changing the direction of the path degree by degree, eventually covering the whole circle.
//Draw a circle field of view.
int[][] collisionMap = map.getCollisionMap();
//Use a lot of small rectangle to cover the full map except of the circle field of view.
mainapplet.fill(0, 0, 0, 128);
for(int i = 0; i <= MyApplet.width; i++ ){
for(int j = 0; j <= MyApplet.height; j++ ){
if(mainapplet.dist(playerx, playery, i, j) > FieldOfView)
mainapplet.rect(i, j, 1, 1);
}
}
//Scan the circle field of view. If there is collision , draw a line to cover the area ,which means that the area is invisible.
mainapplet.stroke(0, 0, 0, 128);
mainapplet.strokeWeight(5);
for(float i = 0; i < 360; i+=1) {
for(float j = 0; j < FieldOfView ; j++ ){
float x = j * mainapplet.cos( mainapplet.radians(i) );
float y = j * mainapplet.sin( mainapplet.radians(i) );
if(collisionMap[player.getX() + (int)x ][player.getY() + (int)y ] == 1){
mainapplet.line(playerx + x, playery + y,
playerx + (FieldOfView-1)* mainapplet.cos( mainapplet.radians(i) ),
playery + (FieldOfView-1)* mainapplet.sin( mainapplet.radians(i) )
);
break;
}
}
}
collisionMap is a 2D array with 0s and 1s, "1" denoting that an obstacle is present at the location.
However, I find this method inefficient, therefore, causing lag. Is there a better way to do this? Or maybe there are already written tools that I can use?
What you're talking about is called 2d shadow mapping. It's not a trivial topic, but there are a ton of resources on google. I highly recommend reading up on them, because they'll explain it much better than I can.
But I did find this sketch on OpenProcessing which does what you describe. The full code is available at that link, but the pertinent bit seems to be this function:
void drawShadow() {
PVector tmp;
PVector m = new PVector(mouseX, mouseY); //mouse vector
fill(0);
stroke(0);
for (int i=0; i < vertCnt; i++) {
beginShape();
PVector v1 = p[i]; //current vertex
PVector v2 = p[i==vertCnt-1?0:i+1]; //"next" vertex
vertex(v2.x, v2.y);
vertex(v1.x, v1.y);
//current shadow vertex
tmp = PVector.sub(v1, m);
tmp.normalize();
tmp.mult(5000); //extend well off screen
tmp.add(v1); //true up position
vertex(tmp.x, tmp.y);
//"next" shadow vertex
tmp = PVector.sub(v2, m);
tmp.normalize();
tmp.mult(5000); //extend well off screen
tmp.add(v2); //true up position
vertex(tmp.x, tmp.y);
endShape(CLOSE);
}
}
But honestly the best thing you can do is google "processing shadow mapping" and spend some time reading through the results. This is a huge topic that's a bit too broad for a single Stack Overflow question.
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.
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?
I am trying the detect the pupil from a infrared image and calculate the center of the pupil.
In my setup, i used a camera sensitive to infrared light, and I added a visible light filter to the lens and two infrared LED around the camera.
However, the image I got is blur not so clear, maybe this caused by the low resolution of the camera, whose max is about 700x500.
In the processing, the first thing i did was to convert this RGB image to gray image, how ever the result is terrible. and it got nothing in the results.
int main()
{
//load image
cv::Mat src = cv::imread("11_13_2013_15_36_09.jpg");
cvNamedWindow("original");
cv::imshow("original", src);
cv::waitKey(10);
if (src.empty())
{
std::cout << "failed to find the image";
return -1;
}
// Invert the source image and convert to graysacle
cv::Mat gray;
cv::cvtColor(~src, gray, CV_BGR2GRAY);
cv::imshow("image1", gray);
cv::waitKey(10);
// Convert to binary image by thresholding it
cv::threshold(gray, gray, 220, 255, cv::THRESH_BINARY);
cv::imshow("image2", gray);
cv::waitKey(10);
// Find all contours
std::vector<std::vector<cv::Point>>contours;
cv::findContours(gray.clone(), contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
// Fill holes in each contour
cv::drawContours(gray, contours, -1, CV_RGB(255, 255, 255), -1);
cv::imshow("image3", gray);
cv::waitKey(10);
for (int i = 0; i < contours.size(); i++)
{
double area = cv::contourArea(contours[i]);
cv::Rect rect = cv::boundingRect(contours[i]);
int radius = rect.width / 2;
// If controu is big enough and has round shape
// Then it is the pupil
if (area >= 800 &&
std::abs(1 - ((double)rect.width / (double)rect.height)) <= 0.3 &&
std::abs(1 - (area / (CV_PI * std::pow(radius, 2)))) <= 0.3)
{
cv::circle(src, cv::Point(rect.x + radius, rect.y + radius), radius, CV_RGB(255, 0, 0), 2);
}
}
cv::imshow("image", src);
cvWaitKey(0);
}
When the original image was converted, the gray image is terrible, does anyone know a better solution to this? I am completely new to this. for the rest of the code for finding the circle, if you have any comments, just tell me. and also i need to extra the position of the two glint (the light point) on the original image, does anyone has some idea?
thanks.
Try equalizing and filtering your source image before thresholding it ;)