OpenCV - Creating Bounding boxes and circles for contours - Xcode [Objective-C++] - ios

a similar question like mine was already posted, but there is still some part which doesn't fit to my current problem. I use Xcode
and Objective C++ to work with openCV.
What I do want to do is to create some bounding boxes and circles around human eyes in photos. I have used the following code but this does not work:
-(void)detectEye {
cv::Mat src_gray;
int thresh = 100;
RNG rng(12345);
NSString *path = #"/Users/NazarMedeiros/Desktop/image.jpg";
cv::Mat src = cv::imread("/Users/NazarMedeiros/Downloads/face.jpg");
if (src.empty())
return;
cv::cvtColor(src, src_gray, CV_BGR2GRAY );
cv::blur(src_gray, src_gray, cv::Size(3,3) );
cv::Mat threshold_output;
std::vector<std::vector<cv::Point> > contours;
std::vector<Vec4i> hierarchy;
/// Detect edges using Threshold
cv::threshold( src_gray, threshold_output, thresh, 255, THRESH_BINARY );
/// Find contours
cv::findContours(threshold_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0) );
/// Approximate contours to polygons + get bounding rects and circles
std::vector<std::vector<cv::Point> > contours_poly( contours.size() );
std::vector<cv::Rect> boundRect( contours.size() );
std::vector<Point2f>center( contours.size() );
std::vector<float>radius( contours.size() );
for(int i = 0; i < contours.size(); i++ ) {
cv::approxPolyDP(cv::Mat(contours[i]), contours_poly[i], 3, true );
boundRect[i] = cv::boundingRect(cv::Mat(contours_poly[i]) );
cv::minEnclosingCircle( (cv::Mat)contours_poly[i], center[i], radius[i] );
}
/// Draw polygonal contour + bonding rects + circles
Mat drawing = Mat::zeros( threshold_output.size(), CV_8UC3 );
for(int i = 0; i < contours.size(); i++ ) {
cv::Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
cv::drawContours( drawing, contours_poly, i, color, 1, 8, std::vector<Vec4i>(), 0, cv::Point() );
cv::circle( drawing, center[i], (int)radius[i], color, 2, 8, 0 );
}
imwrite([path UTF8String], src);}
}
The imwrite-function does not save my image..
Can anyone help me, please?
Best regards,
Nazar Medeiros

Try convert "drawing" to UIImage
- (UIImage *)UIImageFromCVMat:(cv::Mat)cvMat
{
NSData *data = [NSData dataWithBytes:cvMat.data length:cvMat.elemSize()*cvMat.total()];
CGColorSpaceRef colorSpace;
if (cvMat.elemSize() == 1) {
colorSpace = CGColorSpaceCreateDeviceGray();
} else {
colorSpace = CGColorSpaceCreateDeviceRGB();
}
CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
// Creating CGImage from cv::Mat
CGImageRef imageRef = CGImageCreate(cvMat.cols, //width
cvMat.rows, //height
8, //bits per component
8 * cvMat.elemSize(), //bits per pixel
cvMat.step[0], //bytesPerRow
colorSpace, //colorspace
kCGImageAlphaNone|kCGBitmapByteOrderDefault,// bitmap info
provider, //CGDataProviderRef
NULL, //decode
false, //should interpolate
kCGRenderingIntentDefault //intent
);
// Getting UIImage from CGImage
UIImage *finalImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorSpace);
return finalImage;
}

Related

How can I detect human body outline using OpenCV?

I am developing a small demo with openCV which can detect the body with real time camera.
Currently My app detect the face successfully. So, My question is, How I can detect the human body like face detection.
Here is my Code (which is called at every frame):-
///1. Convert input UIImage to Mat
std::vector<cv::Rect> faces;
CGImageRef image = CGImageCreateCopy(source.CGImage);
CGFloat cols = CGImageGetWidth(image);
CGFloat rows = CGImageGetHeight(image);
cv::Mat frame(rows, cols, CV_8UC4);
CGBitmapInfo bitmapFlags = kCGImageAlphaNoneSkipLast | kCGBitmapByteOrderDefault;
size_t bitsPerComponent = 8;
size_t bytesPerRow = frame.step[0];
CGColorSpaceRef colorSpace = CGImageGetColorSpace(image);
CGContextRef context = CGBitmapContextCreate(frame.data, cols, rows, bitsPerComponent, bytesPerRow, colorSpace, bitmapFlags);
CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, cols, rows), image);
CGContextRelease(context);
cv::Mat frame_gray;
cvtColor( frame, frame_gray, CV_BGR2GRAY );
equalizeHist( frame_gray, frame_gray );
///2. detection
NSString *eyes_cascade_name = [[NSBundle mainBundle] pathForResource:#"haarcascade_eye" ofType:#"xml"];
NSString *face_cascade_name = [[NSBundle mainBundle] pathForResource:#"haarcascade_frontalface_default" ofType:#"xml"];
if(!cascade_loaded){
std::cout<<"loading ..";
if( !eyes_cascade.load( std::string([eyes_cascade_name UTF8String]) ) ){ printf("--(!)Error loading\n"); return source;};
if( !face_cascade.load( std::string([face_cascade_name UTF8String]) ) ){ printf("--(!)Error loading\n"); return source;};
cascade_loaded = true;
}
face_cascade.detectMultiScale(frame_gray, faces, 1.3, 5, CV_HAAR_SCALE_IMAGE);
for( size_t i = 0; i < faces.size(); i++ )
{
cv::Point center( faces[i].x + faces[i].width*0.5, faces[i].y + faces[i].height*0.5 );
ellipse( frame, center, cv::Size( faces[i].width*0.5, faces[i].height*0.5), 0, 0, 360, cv::Scalar( 0, 100, 255 ), 4, 8, 0 );
cv::Mat faceROI = frame_gray( faces[i] );
std::vector<cv::Rect> eyes;
//-- In each face, detect eyes
eyes_cascade.detectMultiScale( faceROI, eyes, 1.1, 2, 0 |CV_HAAR_SCALE_IMAGE, cv::Size(30, 30) );
for( size_t j = 0; j < eyes.size(); j++ )
{
cv::Point center( faces[i].x + eyes[j].x + eyes[j].width*0.5, faces[i].y + eyes[j].y + eyes[j].height*0.5 );
int radius = cvRound( (eyes[j].width + eyes[j].height)*0.25 );
circle( frame, center, radius, cv::Scalar( 5, 255, 0 ), 2, 8, 0 );
}
}

Matching template imge(scaled) to Main/larger image

I want to find/check subImage/template image in main image and want to know its coordinates,
I have used code given at following link to implement it,
Check presence of subimage in image in iOS
It is working fine, if the size of the template image is exactly same as size of the matching part of larger image.
But it is not giving result properly if the subimage is scaled down or scaled up than matching part of larger image.
Use OpenCV Feature Detection. it is more accurate than template matching..
Please try with this code..
-(void)featureDetection:(UIImage*)largerImage withImage:(UIImage*)subImage
{
cv::Mat tempMat1 = [largerImage CVMat];
cv::Mat tempMat2 = [subImage CVMat];
cv::cvtColor(tempMat1, tempMat1, CV_RGB2GRAY);
cv::cvtColor(tempMat2, tempMat2, CV_RGB2GRAY);
if( !tempMat1.data || !tempMat2.data ) {
return;
}
//-- Step 1: Detect the keypoints using SURF Detector
int minHessian = 25;
cv::SurfFeatureDetector detector( minHessian ); // More Accurate bt take more time..
//cv::FastFeatureDetector detector( minHessian ); //Less Accurate bt take less time..
std::vector<cv::KeyPoint> keypoints_1, keypoints_2;
detector.detect( tempMat1, keypoints_1 );
detector.detect( tempMat2, keypoints_2 );
//-- Step 2: Calculate descriptors (feature vectors)
cv::SurfDescriptorExtractor extractor;
cv::Mat descriptors_1, descriptors_2;
extractor.compute( tempMat1, keypoints_1, descriptors_1 );
extractor.compute( tempMat2, keypoints_2, descriptors_2 );
std::vector<cv::Point2f> obj_corners(4);
//Get the corners from the object
obj_corners[0] = (cvPoint(0,0));
obj_corners[1] = (cvPoint(tempMat2.cols,0));
obj_corners[2] = (cvPoint(tempMat2.cols,tempMat2.rows));
obj_corners[3] = (cvPoint(0, tempMat2.rows));
//-- Step 3: Matching descriptor vectors with a brute force matcher
//cv::BruteForceMatcher < cv::L2<float> > matcher;
cv::FlannBasedMatcher matcher;
//std::vector< cv::DMatch > matches;
std::vector<cv::vector<cv::DMatch > > matches;
std::vector<cv::DMatch > good_matches;
std::vector<cv::Point2f> obj;
std::vector<cv::Point2f> scene;
std::vector<cv::Point2f> scene_corners(4);
cv::Mat H;
matcher.knnMatch( descriptors_2, descriptors_1, matches,2);
for(int i = 0; i < cv::min(tempMat1.rows-1,(int) matches.size()); i++) {
if((matches[i][0].distance < 0.6*(matches[i][1].distance)) && ((int) matches[i].size()<=2 && (int) matches[i].size()>0)) {
good_matches.push_back(matches[i][0]);
}
}
cv::Mat img_matches;
drawMatches( tempMat2, keypoints_2, tempMat1, keypoints_1, good_matches, img_matches );
NSLog(#"good matches %lu",good_matches.size());
if (good_matches.size() >= 4) {
for( int i = 0; i < good_matches.size(); i++ ) {
//Get the keypoints from the good matches
obj.push_back( keypoints_2[ good_matches[i].queryIdx ].pt );
scene.push_back( keypoints_1[ good_matches[i].trainIdx ].pt );
}
H = findHomography( obj, scene, CV_RANSAC );
perspectiveTransform( obj_corners, scene_corners, H);
NSLog(#"%f %f",scene_corners[0].x,scene_corners[0].y);
NSLog(#"%f %f",scene_corners[1].x,scene_corners[1].y);
NSLog(#"%f %f",scene_corners[2].x,scene_corners[2].y);
NSLog(#"%f %f",scene_corners[3].x,scene_corners[3].y);
//Draw lines between the corners (the mapped object in the scene image )
line( tempMat1, scene_corners[0], scene_corners[1], cvScalar(0, 255, 0), 4 );
line( tempMat1, scene_corners[1], scene_corners[2], cvScalar( 0, 255, 0), 4 );
line( tempMat1, scene_corners[2], scene_corners[3], cvScalar( 0, 255, 0), 4 );
line( tempMat1, scene_corners[3], scene_corners[0], cvScalar( 0, 255, 0), 4 );
}
// View matching..
UIImage *resultimage = [UIImage imageWithCVMat:img_matches];
UIImageView *imageview = [[UIImageView alloc] initWithImage:resultimage];
imageview.frame = CGRectMake(0, 0, 320, 240);
[self.view addSubview:imageview];
// View Result
UIImage *resultimage2 = [UIImage imageWithCVMat:tempMat1];
UIImageView *imageview2 = [[UIImageView alloc] initWithImage:resultimage2];
imageview2.frame = CGRectMake(0, 240, 320, 240);
[self.view addSubview:imageview2];
}

cvFindContours Always return 1

I am using OpenCV 2.4.6 and Visual C++ 2010. I am trying to track RED color in the below code. I'm using cvInRangeS to get the RED object from the image and that image is a binary image. Then using cvFindContours(..) I want to find the contours of the object.But cvFindcontours(..) always returning 1. I am also using cvBoundingRect(..) to get the contour's position in the image. The rectangle position i am getting is always (1,1,638,478). Means the cvFindcontour(..) is taking whole image as a single contour. How can I get only the RED objects in contour?
int main()
{
IplImage* mCVImageColor = cvCreateImageHeader(cvSize(640,480), IPL_DEPTH_8U, 3);
IplImage* imgTracking;
CvCapture* capture = cvCaptureFromCAM( CV_CAP_ANY );
namedWindow( "Display image", CV_WINDOW_AUTOSIZE );
namedWindow( "Display window", CV_WINDOW_AUTOSIZE );
namedWindow( "Display tracking", CV_WINDOW_AUTOSIZE );
namedWindow( "Display thresh", CV_WINDOW_AUTOSIZE );
CvRect rect;
CvMemStorage *storage = cvCreateMemStorage(0);
CvSeq *contours=0;
while(1)
{
IplImage* mCVImageColor = cvQueryFrame( capture );
cvSmooth(mCVImageColor, mCVImageColor, CV_GAUSSIAN,3,3);
imgTracking=cvCreateImage(cvGetSize(mCVImageColor),IPL_DEPTH_8U, 1);
IplImage* imgHSV = cvCreateImage(cvGetSize(mCVImageColor), IPL_DEPTH_8U, 3);
IplImage* imgThresh=cvCreateImage(cvGetSize(mCVImageColor),8, 1);
cvCvtColor(mCVImageColor, imgHSV, CV_BGR2HSV);
cvInRangeS(imgHSV, cvScalar(170,160,60), cvScalar(180,255,256), imgThresh);
cvAdd(imgThresh, imgTracking, imgTracking);
cvConvertScale(imgTracking, imgTracking, 1.0, 0.0);
int x = cvFindContours(imgTracking,storage,&contours,sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(2, 2));
printf("%d\n",contours->total);
for (; contours != 0; contours = contours->h_next)
{
rect = cvBoundingRect(contours); //extract bounding box for current contour
//drawing rectangle
cvRectangle(mCVImageColor,cvPoint(rect.x, rect.y),cvPoint(rect.x+rect.width, rect.y+rect.height),cvScalar(0, 0, 255),2, 8, 0);
printf("%d %d %d %d\n",rect.x, rect.y,rect.width,rect.height);
if(rect.width*rect.height<1400)
{
CvPoint centroid[1];
centroid[0].x = cvRound((rect.x+rect.width)/2);
centroid[0].y = cvRound((rect.y+rect.height)/2);
cvCircle( mCVImageColor, centroid[0], 5, CV_RGB(255, 0, 0),10, -1);
}
}
cvShowImage("Display tracking",imgTracking);
cvShowImage("Display window",imgHSV);
cvShowImage("Display image",mCVImageColor);
cvShowImage("Display thresh",imgThresh);
cvClearMemStorage( storage );
contours = 0;
//cvReleaseImage(&imgHSV);
//cvReleaseImage(&imgThresh);
int c = waitKey(20);
if(c=='q')
break;
}
}

Using cvFindContour with cvMinAreaRect2

I succeeded in using cvDrawContour but when I try to use cvMinRectArea2, it only shows one dot. Any ideas? Here's the source code.
IplImage *src = cvLoadImage("SignImg.jpg",0);
IplImage *dst_img = 0;
IplImage *img = cvCreateImage(cvGetSize(src),8,3);
CvBox2D rect;
CvMemStorage *storage = cvCreateMemStorage ();
CvSeq *contours = 0;
char a[255];
sprintf(&a[0],"%s",openFileDialog1->FileName);
dst_img = cvLoadImage(a,0);
cvFindContours (src, storage, &contours, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE ,cvPoint(0,0));
cvCvtColor(dst_img,img,CV_GRAY2BGR);
cvDrawContours (img, contours, CV_RGB(255,0,0), cvScalarAll(255),1,1,8,cvPoint(0,0));
rect=cvMinAreaRect2 (contours,0);
cvEllipseBox(img,rect,cvScalarAll(0),5,8,0);
The right way to use cvDrawContours is shown here:
for( ; contour != 0; contour = contour->h_next )
{
CvScalar color = CV_RGB( rand()&255, rand()&255, rand()&255 );
/* replace CV_FILLED with 1 to see the outlines */
cvDrawContours( dst, contour, color, color, -1, CV_FILLED, 8 );
}
If it's not obvious enough, your variable contours is an array and you need to iterate over it and draw each element! Else you will be drawing only the first element of the array.

Read HSV value of pixel in opencv

how would you go about reading the pixel value in HSV format rather than RGB? The code below reads the pixel value of the circles' centers in RGB format. Is there much difference when it comes to reading value in HSV?
int main(int argc, char** argv)
{
//load image from directory
IplImage* img = cvLoadImage("C:\\Users\\Nathan\\Desktop\\SnookerPic.png");
IplImage* gray = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);
CvMemStorage* storage = cvCreateMemStorage(0);
//covert to grayscale
cvCvtColor(img, gray, CV_BGR2GRAY);
// This is done so as to prevent a lot of false circles from being detected
cvSmooth(gray, gray, CV_GAUSSIAN, 7, 7);
IplImage* canny = cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,1);
IplImage* rgbcanny = cvCreateImage(cvGetSize(img),IPL_DEPTH_8U,3);
cvCanny(gray, canny, 50, 100, 3);
//detect circles
CvSeq* circles = cvHoughCircles(gray, storage, CV_HOUGH_GRADIENT, 1, 35.0, 75, 60,0,0);
cvCvtColor(canny, rgbcanny, CV_GRAY2BGR);
//draw all detected circles
for (int i = 0; i < circles->total; i++)
{
// round the floats to an int
float* p = (float*)cvGetSeqElem(circles, i);
cv::Point center(cvRound(p[0]), cvRound(p[1]));
int radius = cvRound(p[2]);
//uchar* ptr;
//ptr = cvPtr2D(img, center.y, center.x, NULL);
//printf("B: %d G: %d R: %d\n", ptr[0],ptr[1],ptr[2]);
CvScalar s;
s = cvGet2D(img,center.y, center.x);//colour of circle
printf("B: %f G: %f R: %f\n",s.val[0],s.val[1],s.val[2]);
// draw the circle center
cvCircle(img, center, 3, CV_RGB(0,255,0), -1, 8, 0 );
// draw the circle outline
cvCircle(img, center, radius+1, CV_RGB(0,0,255), 2, 8, 0 );
//display coordinates
printf("x: %d y: %d r: %d\n",center.x,center.y, radius);
}
//create window
//cvNamedWindow("circles", 1);
cvNamedWindow("SnookerImage", 1);
//show image in window
//cvShowImage("circles", rgbcanny);
cvShowImage("SnookerImage", img);
cvSaveImage("out.png", img);
//cvDestroyWindow("SnookerImage");
//cvDestroyWindow("circles");
//cvReleaseMemStorage("storage");
cvWaitKey(0);
return 0;
}
If you use the C++ interface, you can use
cv::cvtColor(img, img, CV_BGR2HSV);
See the documentation for cvtColor for more information.
Update:
Reading and writing pixels the slow way (assuming that the HSV values are stored as a cv::Vec3b (doc))
cv::Vec3b pixel = image.at<cv::Vec3b>(0,0); // read pixel (0,0) (make copy)
pixel[0] = 0; // H
pixel[1] = 0; // S
pixel[2] = 0; // V
image.at<cv::Vec3b>(0,0) = pixel; // write pixel (0,0) (copy pixel back to image)
Using the image.at<...>(x, y) (doc, scroll down a lot) notation is quite slow, if you want to manipulate every pixel. There is an article in the documentation on how to access the pixels faster. You can apply the iterator method also like this:
cv::MatIterator_<cv::Vec3b> it = image.begin<cv::Vec3b>(),
it_end = image.end<cv::Vec3b>();
for(; it != it_end; ++it)
{
// work with pixel in here, e.g.:
cv::Vec3b& pixel = *it; // reference to pixel in image
pixel[0] = 0; // changes pixel in image
}

Resources