I am trying to create a program that will recognize the lottery numbers automaticly.
I have recognized the draw moment, seperated the ball, and now my problem is that i cannot recognize the number on the ball.
This is the original picture:
This is my picture after i find the contours:
Now for each contour I try to determine if its a number and what number is it. This is where my app fails.
*Important to say that the ball can be in many angles/the lighting can be differnet , which all afect the quality of the pic.
This is an example of a contour img my prog found:
This is my code for recognizing the number:
private void identifyNumber(Mat inFile) {
System.out.println("\nRunning identifyNumber");
System.out.println("-------------------------");
int match_method = Imgproc.TM_SQDIFF;
Mat img = inFile;
Mat bestImage = new Mat(), rotImg;
int bestDegree = 0, bestNumber = 0;
double lowerstFornumber, lowest = 1E30;
String templateNumber;
for (int k=0 ; k<=9; k++) {
lowerstFornumber = 1E30;
for(int i=-90; i<=90; i=i+5){
templateNumber = "C:\\pics\\drawProcessing\\numbers\\" + k + ".png";
Mat templ = Highgui.imread(templateNumber);
rotImg = rotateImage(img, i);
int result_cols = rotImg.cols() - templ.cols() + 1;
int result_rows = rotImg.rows() - templ.rows() + 1;
Mat result = new Mat(result_rows, result_cols, CvType.CV_32FC1);
Imgproc.matchTemplate(rotImg, templ, result, match_method);
MinMaxLocResult mmr = Core.minMaxLoc(result);
Point matchLoc;
if (match_method == Imgproc.TM_SQDIFF || match_method == Imgproc.TM_SQDIFF_NORMED) {
matchLoc = mmr.minLoc;
} else {
matchLoc = mmr.maxLoc;
}
double minValue = mmr.minVal;
// System.out.println(i+",maxVal:" +maxValue);
if(lowerstFornumber > minValue){
lowerstFornumber = minValue;
}
if(lowest > minValue){
lowest = minValue;
bestImage = rotImg;
bestDegree = i;
bestNumber = arr[k];
}
}
System.out.println("lowerstFornumber " + arr[k] + " :" + lowerstFornumber);
}
System.out.println("bestDegree:" + bestDegree);
System.out.println("bestNumber:" + bestNumber);
System.out.println("_lowest:" + lowest);
Highgui.imwrite("C:\\pics\\drawProcessing\\out-best.jpg", bestImage);
}
Sometimes it finds the number, Sometimes not.
Is it even possible?(I need 100% accuracy)
Am I doning it wrong?
What if you try an affine invariant descriptor for your boxes? You can even start with an easier descriptor, say sift or surf, computed for every region and matched to a database. It should be fast because it looks like scale will not be changing. Sift and surf might give you some results but for something more stable you can use ASIFT.
Not in java, but it describes the idea:
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
//----------------------------------------------------------------------
//
//----------------------------------------------------------------------
void DetectContour(Mat& img, Mat& res)
{
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
Mat edges=img.clone();
//Canny(img, edges, 50, 190, 3);
img.copyTo(edges);
findContours(edges,contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE, Point());
if(contours.size()>0)
{
for( int i = 0; i < contours.size(); i++ )
{
vector<Point> approx;
approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true)*0.02, true);
double area = contourArea(Mat(approx));
if(area>200)
drawContours( res, contours, i, Scalar(255,0,0), CV_FILLED, 8);
}
}
}
//----------------------------------------------------------------------
//
//----------------------------------------------------------------------
int main(int argc, char **argv)
{
cv::namedWindow("result");
Mat img=imread("ball.png");
// Prepare mask
Mat mask=Mat::zeros(img.size(),CV_8UC1);
Mat img_gray;
cv::cvtColor(img,img_gray,cv::COLOR_BGR2GRAY);
Mat res=Mat(img.size(),CV_8UC1);
res=255;
vector<Vec3f> circles;
/// Apply the Hough Transform to find the circles
HoughCircles( img_gray, circles, cv::HOUGH_GRADIENT, 1, img_gray.rows/8, 140, 70, 0,0 );
/// Draw the circles detected
for( size_t i = 0; i < circles.size(); i++ )
{
Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
int radius = cvRound(circles[i][2]);
// circle outline
circle( mask, center, radius, Scalar(255,255,255), -1, 8, 0 );
}
img.copyTo(res,mask);
cv::cvtColor(res,res,cv::COLOR_BGR2GRAY);
threshold(res,res,80,255,cv::THRESH_BINARY_INV);
mask=0;
DetectContour(res,mask);
mask.copyTo(res);
int element_size=10;
Mat element = getStructuringElement( cv::MORPH_ELLIPSE,Size( 2*element_size + 1, 2*element_size+1 ),Point( element_size, element_size ) );
int element_size2=5;
Mat element2 = getStructuringElement( cv::MORPH_ELLIPSE,Size( 2*element_size + 1, 2*element_size+1 ),Point( element_size, element_size ) );
cv::dilate(res,res,element2);
cv::erode(res,res,element);
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours(res,contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE, Point());
for (int i=0;i<contours.size();++i)
{
RotatedRect box = minAreaRect(contours[i]);
Point2f center, vtx[4];
box.points(vtx);
float w=100;
float h=100;
// Create a column vector with the coordinates of each point (on the field plane)
cv::Mat xField;
xField.create(4, 1, CV_32FC2);
xField.at<Point2f>(0) = ( vtx[0] );
xField.at<Point2f>(1) = ( vtx[1] );
xField.at<Point2f>(2) = ( vtx[2] );
xField.at<Point2f>(3) = ( vtx[3] );
// same thing for xImage but with the pixel coordinates instead of the field coordinates, same order as in xField
cv::Mat xImage;
xImage.create(4, 1, CV_32FC2);
xImage.at<Point2f>(0) = ( cv::Point2f(0, 0) );
xImage.at<Point2f>(1) = ( cv::Point2f(w, 0) );
xImage.at<Point2f>(2) = ( cv::Point2f(w, h) );
xImage.at<Point2f>(3) = ( cv::Point2f(0, h) );
// Compute the homography matrix
cv::Mat H = cv::findHomography(xField,xImage );
xField.release();
xImage.release();
Mat warped;
warpPerspective(img,warped,H,Size(w,h));
H.release();
char win_name[255];
sprintf(win_name,"number_image %d",i);
namedWindow(win_name);
imshow(win_name,warped);
// cv::waitKey(0);
for(int j = 0; j < 4; j++ )
{
line(img, vtx[j], vtx[(j+1)%4], Scalar(0, 255, 0), 1, LINE_AA);
}
}
imshow("result",img);
cv::waitKey(0);
cv::destroyAllWindows();
}
Related
I am using this open cv 3.0 sample project of face detection.
I am writing code for my android application. I have change the code for mouth detection on face.
After detecting the face i am getting MOUTH region from FACE. but when i pass the argument "moutharea" to "detectmultiscale "
it is showing me this error "The method detectMultiScale(Mat, MatOfRect, double, int, int, Size, Size) in the type CascadeClassifier is not applicable for the arguments (Rect, MatOfRect, double, int, int, Size, Size)"
Rect[] facesArray = faces.toArray();
for (int i = 0; i < facesArray.length; i++)
{
Imgproc.rectangle(mRgba, facesArray[i].tl(), facesArray[i].br(), FACE_RECT_COLOR, 3);
Rect r = facesArray[i]; ////// for each detected face
Rect moutharea = new Rect (r.x , r.y+(r.height * 2/3), r.width,r.height/3 ); ////// for extracting lower portion of mouth
MatOfRect mouth = new MatOfRect();
if (mDetectorType == JAVA_DETECTOR) {
if (mJavaDetectorMouth != null)
mJavaDetectorMouth.detectMultiScale(motharea, mouth, 1.1, 2, 2, // TODO: objdetect.CV_HAAR_SCALE_IMAGE
new Size(mAbsoluteFaceSize, mAbsoluteFaceSize), new Size());
}
else if (mDetectorType == NATIVE_DETECTOR) {
if (mNativeDetector != null)
mNativeDetector.detect(mGray, mouth);
}
else {
Log.e(TAG, "Detection method is not selected!");
}
Rect[] mouthArray = mouth.toArray();
for (int j = 0; j < mouthArray.length; j++){
Imgproc.rectangle(mRgba, mouthArray[j].tl(), mouthArray[j].br(),new Scalar(255, 0, 0, 255), 2);
}
Imgproc.rectangle(mRgba, moutharea.tl(), moutharea.br(),new Scalar(255, 0, 0, 255), 2);
}
return mRgba;
}
i am also searching for this i found help in this link cascade classifier Object detection. This is exactly what you want i think. you to use multiScaleDetector for the detected face. But i dont know how to implement this in android.
#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
using namespace std;
using namespace cv;
/** Function Headers */
void detectAndDisplay( Mat frame );
/** Global variables */
String face_cascade_name = "haarcascade_frontalface_alt.xml";
String eyes_cascade_name = "haarcascade_eye_tree_eyeglasses.xml";
CascadeClassifier face_cascade;
CascadeClassifier eyes_cascade;
string window_name = "Capture - Face detection";
RNG rng(12345);
/** #function main */
int main( int argc, const char** argv )
{
CvCapture* capture;
Mat frame;
//-- 1. Load the cascades
if( !face_cascade.load( face_cascade_name ) ){ printf("--(!)Error
loading\n"); return -1; };
if( !eyes_cascade.load( eyes_cascade_name ) ){ printf("--(!)Error
loading\n"); return -1; };
//-- 2. Read the video stream
capture = cvCaptureFromCAM( -1 );
if( capture )
{
while( true )
{
frame = cvQueryFrame( capture );
//-- 3. Apply the classifier to the frame
if( !frame.empty() )
{ detectAndDisplay( frame ); }
else
{ printf(" --(!) No captured frame -- Break!"); break; }
int c = waitKey(10);
if( (char)c == 'c' ) { break; }
}
}
return 0;
}
/** #function detectAndDisplay */
void detectAndDisplay( Mat frame )
{
std::vector<Rect> faces;
Mat frame_gray;
cvtColor( frame, frame_gray, CV_BGR2GRAY );
equalizeHist( frame_gray, frame_gray );
//-- Detect faces
face_cascade.detectMultiScale( frame_gray, faces, 1.1, 2,
0|CV_HAAR_SCALE_IMAGE, Size(30, 30) );
for( size_t i = 0; i < faces.size(); i++ )
{
Point center( faces[i].x + faces[i].width*0.5, faces[i].y +
faces[i].height*0.5 );
ellipse( frame, center, Size( faces[i].width*0.5, faces[i].height*0.5),
0, 0, 360, Scalar( 255, 0, 255 ), 4, 8, 0 );
Mat faceROI = frame_gray( faces[i] );
std::vector<Rect> eyes;
//-- In each face, detect eyes
eyes_cascade.detectMultiScale( faceROI, eyes, 1.1, 2, 0
|CV_HAAR_SCALE_IMAGE, Size(30, 30) );
for( size_t j = 0; j < eyes.size(); j++ )
{
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, Scalar( 255, 0, 0 ), 4, 8, 0 );
}
}
//-- Show what you got
imshow( window_name, frame );
}
You need this to code according to java language... may be somone here can help you out... :)
void detectAndDisplay( Mat frame ,int i)
{
std::vector<Rect> faces;
Mat frame_gray;
cvtColor( frame, frame_gray, CV_BGR2GRAY );
equalizeHist( frame_gray, frame_gray );
//-- Detect faces
//face_cascade.detectMultiScale( frame_gray, faces, 1.1, 2, 0|CV_HAAR_SCALE_IMAGE, Size(30, 30) );
face_cascade.detectMultiScale( frame_gray, faces, 1.1, 1, 0|CV_HAAR_SCALE_IMAGE, Size(20, 20) );
for( size_t i = 0; i < faces.size(); i++ )
{
Point center( faces[i].x + faces[i].width*0.5, faces[i].y + faces[i].height*0.5 );
ellipse( frame, center, Size( faces[i].width*0.5, faces[i].height*0.5), 0, 0, 360, Scalar( 255, 0, 255 ), 4, 8, 0 );
Mat faceROI = frame_gray( faces[i] );
std::vector<Rect> eyes;
//-- In each face, detect eyes
//eyes_cascade.detectMultiScale( faceROI, eyes, 1.1, 2, 0 |CV_HAAR_SCALE_IMAGE, Size(30, 30) );
for( size_t j = 0; j < eyes.size(); j++ )
{
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, Scalar( 255, 0, 0 ), 4, 8, 0 );
}
}
//-- Show what you got
//imshow( window_name, frame );
char filename[512];
sprintf(filename,"C:\\out\\image%d.jpg",i);
imwrite(filename,frame);
}
I have applied global threshold,adaptive threshold,dilation and erosion also but cant get the expected result.
Imgproc.threshold(source2, destination2, 147, 255,Imgproc.THRESH_BINARY );
Highgui.imwrite("threshold.jpg", destination2);
Imgproc.adaptiveThreshold(destination2, destination2, 255,
Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY, 11,2);
Highgui.imwrite("Adpthreshold.jpg", destination2);
Mat destination3 = new Mat(source.rows(),source.cols(),source.type());double erosion_size = 0;
int dilation_size = 1;
Mat element = Imgproc.getStructuringElement(Imgproc.MORPH_CROSS, new Size(2*erosion_size + 1, 2*erosion_size+1));
Imgproc.erode(destination2, destination3, element);
Highgui.imwrite("erosion.jpg", destination3);
Mat element1 = Imgproc.getStructuringElement(Imgproc.MORPH_CROSS, new Size(2*dilation_size + 1, 2*dilation_size+1));
Imgproc.dilate(destination3, destination3, element1);
Highgui.imwrite("dilation.jpg", destination3);
Here is the final image:
this code is open to improvement according to your desired result.by this approach you can remove most of small dots. i hope it helps you.
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;
int main( int, char** argv )
{
Mat src,src_gray;
src = imread("4sE4p.jpg");
if (src.empty())
{
cerr << "No image supplied ..." << endl;
return -1;
}
cvtColor( src, src_gray, COLOR_BGR2GRAY );
src_gray = src_gray >160;
src_gray.copyTo(src);
imshow( "src_gray", src_gray );
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
cv::Mat kernel = cv::Mat::ones(2, 10, CV_8U);
erode(src_gray,src_gray, kernel, Point(-1,-1),2);
findContours( src_gray, contours, hierarchy, RETR_LIST, CHAIN_APPROX_SIMPLE, Point(0, 0) );
for( size_t i = 0; i< contours.size(); i++ )
{
Rect R = boundingRect(Mat(contours[i]));
if( R.width*R.height < 300 )
{
Mat roi(src_gray,R);
if (countNonZero(roi) < R.width*R.height*0.9 )
{
rectangle(src,R,Scalar(0,0,255));
Mat croi(src,R);
croi.setTo(255); // this line is to clear small dots
}
}
}
imshow( "result", src );
waitKey(0);
return(0);
}
I'm developing an android app which main purpose is detects an asked object in a scenario. To do this I'm using the SURF algorithm of OpenCV. I'm not having "good luck" with the detection because I don't know when an object is "found".
I obtain a frame with my device camera and I follow these steps to get objects' keypoints and descriptors:
Java Code
public void onSnapClick(View v) {
Imgproc.GaussianBlur(frameGray, frameGray, new Size(3, 3), 2);
Imgproc.Canny(frameGray, frameGray, 40, 120);
Imgproc.resize(frameGray, frameGray, new Size(320, 240));
FindFeatures(frameGray.getNativeObjAddr()); //JNI call
//Some code to store data in DB...
}
JNI call
double hessianThreshold=600;
int nOctaves=4;
int nOctaveLayers=2;
bool extended=true;
bool upright=false;
JNIEXPORT void JNICALL Java_es_ugr_reconocimiento_Juego_FindFeatures(JNIEnv* env, jobject, jlong addrGray) {
Mat& frameGray= *(Mat*) addrGray;
vector<KeyPoint> keyPoints;
Mat descriptores;
SurfFeatureDetector detector_Surf(hessianThreshold, nOctaves, nOctaveLayers, extended, upright);
SurfDescriptorExtractor extractor_Surf;
detector_Surf.detect(frameGray, keyPoints);
if (keyPoints.size() > 0)
extractor_Surf.compute(frameGray, keyPoints, descriptores);
}
Now I choose what object I want to find and I follow these steps to do that:
Java Code
public void onSearchClick(View v) {
Imgproc.GaussianBlur(frameGray, frameGray, new Size(3, 3), 2);
Imgproc.Canny(frameGray, frameGray, 40, 120);
Imgproc.resize(frameGray, frameGray, new Size(320, 240));
nObject = FindObjects(frameGray.getNativeObjAddr()); //JNI call
if (nObject = searchObject)
//draw frame with a rectangle around the found object in the scenario....
}
JNI call
double hessianThreshold=600;
int nOctaves=4;
int nOctaveLayers=2;
bool extended=true;
bool upright=false;
JNIEXPORT jint JNICALL Java_es_ugr_reconocimiento_Juego_FindObjects(JNIEnv* env, jobject, jlong addrGray) {
Mat& frameGray = *(Mat*) addrGray;
vector<KeyPoint> keyPoints_esc;
Mat descriptores_esc;
SurfFeatureDetector detector_Surf(hessianThreshold, nOctaves, nOctaveLayers, extended, upright);
SurfDescriptorExtractor extractor_Surf;
detector_Surf.detect(frameGray , keyPoints_esc);
if (keyPoints_esc.size() == 0) return -1;
extractor_Surf.compute(frameGray , keyPoints_esc, descriptores_esc);
if (descriptores_esc.rows() == 0) return -1;
for(int i=0;i<lstObjects.size();i++){
Mat descriptores_obj = lstDescriptors.at(i);
vector<KeyPoint> keyPoints_obj = lstKeyPoints.at(i);
FlannBasedMatcher matcher;
vector<vector<DMatch> > matches;
matcher.knnMatch(descriptores_obj, descriptores_esc, matches, 2);
// ----------------------------------------------------------------------
// Draw only "good" matches (i.e. whose distance is less than 2*min_dist,
// or a small arbitary value ( 0.02 ) in the event that min_dist is very
// small)
// PS.- radiusMatch can also be used here.
// ----------------------------------------------------------------------
vector<DMatch> good_matches;
//THIS LOOP IS SENSITIVE TO SEGFAULTS
for (int i = 0; i < min(descriptores_obj.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]);
}
}
if (good_matches.size() >= nThreshold) {
vector < Point2f > obj;
vector < Point2f > scene;
for (int i = 0; i < good_matches.size(); i++) {
//-- Get the keypoints from the good matches
obj.push_back(keyPoints_obj[good_matches[i].queryIdx].pt);
scene.push_back(keyPoints_esc[good_matches[i].trainIdx].pt);
}
Mat H = findHomography(obj, scene, CV_RANSAC);
vector<Point2f> obj_corners(4);
obj_corners[0] = cvPoint(0, 0);
obj_corners[1] = cvPoint(240, 0);
obj_corners[2] = cvPoint(240, 320);
obj_corners[3] = cvPoint(0, 320);
vector<Point2f> scene_corners(4);
perspectiveTransform(obj_corners, scene_corners, H);
line(frameGray, scene_corners[0], scene_corners[1], Scalar(255, 0, 0), 4);
line(frameGray, scene_corners[1], scene_corners[2], Scalar(255, 0, 0), 4);
line(frameGray, scene_corners[2], scene_corners[3], Scalar(255, 0, 0), 4);
line(frameGray, scene_corners[3], scene_corners[0], Scalar(255, 0, 0), 4);
for (unsigned int i = 0; i < scene.size(); i++) {
const Point2f& kp = scene[i];
circle(frameGray, Point(kp.x, kp.y), 10, Scalar(255, 255, 255, 255));
}
return i; //position of the matched object
}
}
}
I don't know what threshold could be the best in this comparison
if (good_matches.size() >= nThreshold) // do findHomography...
I've been searching and almost every code I found contained the number 4 as nThreshold, but for me it's not working good. My code almost every time "find" an object.
Is there any other better way to do this? Like using different matcher or another threshold or trying to figure out if doing the homography is going to create something similar to a rectangle (i said this because sometimes it "find" something but drawing four lines that aren't building a rectangle).
Please make this following changes in your code
int nThreshold= 100;
if (good_matches.size() >= nThreshold)
{
continue; // This line is to prevent further steps of matching if there are too many good matches (Lot of ambiguous points results in false match)
}
vector < Point2f > obj;
vector < Point2f > scene;
for (int i = 0; i < good_matches.size(); i++) {
//-- Get the keypoints from the good matches
obj.push_back(keyPoints_obj[good_matches[i].queryIdx].pt);
scene.push_back(keyPoints_esc[good_matches[i].trainIdx].pt);
}
// Skip doing homography if the object and scene contains less than four points(cant draw a rectangle if less than 4 points, hence your program will crash here if you do not handle the exception)
if(obj.size() < 4 || scene.size() < 4)
{
continue;
}
Mat H = findHomography(obj, scene, CV_RANSAC);
I want to use the imgradient() function of matlab in my android application using opencv. how can i do so and which function of opencv is equivalent to to Matlab imgradient() function.
i m using below mentioned function is it right ?
public Mat imgradient(Mat grayScaleImage)
{
Mat grad_x=new Mat();
Mat grad_y = new Mat();
Mat abs_grad_x=new Mat();
Mat abs_grad_y=new Mat();
Mat gradientImag = new Mat(grayScaleImage.rows(),grayScaleImage.cols(),CvType.CV_8UC1);
Imgproc.Sobel(grayScaleImage, grad_x, CvType.CV_16S, 1, 0,3,1,0,Imgproc.BORDER_DEFAULT );
Core.convertScaleAbs( grad_x, abs_grad_x );
Imgproc.Sobel( grayScaleImage, grad_y, CvType.CV_16S, 0, 1, 3, 1,0,Imgproc.BORDER_DEFAULT );
Core.convertScaleAbs( grad_y, abs_grad_y );
double[] buff_grad = new double[1];
for(int i = 0; i < abs_grad_y.cols(); i++)
{
for(int j =0 ; j<abs_grad_y.rows() ; j++)
{
double[] buff_x = abs_grad_x.get(j, i);
double[] buff_y = abs_grad_y.get(j, i);
double x = buff_x[0];
double y = buff_y[0];
double ans=0;
try
{
ans = Math.sqrt(Math.pow(x,2)+Math.pow(y,2));
}catch(NullPointerException e)
{
ans = 0;
}
buff_grad[0] = ans;
gradientImag.put(j, i, buff_grad);
}
}
return gradientImag;
}
Have you tried using something like sobel or canny operators?
As matlab imgradient() returns the gradient "magnitude" (i.e. sqrt(dx(x,y)² + dy(x,y)²) for each pixel with coordinates x,y), you may want to do something like:
// 1) Get the horizontal gradient
Mat kH = (cv::Mat_<double>(1,3) << -1,0,1); // differential kernel in x
Mat Dx;
filter2D(image, Dx, -1, kH, cv::Point(-1,-1), 0);
// 2) Get the vertical gradient
Mat kV = (cv::Mat_<double>(3,1) << -1,0,1); // differential kernel in y
Mat Dy;
filter2D(image, Dy, -1, kV, cv::Point(-1,-1), 0);
// 3) Get sqrt(dx²+dy²) in each point
for(int i=0; i<Dx.rows; i++)
for(int j=0; j<Dx.cols; j++)
Dmag.at<double>(i,j) = sqrt(pow(Dx.at<double>(i,j),2)+pow(Dy.at<double>(i,j),2));
It should get you what you you want. You can achieve a better performance by accessing gradient data instead of using .at(i,j) for each pixel.
Hope it helps!
I went through many questions in StackOverflow and able to develop small program to detect squares and rectangles correctly. This is my sample code
public static CvSeq findSquares(final IplImage src, CvMemStorage storage) {
CvSeq squares = new CvContour();
squares = cvCreateSeq(0, sizeof(CvContour.class), sizeof(CvSeq.class), storage);
IplImage pyr = null, timg = null, gray = null, tgray;
timg = cvCloneImage(src);
CvSize sz = cvSize(src.width(), src.height());
tgray = cvCreateImage(sz, src.depth(), 1);
gray = cvCreateImage(sz, src.depth(), 1);
// cvCvtColor(gray, src, 1);
pyr = cvCreateImage(cvSize(sz.width() / 2, sz.height() / 2), src.depth(), src.nChannels());
// down-scale and upscale the image to filter out the noise
// cvPyrDown(timg, pyr, CV_GAUSSIAN_5x5);
// cvPyrUp(pyr, timg, CV_GAUSSIAN_5x5);
// cvSaveImage("ha.jpg",timg);
CvSeq contours = new CvContour();
// request closing of the application when the image window is closed
// show image on window
// find squares in every color plane of the image
for (int c = 0; c < 3; c++) {
IplImage channels[] = { cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1) };
channels[c] = cvCreateImage(sz, 8, 1);
if (src.nChannels() > 1) {
cvSplit(timg, channels[0], channels[1], channels[2], null);
} else {
tgray = cvCloneImage(timg);
}
tgray = channels[c];
// // try several threshold levels
for (int l = 0; l < N; l++) {
// hack: use Canny instead of zero threshold level.
// Canny helps to catch squares with gradient shading
if (l == 0) {
// apply Canny. Take the upper threshold from slider
// and set the lower to 0 (which forces edges merging)
cvCanny(tgray, gray, 0, thresh, 5);
// dilate canny output to remove potential
// // holes between edge segments
cvDilate(gray, gray, null, 1);
} else {
// apply threshold if l!=0:
cvThreshold(tgray, gray, (l + 1) * 255 / N, 255,
CV_THRESH_BINARY);
}
// find contours and store them all as a list
cvFindContours(gray, storage, contours, sizeof(CvContour.class), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
CvSeq approx;
// test each contour
while (contours != null && !contours.isNull()) {
if (contours.elem_size() > 0) {
approx = cvApproxPoly(contours, Loader.sizeof(CvContour.class), storage, CV_POLY_APPROX_DP, cvContourPerimeter(contours) * 0.02, 0);
if (approx.total() == 4 && Math.abs(cvContourArea(approx, CV_WHOLE_SEQ, 0)) > 1000 && cvCheckContourConvexity(approx) != 0) {
double maxCosine = 0;
for (int j = 2; j < 5; j++) {
// find the maximum cosine of the angle between
// joint edges
double cosine = Math.abs(angle(
new CvPoint(cvGetSeqElem(
approx, j % 4)),
new CvPoint(cvGetSeqElem(
approx, j - 2)),
new CvPoint(cvGetSeqElem(
approx, j - 1))));
maxCosine = Math.max(maxCosine, cosine);
}
if (maxCosine < 0.2) {
CvRect x = cvBoundingRect(approx, l);
if ((x.width() * x.height()) < 50000) {
System.out.println("Width : " + x.width()
+ " Height : " + x.height());
cvSeqPush(squares, approx);
}
}
}
}
contours = contours.h_next();
}
contours = new CvContour();
}
}
return squares;
}
I use this image to detect rectangles and squares
I need to identify the following output
and
But when I run the above code, it detects only the following rectangles. But I don't know the reason for that. Please can someone explain the reason for that.
This is the output that I got.
Please be kind enough to explain the problem in above code and give some suggensions to detect this squares and rectangles.
Given a mask image (binary image, like your second figure), cvFindContours() gives you the contours (several list of points).
look at this link: http://dasl.mem.drexel.edu/~noahKuntz/openCVTut7.html