opencv rechteckigen Bereich betrachten



  • Hallo,

    ich möchte meinen Scanner dazu benutzen um Puzzleteile einzuscannen und später zu einem Puzzle zusammenbauen zulassen. Für das grafische wollte ich opencv 2.4.3 verwenden.

    Habe meinen Scanner angeworfen und mit 600 dpi ein paar Teile eingescannt.
    http://s14.directupload.net/file/d/3120/5zxygdyq_jpg.htm

    Danach habe ich mittels "inRange" meine Puzzleteile halbwegs gut freigestellt.
    http://s14.directupload.net/file/d/3120/oor7ucpo_jpg.htm
    (die Kannten kann man jedenfalls gut erahnen)

    Das nächste Ziel ist Punkte auf dem Rand der Puzzleteile zu erhalten.
    Dazu hatte ich mir gedacht, das Bild in lauter kleine Quadrate zu unterteilen.
    Dann von oben links beginnend jedes dieser Quadrate abzulaufen, bis ich auf eines treffe, welches nicht komplett schwarz ist (Dieses gehört dann zum rand meines Puzzleteiles), welches als nächstes einmal komplett umlaufen wird.
    Diesen Ausschnitt wollte ich danach in ein neues Bild speichern zu weiteren Verarbeitung und ihn auf dem Originalbild schwarz färben, sodass er nicht mehr gefunden wird.

    Meine Frage ist, wie bekomme ich Zugang zu den Quadraten, die mein Bild unterteilen sollen, und wie könnte man dort effektiv überprüfen, ob alle Pixel schwarz sind?
    Bin für jeden Tipp dankbar.

    MfG Tom

    Hier ist der Code, der aus dem Original das schwarz weiß Bild generiert.

    #include "stdafx.h"
    #include <iostream>
    #include <stdlib.h>
    #include <stdio.h>
    #include <math.h>
    #include<opencv\cv.h>
    #include<opencv\highgui.h>
    using namespace cv;
    #include <cstring>
    
    int _tmain(int argc, char *argv[])
    {
    
    	string str = "K:\\Projekte\\C++\\Puzzle Solver\\Ress\\Ausschnitt.jpg";
    	char * buffer = new char[str.length()+1];
    	strcpy(buffer,str.c_str());
    
    	char* imageName = buffer;
    	Mat image;
    	image = imread( imageName, 1 );
    	if(!image.data )
    	{
    	printf( " No image data \n " );
    	return -1;
    	}	
    	Mat mask;
    	//Varibles to hold scrollbar values
        int hl=0; int hu=255;
    	int sl=0; int su=255;
    	int vl=0; int vu=114;
    
        //Window
        cvNamedWindow("Display window",CV_WINDOW_AUTOSIZE);
        //Scrollbars
        cvCreateTrackbar("vu","Display window",&vu,255,0);
        cvCreateTrackbar("vl","Display window",&vl,255,0);
    
    	blur( image, image, Size(3,3) );
    	while(1)
        {	
    		inRange(image, Scalar(hl, sl, vl), Scalar(hu, su, vu), mask);
    		bitwise_not(mask,mask);
            //Show the image
    		imshow( "Display window", mask );
    
            //Escape Sequence
            char c=cvWaitKey(33);
            if(c==27)
                break;
        }
    
    	imwrite( "K:\\Projekte\\C++\\Puzzle Solver\\Ress\\Output.jpg", mask );
    
    	//Cleanup
        image.release();
    	mask.release();
        cvDestroyAllWindows();
    	return 0;
    }
    


  • Hi ich bin es noch einmal.

    Bin jetzt soweit, dass ich das Bild in lauter kleine Teilquadrate unterteilt habe.
    Konnte ich mittels der Funktion "countNonZero" feststellen ob sich ein Teil des Puzzleteiles in diesem Quadrat befindet. War dies der Fall habe ich das Puzzle ersteinmal grob umrandet. Habt ihr eine Idee wie ich den so gefunden unregelmäßigen Bereich effektiv komplett schwärzen kann ?

    // Puzzle Solver.cpp : Definiert den Einstiegspunkt für die Konsolenanwendung.
    
    /* include
    */
    #include "stdafx.h"
    #include <iostream>
    #include <cstring>
    #include <stdlib.h>
    #include <stdio.h>
    #include <math.h>
    #include<opencv\cv.h>
    #include<vector>
    #include<opencv\highgui.h>
    using namespace cv;
    
    class position
    {
    public:
    	position(int x = 0, int y = 0) 
    		: pos_x(x), pos_y(y)
    	{}
    
    	inline int& x() { return pos_x; }
    	inline const int& x() const { return pos_x; }
    	inline int& y() { return pos_y; }
    	inline const int& y() const { return pos_y; }
    
    	bool operator==(const position& rhs) const
    	{
    		return (pos_x == rhs.pos_x && pos_y == rhs.pos_y);
    	}
    	bool operator!=(const position& rhs) const
    	{
    		return !(*this == rhs);
    	}
    	bool operator<(const position& rhs) const
    	{
    		return (pos_x * pos_x + pos_y * pos_y) < 
    			(rhs.pos_x * rhs.pos_x + rhs.pos_y * rhs.pos_y);
    		//impliment less than so this class works with std::map
    	}
    
    private:
    	friend std::ostream& operator<<(std::ostream&, const position&);
    	int pos_x, pos_y;
    };
    
    std::ostream& operator<<(std::ostream &strm, const position &a) {
      return strm << "(" << a.x() << "," << a.y() << ")";
    }
    
    void testN(int,int,int,int,int,int,int,int,Mat &,int,vector<position> &);
    void testO(int,int,int,int,int,int,int,int,Mat &,int,vector<position> &);
    void testS(int,int,int,int,int,int,int,int,Mat &,int,vector<position> &);
    void testW(int,int,int,int,int,int,int,int,Mat &,int,vector<position> &);
    
    string getImgType(int imgTypeInt)
    {
        int numImgTypes = 35; // 7 base types, with five channel options each (none or C1, ..., C4)
    
        int enum_ints[] =       {CV_8U,  CV_8UC1,  CV_8UC2,  CV_8UC3,  CV_8UC4,
                                 CV_8S,  CV_8SC1,  CV_8SC2,  CV_8SC3,  CV_8SC4,
                                 CV_16U, CV_16UC1, CV_16UC2, CV_16UC3, CV_16UC4,
                                 CV_16S, CV_16SC1, CV_16SC2, CV_16SC3, CV_16SC4,
                                 CV_32S, CV_32SC1, CV_32SC2, CV_32SC3, CV_32SC4,
                                 CV_32F, CV_32FC1, CV_32FC2, CV_32FC3, CV_32FC4,
                                 CV_64F, CV_64FC1, CV_64FC2, CV_64FC3, CV_64FC4};
    
        string enum_strings[] = {"CV_8U",  "CV_8UC1",  "CV_8UC2",  "CV_8UC3",  "CV_8UC4",
                                 "CV_8S",  "CV_8SC1",  "CV_8SC2",  "CV_8SC3",  "CV_8SC4",
                                 "CV_16U", "CV_16UC1", "CV_16UC2", "CV_16UC3", "CV_16UC4",
                                 "CV_16S", "CV_16SC1", "CV_16SC2", "CV_16SC3", "CV_16SC4",
                                 "CV_32S", "CV_32SC1", "CV_32SC2", "CV_32SC3", "CV_32SC4",
                                 "CV_32F"  "CV_32FC1", "CV_32FC2", "CV_32FC3", "CV_32FC4",
                                 "CV_64F", "CV_64FC1", "CV_64FC2", "CV_64FC3", "CV_64FC4"};
    
        for(int i=0; i<numImgTypes; i++)
        {
            if(imgTypeInt == enum_ints[i]) return enum_strings[i];
        }
        return "unknown image type";
    }
    // take number image type number (from cv::Mat.type()), get OpenCV's enum string.
    
    int _tmain(int argc, char *argv[])
    {
    	const int dpi = 600;
    
    	string str = "K:\\Projekte\\C++\\Puzzle Solver\\Ress\\Ausschnitt.jpg";
    	char * buffer = new char[str.length()+1];
    	strcpy(buffer,str.c_str());
    
    	char* imageName = buffer;
    	Mat image;Mat dstImage;
    	image = imread( imageName, 1 );
    	std::cout << getImgType(image.type());
    	if(!image.data )
    	{
    	printf( " No image data \n " );
    	return -1;
    	}
    
    	Mat mask;
    	//Varibles to hold scrollbar values
        int hl=0; int hu=255;
    	int sl=0; int su=255;
    	int vl=0; int vu=97;
    
        //Window
        cvNamedWindow("Display window",CV_WINDOW_AUTOSIZE);
        //Scrollbars
        //cvCreateTrackbar("hu","Display window",&hu,255,0);
        //cvCreateTrackbar("hl","Display window",&hl,255,0);
        //cvCreateTrackbar("su","Display window",&su,255,0);
        //cvCreateTrackbar("sl","Display window",&sl,255,0);
        cvCreateTrackbar("vu","Display window",&vu,255,0);
        cvCreateTrackbar("vl","Display window",&vl,255,0);
    
    	blur( image, image, Size(3,3) );
    	while(1)
        {	
    		inRange(image, Scalar(hl, sl, vl), Scalar(hu, su, vu), mask);
    		std::cout << getImgType(mask.type()) << std::endl;
    		std::cout << " " << mask.cols << " " << mask.rows << std::endl;
    		std::cout<< countNonZero(mask) <<std::endl;
            //Show the image
    		imshow( "Display window", mask );
    
            //Escape Sequence
            char c=cvWaitKey(33);
            if(c==27)
                break;
        }
    
    	int in; int jn;
    	bitwise_not(mask,mask);
    	std::cout << getImgType(mask.type()) << std::endl;
    	std::cout << " " << mask.cols << " " << mask.rows << std::endl;
    	int intervall = (int)ceil(0.05*dpi/2.54); // Gitterintervall
    	for(int i=0; i<mask.rows; i+=intervall)
    		for(int j=0; j<mask.cols; j+=intervall)
    		{
    			in = (i+intervall<=mask.rows) ? (i+intervall) : (mask.rows);
    			jn = (j+intervall<=mask.cols) ? (j+intervall) : (mask.cols);
    			//std::cout << "(" <<i<< " " << in << ")" << " to (" << j << " " << jn << ")" << std::endl;
    			Mat subImg = mask(Range(i, in), Range(j, jn));
    			if (countNonZero(subImg)>0) // mögliches Puzzle-Teil gefunden
    			{
    				int il = i; int inl = in; int jl = j-intervall; int jnl = j;
    				vector<position> V; // enthält dann später Stützpunkte auf der Puzzlekante
    				testO(il,inl,jl,jnl,il,inl,jl,jnl,mask,intervall,V); // beginnt das Puzzleteil zu Umlaufen; teste ob das Feld östlich ein Stück des Puzzels enthält
    				for(vector<position>::iterator it = V.begin(); it != V.end(); ++it) { // Vector ausgeben
    					  std::cout << *it << std::endl;
    				}
    				std::cout << "Puzzleteil umrandet" << std::endl;
    				goto SprungMarke; // nur temporär damit erst einmal nur ein Puzzelteil bearbeitet wird
    			}
    			//break;
    		}
    	SprungMarke: 
    
    	imwrite( "K:\\Projekte\\C++\\Puzzle Solver\\Ress\\Output.jpg", mask );
    
    	while(1)
        {
            //Escape Sequence
            char c=cvWaitKey(33);
            if(c==27)
                break;
    	}
    	//Cleanup
        image.release();
    	mask.release();
        cvDestroyAllWindows();
    	return 0;
    
    }
    
    void testO(int is,int ins,int js,int jns,int i,int in,int j,int jn,Mat &image,int intervall,vector<position> &V){
    	//std::cout << "Teste Osten" << std::endl;
    	if (!V.empty() && is == i && ins == in && js == j && jns == jn)
    		return;
    	Mat subImg;
    	subImg = image(Range(i, in), Range(jn, (jn+intervall<=image.cols) ? (jn+intervall) : (image.cols)));
    	if (countNonZero(subImg)>0) // mögliches Puzzle-Teil gefunden
    	{
    		testN(is,ins,js,jns,i,in,j,jn,image,intervall,V);
    	}else{
    		V.push_back(position(i,j));
    		//std::cout << "gehe Osten" << std::endl;
    		testS(is,ins,js,jns,i,in,jn,(jn+intervall<=image.cols) ? (jn+intervall) : (image.cols),image,intervall,V); // wenn nicht dann betrehte nächstes Teil in Suchrichtung und drehe dich um 90° nach rechts
    	}
    }
    
    void testN(int is,int ins,int js,int jns,int i,int in,int j,int jn,Mat &image,int intervall,vector<position> &V){
    	//std::cout << "Teste Norden" << std::endl;
    	if (!V.empty()  && is == i && ins == in && js == j && jns == jn)
    		return;
    	Mat subImg;
    	subImg = image(Range(i-intervall, i), Range(j, jn));
    	if (countNonZero(subImg)>0) // mögliches Puzzle-Teil gefunden
    	{
    		testW(is,ins,js,jns,i,in,j,jn,image,intervall,V);
    	}else{
    		V.push_back(position(i,j));
    		//std::cout << "gehe Norden" << std::endl;
    		testO(is,ins,js,jns,i-intervall,i,j,jn,image,intervall,V); // wenn nicht dann betrehte nächstes teil und drehe dich um 90° nach rechts
    	}
    }
    
    void testS(int is,int ins,int js,int jns,int i,int in,int j,int jn,Mat &image,int intervall,vector<position> &V){
    	//std::cout << "Teste Sueden" << std::endl;
    	if (!V.empty()  && is == i && ins == in && js == j && jns == jn)
    		return;
    	Mat subImg;
    	subImg = image(Range(in, (in+intervall<=image.rows) ? (in+intervall) : (image.rows)), Range(j, jn));
    	if (countNonZero(subImg)>0) // mögliches Puzzle-Teil gefunden
    	{
    		testO(is,ins,js,jns,i,in,j,jn,image,intervall,V);
    	}else{
    		V.push_back(position(i,j));
    		//std::cout << "gehe Sueden" << std::endl;
    		testW(is,ins,js,jns,in,(in+intervall<=image.rows) ? (in+intervall) : (image.rows),j,jn,image,intervall,V); // wenn nicht dann betrehte nächstes teil und drehe dich um 90° nach rechts
    	}
    }
    
    void testW(int is,int ins,int js,int jns,int i,int in,int j,int jn,Mat &image,int intervall,vector<position> &V){
    	//std::cout << "Teste Westen" << std::endl;
    	if (!V.empty()  && is == i && ins == in && js == j && jns == jn)
    		return;
    	Mat subImg;
    	subImg = image(Range(i, in), Range(j-intervall, j));
    	if (countNonZero(subImg)>0) // mögliches Puzzle-Teil gefunden
    	{
    		testS(is,ins,js,jns,i,in,j,jn,image,intervall,V);
    	}else{
    		V.push_back(position(i,j));
    		//std::cout << "gehe Westen" << std::endl;
    		testN(is,ins,js,jns,i,in,j-intervall,j,image,intervall,V); // wenn nicht dann betritt nächstes teil und drehe dich um 90° nach rechts
    	}
    }
    

    Gruß Tom



  • Gibts einen Grund, warum du nicht einfach cv::findContours benutzt?
    http://docs.opencv.org/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html#findcontours



  • ich kann dir nicht sagen wie man das Bild schwarz färben sollte, doch wie so sollte man es überhaupt schwarz färben!? 😮

    sodass er nicht mehr gefunden wird

    dann würde ich das Bild einfach löschen 😉

    Ok du willst nur die Rechtecke schwarz färben?

    gruß
    coco



  • Der derzeitige Algorithmus generiert ein Bild welches die Puzzleteile weiß färbt und den Rest schwarz. Danach sucht er Zeilenblockweiße das Bild ab, bis es auf einen Teil des Puzzleteiles trifft. Ist dies passiert umrundet der Algorithmus das Puzzleteil. Wenn ich nach getaner Umrundung diesen bereich nicht auch schwarz färbe könnte es passieren, dass ich im weiteren erneut das Puzzleumrunden würde.

    Das find findContours hatte ich probiert, aber irgendwie sieht mein Bild nach benutzen der Funktion nur aus wie ein rießiges Labyrinth. Mein Ziel ist es ja viele Koordinatenpunkte am Rand der Puzzleteile zu finden (die ich dann auch jedem Puzzleteil zuornden kann) Durch diese Punkte wollte ich dann Splines legen die die Puzzlekante appromximieren.

    Vlt. ist das auch viel zu umständlich gedacht.



  • ja, findContours verwurschtelt das bild, deshalb sollte man davor sowas machen:

    Mat image; // das eigentliche Bild
    Mat findImage(image.clone()); // Wegwerfkopie
    findContours(findImage...);
    

    Aber kein Grund, sone praktische Funktion nicht zu benutzen, vorallem da sie genau das macht, was du suchst, nämlich die Punkte auf den Randkurven deiner Puzzleteile ermitteln. 😉



  • Danke für den Tipp,

    die Kanten habe ich jetzt durch deine Funktion einigermaßen gut hinbekommen.

    Nun wäre mein nächster Schritt gewesen, von jedem Puzzleteil die 4 Eckpunkte zu bestimmen. An jedem Punkt i der Kontur habe ich durch die nächsten 20 aufeinanderfolgenden Konturpunkte eine regressionsgerade gelegt. Und eine weitere regressions Gerade durch die i+20 bis i+40 Punkte. Diese beiden habe ich dann geschnitten. Ich hatte mir dann gedacht, wenn der Winkel zwischen 80° und 90° liegt. Dass dieser Punkt eventuell zu einer Ecke gehört.

    Um bei dem Testbild aber alle Ecken überhaupt mit auf dem Radar zu haben musste ich Winkel zwischen 65° und 90° akzeptieren.

    Nun habe ich das ganze mit einem größeren Ausschnitt gemacht, wo mehr als zwei Puzzleteile zu sehen waren. Die Konturen findet er auch da. Nur die möglichen Ecken zeigt er mir auf nur 2-3 der Teile an. Kann mir jemand sagen wo da mein Fehler ist.

    Habt ihr vielleicht eine bessere Idee, wie man die Ecken der Puzzleteile finden kann ?

    Gruß Tom

    // Puzzle Solver.cpp : Definiert den Einstiegspunkt für die Konsolenanwendung.
    
    /* include
    */
    #include <stdio.h>
    #include <tchar.h>
    #include <iostream>
    #include <cstring>
    #include <stdlib.h>
    #include <stdio.h>
    #include <math.h>
    #include <vector>
    #include<opencv\cv.h>
    #include<opencv\highgui.h>
    using namespace cv;
    
    string getImgType(int imgTypeInt)
    {
        int numImgTypes = 35; // 7 base types, with five channel options each (none or C1, ..., C4)
    
        int enum_ints[] =       {CV_8U,  CV_8UC1,  CV_8UC2,  CV_8UC3,  CV_8UC4,
                                 CV_8S,  CV_8SC1,  CV_8SC2,  CV_8SC3,  CV_8SC4,
                                 CV_16U, CV_16UC1, CV_16UC2, CV_16UC3, CV_16UC4,
                                 CV_16S, CV_16SC1, CV_16SC2, CV_16SC3, CV_16SC4,
                                 CV_32S, CV_32SC1, CV_32SC2, CV_32SC3, CV_32SC4,
                                 CV_32F, CV_32FC1, CV_32FC2, CV_32FC3, CV_32FC4,
                                 CV_64F, CV_64FC1, CV_64FC2, CV_64FC3, CV_64FC4};
    
        string enum_strings[] = {"CV_8U",  "CV_8UC1",  "CV_8UC2",  "CV_8UC3",  "CV_8UC4",
                                 "CV_8S",  "CV_8SC1",  "CV_8SC2",  "CV_8SC3",  "CV_8SC4",
                                 "CV_16U", "CV_16UC1", "CV_16UC2", "CV_16UC3", "CV_16UC4",
                                 "CV_16S", "CV_16SC1", "CV_16SC2", "CV_16SC3", "CV_16SC4",
                                 "CV_32S", "CV_32SC1", "CV_32SC2", "CV_32SC3", "CV_32SC4",
                                 "CV_32F"  "CV_32FC1", "CV_32FC2", "CV_32FC3", "CV_32FC4",
                                 "CV_64F", "CV_64FC1", "CV_64FC2", "CV_64FC3", "CV_64FC4"};
    
        for(int i=0; i<numImgTypes; i++)
        {
            if(imgTypeInt == enum_ints[i]) return enum_strings[i];
        }
        return "unknown image type";
    }
    // take number image type number (from cv::Mat.type()), get OpenCV's enum string.
    
    int _tmain(int argc, char *argv[])
    {
    	const int dpi = 600;
    	RNG rng(12345);
    
    	string str = "K:\\Projekte\\C++\\Puzzle Solver\\Ress\\PuzzleTeileScan.jpg";
    	char * buffer = new char[str.length()+1];
    	strcpy(buffer,str.c_str());
    
    	char* imageName = buffer;
    	Mat image;Mat dstImage;
    	image = imread( imageName, 1 );
    	std::cout << getImgType(image.type());
    	if(!image.data )
    	{
    	printf( " No image data \n " );
    	return -1;
    	}
    
    	Mat mask;
    	//Varibles to hold scrollbar values
        int hl=0; int hu=255;
    	int sl=0; int su=62;
    	int vl=93; int vu=255;
    
    	cvtColor(image, image, CV_BGRA2BGR);
    	cvtColor(image, image, CV_BGR2HSV);
        //Window
        cvNamedWindow("Display window",CV_WINDOW_AUTOSIZE);
        //Scrollbars
        //cvCreateTrackbar("hu","Display window",&hu,255,0);
        //cvCreateTrackbar("hl","Display window",&hl,255,0);
        cvCreateTrackbar("su","Display window",&su,255,0);
        cvCreateTrackbar("sl","Display window",&sl,255,0);
        cvCreateTrackbar("vu","Display window",&vu,255,0);
        cvCreateTrackbar("vl","Display window",&vl,255,0);
    
    	blur( image, image, Size(3,3) );
    	while(1)
        {	
    		inRange(image, Scalar(hl, sl, vl), Scalar(hu, su, vu), mask);
    		std::cout << getImgType(mask.type()) << std::endl;
    		std::cout << " " << mask.cols << " " << mask.rows << std::endl;
    		std::cout<< countNonZero(mask) <<std::endl;
    		blur( mask, mask, Size(3,3) );
    		blur( mask, mask, Size(3,3) );
    		//image.copyTo(dstImage, mask);
            //Show the image
    		imshow( "Display window", mask );
    
            //Escape Sequence
            char c=cvWaitKey(33);
            if(c==27)
                break;
        }
    	imwrite( "K:\\Projekte\\C++\\Puzzle Solver\\Ress\\Output.jpg", mask );	
    
    	Mat findImage(mask.clone()); // Wegwerfkopie	
    	vector<vector<Point> > contours;
    	vector<Vec4i> hierarchy;
    	/// Find contours
    	findContours( findImage, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE, Point(0, 0) );
    
    	/// Draw contours
    	Mat drawing = Mat::zeros( mask.size(), CV_8UC3 );
    	double m1; double b1; double m2; double b2;
    	long  double n=30; // gibt an wieviele Punkte zur Berechnung der Regressionsgeraden hinzugezogen werden
    	double sw;
    	Point ps; Point pe;
    	for( int i = 0; i< contours.size(); i++ )
    	{
    		int s = contours[i].size();
    		if ( s < 1000 ) // kein ganzes Puzzleteil
    			continue;
    		std:: cout << contours[i].size() << std::endl;
    		Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
    		drawContours( drawing, contours, i, color, 2, 8, hierarchy, 0, Point() );
    		int count = 0;
    		for ( int j=0; j< contours[i].size();j++){
    			regressionsGerade(j,contours[i],n, m1, b1);
    			regressionsGerade((j+(int) n) % s,contours[i],n, m2, b2);
    			sw = atan(abs(( m1-m2 )/(1+m1*m2)))/3.14159265358979323846f*180.0; // schnittwinkel der beiden regressionsGeraden
    			if (sw < 95 && sw > 65){   // leider sind nicht viele dabei die wirklich fast 90° sind
    				count++;
    				if (count < 10) // zeichne nur jeden 10. ein
    					continue;
    				std::cout << j << " " << sw << std::endl;
    				line(drawing, Point(contours[i][(j+(int) n) % s].x, contours[i][(j+(int) n) % s].y),
    				Point(contours[i][(j+(int) n) % s].x, contours[i][(j+(int) n) % s].y),
    				Scalar(255, 255, 255 ), 7);
    				if (count >= 10){
    					count = 0;
    				}
    			}
    		}
    		}
    
    	/// Show in a window
    	namedWindow( "Contours", CV_WINDOW_AUTOSIZE );
    	imshow( "Contours", drawing );
    	imwrite( "K:\\Projekte\\C++\\Puzzle Solver\\Ress\\Edge.jpg", drawing );	
    
    	while(1)
        {
            //Escape Sequence
            char c=cvWaitKey(33);
            if(c==27)
                break;
    	}
    	//Cleanup
        image.release();
    	mask.release();
        cvDestroyAllWindows();
    	return 0;
    
    }
    
    void regressionsGerade(int s ,const vector<Point>& contour,long double n, double& m, double& b){
    	int sumx = 0;
    	int sumy = 0; 
    	int sumxy = 0;
    	int sumx2 = 0;
    	int d = contour.size();
    	for (int i=s;i<s+n; i++){
    		int z1 = contour[i % d].x;
    		int z2 = contour[i % d].y;
    		sumx += z1;
    		sumy += z2;
    		sumxy += z1*z2;
    		sumx2 += z1*z1;
    	}
    	m = (sumxy-sumx*sumy/n )/(sumx2-sumx*sumx/n);
    	b = sumy/n-m*sumx/n;
    }
    


  • Achso jetzt habe ich das Beispielbild vergessen:

    PuzzleTeileScan.jpg
    http://s14.directupload.net/file/d/3124/qjnndszd_jpg.htm

    Edge.jpg
    http://s1.directupload.net/file/d/3124/2tmhdjb5_jpg.htm



  • Hilft es dir vielleicht schon, die Ausrichtung der Teile grob zu kennen?
    Eingabe: http://daiw.de/share/Forum/cpp/20130104Puzzle/ten_small.jpg
    Ausgabe: http://daiw.de/share/Forum/cpp/20130104Puzzle/output_contours.jpg

    #include "opencv2/opencv.hpp"
    
    int main()
    {
    	using namespace cv;
    	using namespace std;
    
    	// Load image.
    	string imageFileName("ten_small.jpg");
    	Mat rgbImage(imread(imageFileName));
    
    	// No image data?
    	if(!rgbImage.data)
    	{
    		cout << "Unable to load: " << imageFileName << "\n";
    		return -1;
    	}
    
    	// Convert to HSV.
    	Mat hsvImage;
    	cvtColor(rgbImage, hsvImage, CV_BGR2HSV);
    
    	// Threshold and blur.
    	Mat mask;
    	inRange(hsvImage, Scalar(0, 0, 93), Scalar(255, 62, 255), mask);
    	blur(mask, mask, Size(5,5));
    
    	// Find the contours.
    	typedef vector<Point> Contour;
    	vector<Contour> contours;
    	{
    		// cv::findContours destroys the original image, so we need a copy.
    		Mat findImage(mask.clone());
    		findContours(findImage, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);  
    	}
    
    	// Helper function for drawing rotated rectangles.
    	auto drawRotatedRect([](Mat& image, const RotatedRect& rRect, const Scalar& col)
    	{
    		Point2f vertices[4];
    		rRect.points(vertices);
    		for (int i = 0; i < 4; i++)
    			line(image, vertices[i], vertices[(i+1)%4], col);
    	});
    
    	// Draw contours and minAreaRects.
    	Mat drawImage(mask.size(), CV_8UC3, Scalar(0, 0, 0));
    	for_each(begin(contours), end(contours), [&](const Contour& contour)
    	{
    		drawContours(drawImage, vector<Contour>(1, contour), 0, Scalar(255, 0, 0), 2);
    		drawRotatedRect(drawImage, minAreaRect(contour), Scalar(0, 0, 255));
    	});
    
    	// Save results.
    	imwrite("output_mask.jpg", mask);
    	imwrite("output_contours.jpg", drawImage);
    }
    


  • Dank dir, ich denke damit kann man nun recht gut die Ecken des Puzzles bestimmen.
    Gibt es so eine Funktion wie - bestimme den kürzesten Abstand von einem Punkt(Eckpunkt des Quadrates) zu einem Punkt auf der Kontur. Dies ist dann mit hoher Wahrscheinlichkeit ein Eckpunkt.



  • In OpenCV gibt es so eine Funktion nicht fertig, aber die ist ja schnell geschrieben:

    #include "opencv2/opencv.hpp"
    
    typedef std::vector<cv::Point> Contour;
    typedef std::vector<cv::Point> Corners;
    
    // Distance between two points.
    float Dist(const cv::Point& p1, const cv::Point& p2)
    {
    	float dx(p1.x-p2.x);
    	float dy(p1.y-p2.y);
    	return sqrt(dx * dx + dy * dy);
    }
    
    // Find possible corners.
    Corners ContourCorners(const Contour& contour)
    {
    	using namespace cv;
    	using namespace std;
    	Corners result;
    	auto rRect(minAreaRect(contour));
    	Point2f rectCorners[4];
    	rRect.points(rectCorners);
    	for_each(begin(rectCorners), end(rectCorners), [&](const Point& rectCorner)
    	{
    		result.push_back(
    			*min_element(begin(contour), end(contour),
    				[&rectCorner](const Point& lhs, const Point& rhs)
    			{
    				return Dist(lhs, rectCorner) < Dist(rhs, rectCorner);
    			})
    		);
    	});
    	return result;
    }
    
    // Draw rotated rectangle.
    void DrawRotatedRect(cv::Mat& image, const cv::RotatedRect& rRect, const cv::Scalar& col)
    {
    	using namespace cv;
    	using namespace std;
    	Point2f vertices[4];
    	rRect.points(vertices);
    	for (int i = 0; i < 4; i++)
    		line(image, vertices[i], vertices[(i+1)%4], col);
    }
    
    int main()
    {
    	using namespace cv;
    	using namespace std;
    
    	// Load image.
    	string imageFileName("ten_small.jpg");
    	Mat rgbImage(imread(imageFileName));
    
    	// No image data?
    	if(!rgbImage.data)
    	{
    		cout << "Unable to load: " << imageFileName << "\n";
    		return -1;
    	}
    
    	// Convert to HSV.
    	Mat hsvImage;
    	cvtColor(rgbImage, hsvImage, CV_BGR2HSV);
    
    	// Threshold and blur.
    	Mat mask;
    	inRange(hsvImage, Scalar(0, 0, 93), Scalar(255, 62, 255), mask);
    	blur(mask, mask, Size(5,5));
    
    	// Find the contours.
    	vector<Contour> contours;
    	{
    		// cv::findContours destroys the original image, so we need a copy.
    		Mat findImage(mask.clone());
    		findContours(findImage, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);  
    	}
    
    	// Draw contours, minAreaRects and possible corners.
    	Mat drawImage(mask.size(), CV_8UC3, Scalar(0, 0, 0));
    	for_each(begin(contours), end(contours), [&](const Contour& contour)
    	{
    		drawContours(drawImage, vector<Contour>(1, contour), 0, Scalar(255, 0, 0), 2);
    		DrawRotatedRect(drawImage, minAreaRect(contour), Scalar(0, 0, 255));
    
    		// Find Corners.
    		auto corners(ContourCorners(contour));
    		for_each(begin(corners), end(corners), [&](const Point& point)	
    		{
    			circle(drawImage, point, 4, Scalar(0, 255, 0), 2);
    		});
    	});
    
    	// Save result.
    	imwrite("output_corners.jpg", drawImage);
    }
    

    Meistens passt es auch, aber eben doch nicht immer. 😉
    Input: http://daiw.de/share/Forum/cpp/20130105Puzzle/ten_small.jpg
    Output: http://daiw.de/share/Forum/cpp/20130105Puzzle/output_corners.jpg



  • Danke, da kann man bestimmt noch was drehen.
    Ich habe diese Woche leider nicht viel Zeit melde mich dann aber bestimmt nochmal.



  • Hi hatte soeben mal kurz Zeit mir dein Beipiel genauer anzusehen.
    Beim compilieren erhalte ich folgende Fehler, woran kann das denn liegen ?

    ------ Erstellen gestartet: Projekt: Puzzle Solver, Konfiguration: Debug Win32 ------
    Find Contur.cpp
    k:\projekte\c++\puzzle solver\puzzle solver\find contur.cpp(9): warning C4244: 'Initialisierung': Konvertierung von 'const int' in 'float', möglicher Datenverlust
    k:\projekte\c++\puzzle solver\puzzle solver\find contur.cpp(10): warning C4244: 'Initialisierung': Konvertierung von 'const int' in 'float', möglicher Datenverlust
    k:\projekte\c++\puzzle solver\puzzle solver\find contur.cpp(26): error C4430: Fehlender Typspezifizierer - int wird angenommen. Hinweis: "default-int" wird von C++ nicht unterstützt.
    k:\projekte\c++\puzzle solver\puzzle solver\find contur.cpp(26): error C2143: Syntaxfehler: Es fehlt ',' vor '&'
    k:\projekte\c++\puzzle solver\puzzle solver\find contur.cpp(28): error C2065: 'lhs': nichtdeklarierter Bezeichner
    k:\projekte\c++\puzzle solver\puzzle solver\find contur.cpp(28): error C2065: 'rhs': nichtdeklarierter Bezeichner
    k:\projekte\c++\puzzle solver\puzzle solver\find contur.cpp(26): error C3861: "min_element": Bezeichner wurde nicht gefunden.
    k:\projekte\c++\puzzle solver\puzzle solver\find contur.cpp(28): error C2065: 'lhs': nichtdeklarierter Bezeichner
    k:\projekte\c++\puzzle solver\puzzle solver\find contur.cpp(28): error C2065: 'rhs': nichtdeklarierter Bezeichner
    k:\projekte\c++\puzzle solver\puzzle solver\find contur.cpp(83): error C2065: 'vector': nichtdeklarierter Bezeichner
    k:\projekte\c++\puzzle solver\puzzle solver\find contur.cpp(83): error C2275: 'Contour': Ungültige Verwendung dieses Typs als Ausdruck
    k:\projekte\c++\puzzle solver\puzzle solver\find contur.cpp(3): Siehe Deklaration von 'Contour'
    k:\projekte\c++\puzzle solver\puzzle solver\find contur.cpp(83): error C3861: "Scalar": Bezeichner wurde nicht gefunden.
    k:\projekte\c++\puzzle solver\puzzle solver\find contur.cpp(84): error C3861: "Scalar": Bezeichner wurde nicht gefunden.
    k:\projekte\c++\puzzle solver\puzzle solver\find contur.cpp(88): error C4430: Fehlender Typspezifizierer - int wird angenommen. Hinweis: "default-int" wird von C++ nicht unterstützt.
    k:\projekte\c++\puzzle solver\puzzle solver\find contur.cpp(88): error C2143: Syntaxfehler: Es fehlt ',' vor '&'
    k:\projekte\c++\puzzle solver\puzzle solver\find contur.cpp(90): error C2065: 'point': nichtdeklarierter Bezeichner
    k:\projekte\c++\puzzle solver\puzzle solver\find contur.cpp(88): error C3861: "for_each": Bezeichner wurde nicht gefunden.
    k:\projekte\c++\puzzle solver\puzzle solver\find contur.cpp(90): error C3861: "Scalar": Bezeichner wurde nicht gefunden.
    k:\projekte\c++\puzzle solver\puzzle solver\find contur.cpp(90): error C2065: 'point': nichtdeklarierter Bezeichner
    k:\projekte\c++\puzzle solver\puzzle solver\find contur.cpp(90): error C3861: "Scalar": Bezeichner wurde nicht gefunden.
    ========== Erstellen: 0 erfolgreich, Fehler bei 1, 0 aktuell, 0 übersprungen ==========

    Ich benutze Visual Studio 2010 Express, liegt es vlt daran ?


  • Mod

    Tom1994 schrieb:

    Ich benutze Visual Studio 2010 Express, liegt es vlt daran ?

    Ja, VS2010 kann meines Wissens nach noch keine Lambdas. Du musst entweder einen Compiler mit besserer C++11-Unterstützung benutzen oder den Code auf C++98 umschreiben. VS 2010 kann allgemein so einiges aus C++11 nicht, der Code benutzt aber jede Menge. Wahrscheinlich bekommst du noch mehr Probleme, aber ich habe gerade keine Lust, jede Zeile durchzugehen und die benutzten Features mit den von VS 2010 unterstützten Features zu vergleichen.



  • Ne, daran liegts nicht. VS2010 kann Lambdas, allerdings stellt es sich was dämlich mit using namespace davor an. Hier eine Version, die fehlerfrei kompilieren sollte:

    #include "opencv2/opencv.hpp"
    #include <algorithm>
    
    typedef std::vector<cv::Point> Contour;
    typedef std::vector<cv::Point> Corners;
    
    // Distance between two points.
    float Dist(const cv::Point& p1, const cv::Point& p2)
    {
        float dx(static_cast<float>(p1.x-p2.x));
        float dy(static_cast<float>(p1.y-p2.y));
        return sqrt(dx * dx + dy * dy);
    }
    
    // Find possible corners.
    Corners ContourCorners(const Contour& contour)
    {
        using namespace cv;
        using namespace std;
        Corners result;
        auto rRect(minAreaRect(contour));
        Point2f rectCorners[4];
        rRect.points(rectCorners);
        for_each(begin(rectCorners), end(rectCorners), [&](const Point& rectCorner)
        {
            result.push_back(
                *std::min_element(begin(contour), end(contour),
                    [&rectCorner](const cv::Point& lhs, const cv::Point& rhs)
                {
                    return Dist(lhs, rectCorner) < Dist(rhs, rectCorner);
                })
            );
        });
        return result;
    }
    
    // Draw rotated rectangle.
    void DrawRotatedRect(cv::Mat& image, const cv::RotatedRect& rRect, const cv::Scalar& col)
    {
        using namespace cv;
        using namespace std;
        Point2f vertices[4];
        rRect.points(vertices);
        for (int i = 0; i < 4; i++)
            line(image, vertices[i], vertices[(i+1)%4], col);
    }
    
    int main()
    {
        using namespace cv;
        using namespace std;
    
        // Load image.
        string imageFileName("ten_small.jpg");
        Mat rgbImage(imread(imageFileName));
    
        // No image data?
        if(!rgbImage.data)
        {
            cout << "Unable to load: " << imageFileName << "\n";
            return -1;
        }
    
        // Convert to HSV.
        Mat hsvImage;
        cvtColor(rgbImage, hsvImage, CV_BGR2HSV);
    
        // Threshold and blur.
        Mat mask;
        inRange(hsvImage, Scalar(0, 0, 93), Scalar(255, 62, 255), mask);
        blur(mask, mask, Size(5,5));
    
        // Find the contours.
        vector<Contour> contours;
        {
            // cv::findContours destroys the original image, so we need a copy.
            Mat findImage(mask.clone());
            findContours(findImage, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);  
        }
    
        // Draw contours, minAreaRects and possible corners.
        Mat drawImage(mask.size(), CV_8UC3, Scalar(0, 0, 0));
        for_each(begin(contours), end(contours), [&](const Contour& contour)
        {
            drawContours(drawImage, std::vector<Contour>(1, contour), 0, cv::Scalar(255, 0, 0), 2);
            DrawRotatedRect(drawImage, minAreaRect(contour), cv::Scalar(0, 0, 255));
    
            // Find Corners.
            auto corners(ContourCorners(contour));
            std::for_each(begin(corners), end(corners), [&](const cv::Point& point) 
            {
                circle(drawImage, point, 4, cv::Scalar(0, 255, 0), 2);
            });
        });
    
        // Save result.
        imwrite("output_corners.jpg", drawImage);
    }
    


  • Danke dir,

    nun bekomme ich aber folgenden Fehler in der Zeile:

    circle(drawImage, point, 4, cv::Scalar(0, 255, 0), 2);

    "Ungültiger Verweis auf eine lokale Variable eines äußeren Gültigkeitsbereichs in einem Lambda-Text."



  • Mhh, das ist seltsam, denn bei mir compiliert der code fehlerfrei mit VC2010 express. Den Fehler von dir bekomme ich nur vom dusseligen Intellisense angezeigt, aber das ist ja kein Drama: http://i47.tinypic.com/1110d5k.png

    Aber na gut, hier eine Version, bei der auch nix mehr rot unterstrichen wird. Das mit dem innerDrawImage funktioniert, weil cv::Mat reference counted ist und der Bildspeicher nur neu angelegt und kopiert wird wenn man explizit .clone() sagt.

    #include "opencv2/opencv.hpp"
    #include <algorithm>
    
    typedef std::vector<cv::Point> Contour;
    typedef std::vector<cv::Point> Corners;
    
    // Distance between two points.
    float Dist(const cv::Point& p1, const cv::Point& p2)
    {
        float dx(static_cast<float>(p1.x-p2.x));
        float dy(static_cast<float>(p1.y-p2.y));
        return sqrt(dx * dx + dy * dy);
    }
    
    // Find possible corners.
    Corners ContourCorners(const Contour& contour)
    {
        using namespace cv;
        using namespace std;
        Corners result;
        auto rRect(minAreaRect(contour));
        Point2f rectCorners[4];
        rRect.points(rectCorners);
        for_each(begin(rectCorners), end(rectCorners), [&](const Point& rectCorner)
        {
            result.push_back(
                *std::min_element(begin(contour), end(contour),
                    [&rectCorner](const cv::Point& lhs, const cv::Point& rhs)
                {
                    return Dist(lhs, rectCorner) < Dist(rhs, rectCorner);
                })
            );
        });
        return result;
    }
    
    // Draw rotated rectangle.
    void DrawRotatedRect(cv::Mat& image, const cv::RotatedRect& rRect, const cv::Scalar& col)
    {
        using namespace cv;
        using namespace std;
        Point2f vertices[4];
        rRect.points(vertices);
        for (int i = 0; i < 4; i++)
            line(image, vertices[i], vertices[(i+1)%4], col);
    }
    
    int main()
    {
        using namespace cv;
        using namespace std;
    
        // Load image.
        string imageFileName("ten_small.jpg");
        Mat rgbImage(imread(imageFileName));
    
        // No image data?
        if(!rgbImage.data)
        {
            cout << "Unable to load: " << imageFileName << "\n";
            return -1;
        }
    
        // Convert to HSV.
        Mat hsvImage;
        cvtColor(rgbImage, hsvImage, CV_BGR2HSV);
    
        // Threshold and blur.
        Mat mask;
        inRange(hsvImage, Scalar(0, 0, 93), Scalar(255, 62, 255), mask);
        blur(mask, mask, Size(5,5));
    
        // Find the contours.
        vector<Contour> contours;
        {
            // cv::findContours destroys the original image, so we need a copy.
            Mat findImage(mask.clone());
            findContours(findImage, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);  
        }
    
        // Draw contours, minAreaRects and possible corners.
        Mat drawImage(mask.size(), CV_8UC3, Scalar(0, 0, 0));
        for_each(begin(contours), end(contours), [&](const Contour& contour)
        {
            drawContours(drawImage, std::vector<Contour>(1, contour), 0, cv::Scalar(255, 0, 0), 2);
            DrawRotatedRect(drawImage, minAreaRect(contour), cv::Scalar(0, 0, 255));
    
            // Find Corners.
            auto corners(ContourCorners(contour));
            cv::Mat innerDrawImage(drawImage); // (Lol, Billy!)
            std::for_each(begin(corners), end(corners), [&](const cv::Point& point)
            {
                circle(innerDrawImage, point, 4, cv::Scalar(0, 255, 0), 2);
            });
        });
    
        // Save result.
        imwrite("output_corners.jpg", drawImage);
    }
    


  • Danke!


Anmelden zum Antworten