├── README.md ├── Reconstruction-from-Two-Picture ├── README.md └── main.cpp ├── Tools ├── README.md ├── Stereocam_Capture └── Stereocam_Recoder ├── core ├── MatIterator_.cc └── matx21d.cpp ├── feature-based ├── EdgeDetectionUsingGradientMagnitude.cpp ├── FastFeatureDetector.cpp ├── Find_Contours.cpp ├── SIFTxfeatures2d.cpp ├── SumofSquaredDifferences(SSD).cpp ├── TemplateMatching.cpp ├── goodFeaturesToTrack.cpp └── harrisdetector_demo.cpp ├── imageproc ├── HoughEllipse.cpp ├── colordetector_demo.cpp ├── cornersEliminate.cpp ├── demo4HistAnalysis.cpp ├── houghNgon.cpp ├── houghPeak.cpp ├── swt.cpp ├── swt.hpp └── swtExample.cpp ├── include ├── ColorDetector.cpp ├── ColorDetector.h ├── README.md ├── colorExtractor.h ├── colorRectify.cpp ├── harrisdetector.h ├── houghPeak.h └── ransacLines.h └── lazer-light ├── Item-lists.md ├── README.md ├── calibration └── README.md └── main.cpp /README.md: -------------------------------------------------------------------------------- 1 | # a-test-for-triangulation-using-opencv 2 | Stereo Vision for 3D Reconstruction , platform independent , c++ 3 | -------------------------------------------------------------------------------- /Reconstruction-from-Two-Picture/README.md: -------------------------------------------------------------------------------- 1 | Attention! 2 | 3 | This program is not completed 4 | 5 | -------------------------------------------------------------------------------- /Reconstruction-from-Two-Picture/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | int main() 6 | { 7 | 8 | cv::VideoCapture camera1(0); 9 | cv::VideoCapture camera2(1); 10 | 11 | if(!camera1.isOpened()) 12 | return -1; 13 | if(!camera2.isOpened()) 14 | return -1; 15 | 16 | 17 | cv::Mat img1; 18 | cv::Mat img2; 19 | cv::namedWindow("img1",1); 20 | cv::namedWindow("img2",1); 21 | 22 | cv::Mat M1,M2,D1,D2,R,T;// 由标定参数获得,需要赋值 23 | cv::Size img_size,newimg_size; //Size of the image used for stereo calibration.需要赋值 24 | //New image resolution after rectification. 25 | 26 | cv::Rect roi1,roi2; //校正后输出矩形 27 | cv::Mat R1,R2,P1,P2,Q; //输出的旋转、投影、视差映射矩阵 28 | cv::stereoRectify( M1, D1, M2, D2, img_size, R, T, R1, R2, P1, P2, Q, 29 | CALIB_ZERO_DISPARITY, -1, newimg_size, &roi1, &roi2 ); 30 | 31 | 32 | cv::Mat map11, map12, map21, map22; 33 | cv::initUndistortRectifyMap(M1, D1, R1, P1, newimg_size, CV_16SC2, map11, map12); //Computes the undistortion 34 | //and rectification transformation map. 35 | cv::initUndistortRectifyMap(M2, D2, R2, P2, newimg_size, CV_16SC2, map21, map22); 36 | 37 | 38 | 39 | 40 | cv::Mat frame1, frame2; 41 | camera1 >> frame1; 42 | camera2 >> frame2; 43 | cv::cvtColor(frame1, img1, CV_BGR2GRAY); 44 | cv::cvtColor(frame2, img2, CV_BGR2GRAY); 45 | cv::GaussianBlur(img1, img1, cv::Size(7,7), 1.5, 1.5);//gaussian smoothing 46 | cv::GaussianBlur(img2, img2, cv::Size(7,7), 1.5, 1.5); 47 | 48 | 49 | cv::remap(img1, img1, map11, map12, INTER_LINEAR); //双线性插补 50 | cv::remap(img2, img2, map21, map22, INTER_LINEAR); 51 | 52 | 53 | bm->setROI1(roi1); 54 | bm->setROI2(roi2); 55 | bm->setPreFilterCap(31); 56 | bm->setBlockSize(SADWindowSize > 0 ? SADWindowSize : 9); 57 | bm->setMinDisparity(0); 58 | bm->setNumDisparities(numberOfDisparities); 59 | bm->setTextureThreshold(10); 60 | bm->setUniquenessRatio(15); 61 | bm->setSpeckleWindowSize(100); 62 | bm->setSpeckleRange(32); 63 | bm->setDisp12MaxDiff(1); 64 | 65 | cv::Mat disp; 66 | 67 | bm->compute(img1, img2, disp); 68 | 69 | cv::Mat image3D; 70 | 71 | cv::reprojectImageTo3D(disp, image3D, Q, false, -1 ); 72 | 73 | 74 | 75 | if(cv::waitKey(30)>=0) return 0; 76 | 77 | 78 | 79 | 80 | } 81 | -------------------------------------------------------------------------------- /Tools/README.md: -------------------------------------------------------------------------------- 1 | Tools 2 | -------------------------------------------------------------------------------- /Tools/Stereocam_Capture: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is for generating images for calibration in Matlab 3 | * Press any key to capture current two image in the stereo webcam simultaneously 4 | * Press Esc twice to quit this program 5 | * 6 | */ 7 | 8 | 9 | 10 | #include 11 | #include 12 | using namespace std; 13 | using namespace cv; 14 | 15 | int main() 16 | { 17 | cout << "Press any key to capture " << endl 18 | << "Press Esc twice to quit this program" << endl; 19 | VideoCapture capture1(0),capture2(1); 20 | if (!capture1.isOpened()) return -1; 21 | if (!capture2.isOpened()) return -1; 22 | Mat img1,img2; 23 | namedWindow("cam1",1); 24 | namedWindow("cam2",1); 25 | 26 | vector compression_params; 27 | compression_params.push_back(CV_IMWRITE_JPEG_QUALITY); 28 | compression_params.push_back(100); 29 | 30 | static int flag = 1; 31 | 32 | for(;;) 33 | { 34 | capture1 >> img1; 35 | capture2 >> img2; 36 | imshow("cam1",img1); 37 | imshow("cam2",img2); 38 | 39 | if ( waitKey(30) >=0) 40 | { 41 | char c = waitKey(30); 42 | if (c == 27) break; 43 | 44 | string flag_string; 45 | stringstream ss; 46 | if ( flag < 100) 47 | { 48 | ss << flag; 49 | ss >> flag_string; 50 | } 51 | else 52 | { 53 | cout << "Out of Range, Stop" << endl; 54 | break; 55 | 56 | } 57 | const string str1 = "left" + flag_string + ".jpeg"; 58 | const string str2 = "right" + flag_string + ".jpeg"; 59 | imwrite( str1 , img1, compression_params ); 60 | imwrite( str2 , img2, compression_params ); 61 | ++flag; 62 | 63 | } 64 | 65 | } 66 | 67 | 68 | } 69 | -------------------------------------------------------------------------------- /Tools/Stereocam_Recoder: -------------------------------------------------------------------------------- 1 | /* Note: 2 | * The two videos are writed in the folder that contains the executable file , 3 | * in the format of MJPG , BGR. 4 | * Press any key to exit. 5 | */ 6 | 7 | 8 | #include 9 | #include 10 | using namespace std; 11 | using namespace cv; 12 | int main() 13 | { 14 | 15 | VideoCapture camera1(0); 16 | VideoCapture camera2(1); 17 | 18 | if(!camera1.isOpened()) return -1; 19 | if(!camera2.isOpened()) return -1; 20 | 21 | Mat cam1; 22 | Mat cam2; 23 | double fps1 = camera1.get(CAP_PROP_FPS); 24 | double fps2 = camera2.get(CAP_PROP_FPS); 25 | Size size1( (int)camera1.get(CAP_PROP_FRAME_WIDTH), 26 | (int)camera1.get(CAP_PROP_FRAME_HEIGHT)); 27 | 28 | Size size2( (int)camera2.get(CAP_PROP_FRAME_WIDTH), 29 | (int)camera2.get(CAP_PROP_FRAME_HEIGHT)); 30 | VideoWriter writer1; 31 | VideoWriter writer2; 32 | writer1.open("cam_video1.avi",CV_FOURCC('M','J','P','G'),fps1,size1); 33 | writer2.open("cam_video2.avi",CV_FOURCC('M','J','P','G'),fps2,size2); 34 | 35 | 36 | namedWindow("cam1",1); 37 | namedWindow("cam2",WINDOW_AUTOSIZE); 38 | for(;;) 39 | { 40 | 41 | camera1 >> cam1; 42 | if(!cam1.data) break; 43 | camera2 >> cam2; 44 | if(!cam2.data) break; 45 | //if(!cam2.data) break; 46 | cv::imshow("cam1",cam1); 47 | cv::imshow("cam2",cam2); 48 | 49 | 50 | writer1 << cam1; 51 | writer2 << cam2; 52 | 53 | 54 | if(waitKey(33) >= 0) break; 55 | } 56 | 57 | camera1.release(); 58 | camera2.release(); 59 | } 60 | -------------------------------------------------------------------------------- /core/MatIterator_.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using namespace cv; 4 | int main() 5 | { 6 | Mat image = imread("/home/tau/opencv/opencv-3.1.0/samples/data/blox.jpg",0); 7 | // uchar* it = image.ptr; 8 | cv::MatIterator_ it = image.begin(); 9 | cv::MatIterator_ itend = image.end(); 10 | for(; it != itend; it++) 11 | { 12 | std::cout << *it ; 13 | 14 | } 15 | 16 | 17 | } 18 | -------------------------------------------------------------------------------- /core/matx21d.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | int main() 6 | { 7 | cv::Matx21d m1( 1, 8 | 2); 9 | cv::Matx22d m2( 1,1, 10 | 1,1); 11 | cv::Matx21d m3 = m2* m1; 12 | m3 (2,1); // 第二行第一列取值 注:由于过去很久时间,这段代码没有再试验,在其他的程序中 m(i,j),i,j是从0开始的,特此注明 13 | std::cout << m3 < 6 | #include 7 | 8 | int main(int argc, char ** argv) 9 | { 10 | if(argc < 2) 11 | return -1; 12 | cv::Mat img = cv::imread(argv[1],0); 13 | cv::Mat dst1 , dst2, dst3 ,dst; 14 | cv::Matx33f kernelx(-1,0,1, 15 | -2,0,2, 16 | -1,0,1); 17 | cv::Matx33f kernely(-1,-2,-1, 18 | 0, 0, 0, 19 | 1, 2, 1); 20 | // cv::flip(kernelx,kernelx,-1); //翻转 21 | // cv::flip(kernely,kernely,-1); 22 | // cv::sepFilter2D(img,dst,CV_64F,kernelx,kernely);//kernel.type() == DataType
::type && (kernel.rows == 1 || kernel.cols == 1)) in RowFilter 23 | cv::filter2D(img,dst1,CV_32F,kernelx); 24 | cv::filter2D(img,dst2,CV_32F,kernely); 25 | 26 | cv::namedWindow("dst1",cv::WINDOW_AUTOSIZE); 27 | cv::namedWindow("dst2",cv::WINDOW_AUTOSIZE); 28 | cv::imshow("dst1",dst1); 29 | cv::imshow("dst2",dst2); 30 | // dst3 = dst1 + dst2; 31 | // cv::convertScaleAbs( dst1, dst1 ); 32 | // cv::convertScaleAbs( dst2, dst2 ); 33 | std::cout << dst1.depth() < 7 | #include 8 | #include 9 | int main(int argc, char** argv) 10 | { 11 | if(argc < 2) 12 | return -1; 13 | cv::Mat image1 = cv::imread(argv[1]); 14 | cv::cvtColor(image1,image1,CV_BGR2GRAY); 15 | std::vector corners; 16 | cv::goodFeaturesToTrack(image1,corners,500,1,10); 17 | cv::Mat corner(image1.size(), CV_32FC1,cv::Scalar(255)); 18 | for (auto a : corners) 19 | corner.at(a) = 0; 20 | 21 | std::vector keypoints; 22 | cv::Ptr fast = 23 | cv::FastFeatureDetector::create (10, true,cv::FastFeatureDetector::TYPE_9_16 ); 24 | fast -> detect(image1,keypoints); 25 | cv::drawKeypoints(image1,keypoints,image1,cv::Scalar(0,255,0),0); 26 | 27 | 28 | cv::namedWindow("corner",1); 29 | cv::namedWindow("image",1); 30 | cv::imshow("image",image1); 31 | cv::imshow("corner",corner); 32 | cv::waitKey(0); 33 | 34 | } 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /feature-based/Find_Contours.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | //#define minwidth 30 6 | cv::Size2f a; 7 | const decltype(a.width) minwidth = 30; //if you dont know the type of width 8 | #define minheight 30 9 | #define maxwidth 100 10 | #define maxheight 100 11 | 12 | 13 | 14 | double threshold1 = 50; 15 | double threshold2 = 150; 16 | int apertureSize = 3;//aperture size for the Sobel() operator. 17 | int point_num_min = 100; //least point in a contour 18 | 19 | int main(int argc, char** argv) 20 | { 21 | if(argc < 2) 22 | return -1; 23 | cv::Mat image1 = cv::imread(argv[1]); 24 | cv::namedWindow("canny_edge1",1); 25 | cv::cvtColor(image1, image1, CV_BGR2GRAY); 26 | cv::GaussianBlur(image1, image1, cv::Size(7,7), 1.5, 1.5); 27 | cv::Canny(image1,image1,threshold1,threshold2,apertureSize, false); 28 | cv::imshow("canny_edge1",image1); 29 | std::vector > contours1; 30 | std::vector contours01; 31 | 32 | cv::findContours( image1, contours1 ,CV_RETR_LIST,CV_CHAIN_APPROX_NONE,cvPoint(0,0)); 33 | 34 | cv::Mat ellipse_contour ; 35 | 36 | cv::Mat allcontours(image1.size(),CV_8U,cv::Scalar(255)); 37 | ellipse_contour.create(image1.size(),image1.type()); //two types of initiation 38 | 39 | contours01.reserve(contours1.size()); 40 | 41 | for( std::size_t k = 0; k < contours1.size(); k++ ) 42 | { 43 | 44 | int count = contours1[k].size(); // This is point's number in the contour 45 | if( count > point_num_min ) // the least points to form a contour 46 | contours01.push_back(cv::fitEllipse( contours1[k]) ); 47 | } 48 | 49 | 50 | cv::drawContours(allcontours, contours1,-1, CV_RGB(0,0,0), 1, cv::LINE_8,cv::noArray(),contours1.size(),cvPoint(0,0)); 51 | 52 | 53 | cv::Size2f min; 54 | cv::Size2f max; 55 | min.width = minwidth; 56 | min.height = minheight; 57 | max.width = maxwidth ; 58 | max.height = maxheight; 59 | 60 | for( auto a : contours01) 61 | { 62 | auto ellipse_size = a.size; 63 | if( 64 | ellipse_size.width < max.width && ellipse_size.height < max.height 65 | && ellipse_size.width > min.width && ellipse_size.height > min.height 66 | ) 67 | { 68 | cv::ellipse(ellipse_contour,a,CV_RGB(255,255,255)); 69 | std::cout << "size = " << a.size << " , " 70 | << "center =" << a.center << std::endl; 71 | } 72 | 73 | } 74 | cv::namedWindow("drawcontour",1); 75 | cv::namedWindow("ellipsecontour",1); 76 | cv::imshow("drawcontour",allcontours); 77 | cv::imshow("ellipsecontour",ellipse_contour); 78 | cv::waitKey(0); 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /feature-based/SIFTxfeatures2d.cpp: -------------------------------------------------------------------------------- 1 | // opencv 3.1.0 , xfeatures2d example code . 2 | // Add the include path and link the xfeatures2d module first 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | int main(int argc, char** argv) 9 | { 10 | if(argc < 2) 11 | return -1; 12 | cv::Mat image1 = cv::imread(argv[1]); 13 | cv::cvtColor(image1,image1,CV_BGR2GRAY); 14 | std::vector corners; 15 | cv::goodFeaturesToTrack(image1,corners,500,1,10); 16 | cv::Mat corner(image1.size(), CV_32FC1,cv::Scalar(255)); 17 | for (auto a : corners) 18 | corner.at(a) = 0; 19 | 20 | std::vector keypoints; 21 | cv::Ptr fast = 22 | cv::FastFeatureDetector::create (10, true,cv::FastFeatureDetector::TYPE_9_16 ); 23 | fast -> detect(image1,keypoints); 24 | cv::drawKeypoints(image1,keypoints,image1,cv::Scalar(0,255,0),0); 25 | 26 | std::vector skeypoints; 27 | cv::Ptr sift = 28 | cv::xfeatures2d::SIFT::create(10); 29 | sift -> detect(image1,skeypoints); 30 | cv::Mat image2; 31 | cv::drawKeypoints(image1,skeypoints,image2,cv::Scalar(0,255,0),0); 32 | cv::imshow("sift",image2); 33 | 34 | 35 | 36 | cv::namedWindow("corner",1); 37 | cv::namedWindow("image",1); 38 | cv::imshow("image",image1); 39 | cv::imshow("corner",corner); 40 | cv::waitKey(0); 41 | 42 | 43 | 44 | 45 | } 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /feature-based/SumofSquaredDifferences(SSD).cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * opencv computer vision programming cook book 2nd edition 3 | * chapter 9 example code 4 | * bug fixed by TauBai 5 | * tested on ubuntu x64, opencv 3.1.0 6 | */ 7 | 8 | #include 9 | #include 10 | int main(int argc, char ** argv) 11 | { 12 | if(argc < 3) 13 | return -1; 14 | cv::Mat image1 = cv::imread(argv[1],0); 15 | cv::Mat image2 = cv::imread(argv[2],0); 16 | if(image1.empty()) 17 | return -1; 18 | if(image2.empty()) 19 | return -1; 20 | std::vector keypoints1; 21 | std::vector keypoints2; 22 | cv::FAST(image1,keypoints1,80); 23 | cv::FAST(image2,keypoints2,80); 24 | const int nsize(12); 25 | std::vector matches; 26 | for (int i=0; i < keypoints1.size(); i++) 27 | { 28 | cv::Rect neighborhood(0, 0, nsize, nsize); 29 | cv::Mat patch1; 30 | neighborhood.x = keypoints1[i].pt.x-nsize/2; 31 | neighborhood.y = keypoints1[i].pt.y-nsize/2; 32 | 33 | if (neighborhood.x<0 || 34 | neighborhood.y<0 || 35 | neighborhood.x+nsize >= image1.cols || 36 | neighborhood.y+nsize >= image1.rows) 37 | continue; 38 | patch1 = image1(neighborhood); 39 | cv::DMatch bestMatch; 40 | for (int j=0; j < keypoints2.size(); j++) 41 | { 42 | cv::Rect neighborhood(0, 0, nsize, nsize); 43 | cv::Mat patch2; 44 | cv::Mat result; 45 | 46 | neighborhood.x = keypoints2[j].pt.x-nsize/2; 47 | neighborhood.y = keypoints2[j].pt.y-nsize/2; 48 | 49 | if (neighborhood.x<0 || neighborhood.y<0 || 50 | neighborhood.x + nsize >= image2.cols || 51 | neighborhood.y + nsize >= image2.rows) 52 | continue; 53 | patch2 = image2(neighborhood); 54 | cv::matchTemplate(patch1,patch2,result,CV_TM_SQDIFF_NORMED); 55 | if (result.at(0,0) < bestMatch.distance) 56 | { 57 | bestMatch.distance = result.at(0,0); 58 | bestMatch.queryIdx = i; 59 | bestMatch.trainIdx = j; 60 | } 61 | } 62 | matches.push_back(bestMatch); 63 | } 64 | std::nth_element(matches.begin(),matches.begin()+25,matches.end()); 65 | cv::Mat matchImage; 66 | cv::drawMatches(image1,keypoints1, image2,keypoints2, matches,matchImage, 67 | cv::Scalar(255),cv::Scalar::all(-1),cv::Mat(),0); 68 | cv::namedWindow("matchresult",cv::WINDOW_NORMAL); 69 | cv::imshow("matchresult",matchImage); 70 | 71 | cv::waitKey(0); 72 | } 73 | -------------------------------------------------------------------------------- /feature-based/TemplateMatching.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int method = CV_TM_SQDIFF;//Parameter specifying the comparison method 5 | 6 | int main( int argc , char** argv ) 7 | { 8 | if(argc < 3) 9 | return -1; 10 | cv::Mat image, templ, result; 11 | image = cv::imread( argv[1]); 12 | templ = cv::imread( argv[2]); 13 | if(image.empty()) 14 | return -1; 15 | if(templ.empty()) 16 | return -1; 17 | cv::namedWindow( "source", 1); 18 | cv::namedWindow( "result", 1 ); 19 | result.create( image.cols - templ.cols + 1, image.rows - templ.rows + 1, CV_32FC1 ); 20 | 21 | cv::matchTemplate( image, templ, result, method ); 22 | cv::normalize( result, result, 0, 1, cv::NORM_MINMAX, -1, cv::Mat() ); 23 | 24 | double minVal; double maxVal; 25 | cv::Point minLoc, maxLoc; 26 | cv::minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, cv::Mat() ); 27 | 28 | 29 | cv::Mat display; 30 | display = image.clone(); 31 | cv::Point pt2( minLoc.x + templ.cols , minLoc.y + templ.rows ); 32 | 33 | cv::rectangle( display, minLoc, pt2, CV_RGB(0,255,0) ); 34 | cv::rectangle( result, minLoc, pt2, CV_RGB(0,255,0) ); 35 | 36 | cv::imshow( "source", display ); 37 | cv::imshow( "result", result ); 38 | 39 | 40 | cv::waitKey(0); 41 | return 0; 42 | } 43 | 44 | -------------------------------------------------------------------------------- /feature-based/goodFeaturesToTrack.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | int main(int argc, char** argv) 5 | { 6 | if(argc < 2) 7 | return -1; 8 | cv::Mat image1 = cv::imread(argv[1]); 9 | cv::cvtColor(image1,image1,CV_BGR2GRAY); 10 | std::vector corners; 11 | cv::goodFeaturesToTrack(image1,corners,500,0.01,10); 12 | cv::Mat corner(image1.size(), CV_32FC1,cv::Scalar(255)); 13 | for (auto a : corners) 14 | corner.at(a) = 0; 15 | cv::namedWindow("corner",1); 16 | cv::namedWindow("image",1); 17 | cv::imshow("image",image1); 18 | cv::imshow("corner",corner); 19 | cv::waitKey(0); 20 | 21 | 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /feature-based/harrisdetector_demo.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * opencv computer vision application programming cookbook 2nd edition 3 | * demo for the example class file harrisdetector 4 | */ 5 | 6 | #include 7 | #include 8 | #include "harrisdetector.h" 9 | 10 | int main(int argc, char **argv) 11 | { 12 | if (argc < 2) 13 | return -1; 14 | cv::Mat image = cv::imread(argv[1]); 15 | cv::cvtColor(image,image,CV_RGB2GRAY); 16 | 17 | HarrisDetector harris; 18 | // Compute Harris values 19 | harris.detect(image); 20 | // Detect Harris corners 21 | std::vector pts; 22 | harris.getCorners(pts,0.02); 23 | // Draw Harris corners 24 | harris.drawOnImage(image,pts); 25 | cv::namedWindow("harriscorner",1); 26 | cv::imshow("harriscorner", image); 27 | 28 | 29 | cv::waitKey(0); 30 | return 0; 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /imageproc/HoughEllipse.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 程序没有完成, 只是用霍夫变换刚刚求出了椭圆中心, 3 | * 也就是http://blog.csdn.net/traumland/article/details/51089786 提到的简化方法 4 | * 至于后面的三个参数简化到两个参数, 我还没想好 B, C 的取值范围, 应该是浮点数, 还涉及到参数空间中每个格子代表多少的问题 5 | * 过程和这里面的求中心类似 6 | * ---------------2016.4.11 20:00 7 | * 会接着更新(to be continued.. _(:з」 ∠)_ ) 8 | * ------------------------- 9 | * 一个多月前写的, 现在看来代码太渣了, 都懒得改了, minna斟酌去看吧.. 10 | * ----------------2016. 5.17 21:00 11 | */ 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | //#define minwidth 30 19 | cv::Size2f a; 20 | const decltype(a.width) minwidth = 30; //if you dont know the type of width 21 | #define minheight 30 22 | #define maxwidth 100 23 | #define maxheight 100 24 | 25 | 26 | unsigned int iteration_times = 20; // Max times for loops. 27 | std::vector trashnum_box; //储存随机生成的数, 避免后面重复 28 | 29 | 30 | 31 | double threshold1 = 50; 32 | double threshold2 = 150; 33 | int apertureSize = 3;//aperture size for the Sobel() operator. 34 | int point_num_min = 100; //least point in a contour 35 | 36 | 37 | cv::Mat dx, dy; 38 | 39 | int main(int argc, char** argv) 40 | { 41 | if(argc < 2) 42 | return -1; 43 | cv::Mat image1 = cv::imread(argv[1]); 44 | 45 | cv::namedWindow("canny_edge1",1); 46 | cv::cvtColor(image1, image1, CV_BGR2GRAY); 47 | cv::GaussianBlur(image1, image1, cv::Size(7,7), 1.5, 1.5); 48 | 49 | 50 | cv::Sobel(image1, dx, CV_16S, 1, 0, apertureSize, 1, 0, cv::BORDER_REPLICATE); // compute dx, dy 51 | cv::Sobel(image1, dy, CV_16S, 0, 1, apertureSize, 1, 0, cv::BORDER_REPLICATE); 52 | cv::Canny(image1,image1,threshold1,threshold2,apertureSize, false); 53 | cv::imshow("canny_edge1",image1); 54 | std::vector > contours1; 55 | std::vector contours01; 56 | 57 | cv::findContours( image1, contours1 ,CV_RETR_LIST,CV_CHAIN_APPROX_NONE,cvPoint(0,0)); 58 | 59 | cv::Mat ellipse_contour ; 60 | 61 | cv::Mat allcontours(image1.size(),CV_8U,cv::Scalar(255)); 62 | ellipse_contour.create(image1.size(),image1.type()); //two types of initiation 63 | 64 | contours01.reserve(contours1.size()); 65 | 66 | 67 | void houghellipse( std::vector contour,cv::Mat image); 68 | 69 | for( std::size_t k = 0; k < contours1.size(); k++ ) 70 | { 71 | 72 | int count = contours1[k].size(); // This is point's number in the contour 73 | if( count > point_num_min ) // the least points to form a contour 74 | houghellipse(contours1[k],image1); 75 | 76 | 77 | } 78 | 79 | 80 | cv::drawContours(allcontours, contours1,-1, CV_RGB(0,0,0), 1, cv::LINE_8,cv::noArray(),contours1.size(),cvPoint(0,0)); 81 | 82 | 83 | cv::Size2f min; 84 | cv::Size2f max; 85 | min.width = minwidth; 86 | min.height = minheight; 87 | max.width = maxwidth ; 88 | max.height = maxheight; 89 | 90 | for( auto a : contours01) 91 | { 92 | auto ellipse_size = a.size; 93 | if( 94 | ellipse_size.width < max.width && ellipse_size.height < max.height 95 | && ellipse_size.width > min.width && ellipse_size.height > min.height 96 | ) 97 | { 98 | cv::ellipse(ellipse_contour,a,CV_RGB(255,255,255)); 99 | std::cout << "size = " << a.size << " , " 100 | << "center =" << a.center << std::endl; 101 | } 102 | 103 | } 104 | cv::namedWindow("drawcontour",1); 105 | cv::namedWindow("ellipsecontour",1); 106 | cv::imshow("drawcontour",allcontours); 107 | cv::imshow("ellipsecontour",ellipse_contour); 108 | cv::waitKey(0); 109 | return 0; 110 | } 111 | 112 | void houghellipse(std::vector contour, cv::Mat image) 113 | { 114 | cv::Mat accumulator(image.size(),CV_8UC1,cv::Scalar(0)); 115 | cv::Point ellipsecenter; 116 | 117 | size_t k = contour.size(); 118 | if (iteration_times > contour.size()) 119 | iteration_times = contour.size(); 120 | bool compare_trashnum(size_t t); 121 | std::vector().swap(trashnum_box); // 删除vector内的所有元素, 不删除后面越界会陷入循环 122 | for (unsigned int i = 0; i< iteration_times ; ++i) //随机抽iteration_times个点 123 | { 124 | 125 | 126 | size_t t1 , t2 , t3; //随机生成三个点 127 | srand((unsigned) time(NULL)); 128 | do { 129 | t1 = rand() % k; 130 | } 131 | while(compare_trashnum(t1)); 132 | trashnum_box.push_back(t1); //扔掉 133 | srand((unsigned) time(NULL)); 134 | do { 135 | t2 = rand() % k; 136 | } 137 | while(compare_trashnum(t2)); 138 | trashnum_box.push_back(t2); 139 | srand((unsigned) time(NULL)); 140 | do { 141 | t3 = rand() % k; 142 | } 143 | while(compare_trashnum(t3)); 144 | trashnum_box.push_back(t3); 145 | 146 | double k1,k2,k3; 147 | 148 | cv::Point2d p1,p2; 149 | cv::Point2d pp1 = (contour[t1]+contour[t2])/2, 150 | pp2 = (contour[t1]+contour[t3])/2; 151 | 152 | 153 | // if (!dx.at(contour[t1])) // ignore the assumption that dx equals to zero 154 | k1 = (double)dy.at(contour[t1]) / (double)dx.at(contour[t1]); 155 | // if (!dx.at(contour[t2])) 156 | k2 = (double)dy.at(contour[t2]) / (double)dx.at(contour[t2]); 157 | // if (!dx.at(contour[t3])) 158 | k3 = (double)dy.at(contour[t3]) / (double)dx.at(contour[t3]); 159 | cv::Matx21d matp1, matp2, 160 | matcp1(contour[t1].y - k1*contour[t1].x, 161 | contour[t2].y - k2*contour[t2].x), 162 | matcp2(contour[t1].y - k1*contour[t1].x, 163 | contour[t3].y - k2*contour[t3].x); 164 | cv::Matx22d matkp1(-k1, 1, 165 | -k2, 1 ), 166 | matkp2(-k1, 1 167 | -k3, 1); 168 | cv::solve(matkp1,matcp1,matp1); 169 | cv::solve(matkp2,matcp2,matp2); 170 | p1.x =matp1(0,0); 171 | p1.y =matp2(0,1); 172 | p2.x =matp1(0,0); 173 | p2.y =matp2(0,1); 174 | 175 | double kp1,kp2; 176 | kp1 = (double)(p1.y-pp1.y)/(p1.x-pp1.x); 177 | kp2 = (double)(p2.y-pp2.y)/(p2.x-pp2.x); 178 | 179 | cv::Matx21d mcenter; 180 | cv::Matx22d kpp(-kp1,1, 181 | -kp2,1); 182 | cv::Matx21d kpc(p1.y-kp1*p1.x, 183 | p2.y-kp2*p2.x); 184 | cv::solve(kpp,kpc,mcenter); 185 | cv::Point center; 186 | center.x = cvRound(mcenter(0,0)); 187 | center.y = cvRound(mcenter(0,1)); 188 | if(center.x < image.cols && 189 | center.x >0 && 190 | center.y < image.rows && 191 | center.y >0) //防止中心坐标出界 192 | accumulator.at(center) += 1; 193 | 194 | } 195 | cv::MatIterator_ it = accumulator.begin(); 196 | cv::MatIterator_ itend = accumulator.end(); 197 | uchar peak = 0; 198 | for(;it != itend; ++it) 199 | { 200 | if( *it > peak) 201 | peak = *it; 202 | } 203 | it = accumulator.begin(); //reset iterator it 204 | // int count = 0; //count how many pixels reach the peak. but there is no need since the vector has it's size 205 | std::vector Points; 206 | for(int i = 0;it != itend; ++it, ++i) // 这里能不能简化?避免 使用这样的循环? 207 | { 208 | cv::Point pt; 209 | if( *it == peak) 210 | { 211 | pt.x = i % accumulator.cols; 212 | pt.y = i / accumulator.cols; 213 | Points.push_back(pt); 214 | } 215 | } 216 | cv::Point pts(0,0); 217 | for(auto a : Points) 218 | pts += a; 219 | ellipsecenter.x = pts.x/Points.size(); //get the center; 220 | ellipsecenter.y = pts.y/Points.size(); 221 | //下一步是求剩下的3个参数 222 | std::cout << ellipsecenter << std::endl; //先测试一下 223 | 224 | 225 | } 226 | 227 | 228 | bool compare_trashnum(size_t t) 229 | { 230 | for (size_t i = 0 ; i < trashnum_box.size(); ++i) 231 | { 232 | if ( t == trashnum_box[i]) 233 | return true; 234 | } 235 | return false; 236 | } 237 | -------------------------------------------------------------------------------- /imageproc/colordetector_demo.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * opencv computer vision programming cookbook 2nd edition 3 | * example code 4 | * demo for ColorDetector 5 | */ 6 | 7 | 8 | #include "ColorDetector.h" 9 | 10 | int main(int argc, char** argv) 11 | { 12 | if(argc < 2) 13 | return -1; 14 | ColorDetector cdetect; 15 | ColorDetector ccdetect(80,(23,23,23)); //for test 16 | cv::Mat image = cv::imread(argv[1]); 17 | if(image.empty()) 18 | return -1; 19 | cdetect.setTargetColor(230,190,130); 20 | cv::namedWindow("result",cv::WINDOW_AUTOSIZE); 21 | cv::waitKey(0); 22 | return 0; 23 | 24 | } 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /imageproc/cornersEliminate.cpp: -------------------------------------------------------------------------------- 1 | // use cornerHarris fun to detect corners, then eliminate them 2 | // completely nosense work, just for recording 3 | 4 | cv::Mat cornerContainer; 5 | cv::cornerHarris(roi_enlarged,cornerContainer,7,7,0.05); 6 | //the dst of cornerHarris is CV_32FC1, so the pixel value type is float 7 | cv::threshold(cornerContainer,cornerContainer,0.001, 255,cv::THRESH_BINARY); 8 | if(debugflag) 9 | cv::imshow("corner",cornerContainer); 10 | /* 11 | //// Alternative Method for Mat iteration 12 | for(int i=0; i(i,j) != 0) 15 | roi_enlarged.at(i,j) = 0; 16 | */ 17 | // set corner section to zero 18 | cv::MatIterator_ itHarris; 19 | cv::MatIterator_ itsrc; 20 | itHarris = cornerContainer.begin(); 21 | for(;itHarris != cornerContainer.end(); ++itHarris) 22 | if(*itHarris != 0){ 23 | itsrc = itHarris - cornerContainer.begin() + roi_enlarged.begin(); 24 | *itsrc = 0; 25 | } 26 | if(debugflag) 27 | cv::imshow("After corner eliminate",roi_enlarged); 28 | -------------------------------------------------------------------------------- /imageproc/demo4HistAnalysis.cpp: -------------------------------------------------------------------------------- 1 | #include "opencv2/opencv.hpp" 2 | #include 3 | 4 | using namespace std; 5 | using namespace cv; 6 | 7 | int histSize = 255; 8 | 9 | float range[] = { 0, 256 } ; 10 | const float* histRange = { range }; 11 | 12 | 13 | int thresh = 0; 14 | const double thresh_maxval = 255; 15 | const int thresh_type = CV_THRESH_BINARY; 16 | int hist_w = 512; 17 | int hist_h = 400; 18 | int bin_w = cvRound( (double) hist_w/histSize ); 19 | 20 | Mat histImage( hist_h, hist_w, CV_8UC3, Scalar( 0,0,0) ); 21 | Mat src; 22 | Mat hist; 23 | Mat dst; 24 | void changeThreshAndShowResult(int,void*){ 25 | histImage = 0; 26 | for( int i = 1; i < histSize; i++ ) 27 | line( histImage, Point( bin_w*(i-1), hist_h - cvRound(hist.at(i-1)) ) , 28 | Point( bin_w*(i), hist_h - cvRound(hist.at(i)) ), 29 | Scalar( 255, 0, 0), 2, 8, 0 ); 30 | 31 | line(histImage,Point(thresh*bin_w,0),Point(thresh*bin_w,hist_h),Scalar(255,255,255)); 32 | cv::threshold(src,dst,thresh,thresh_maxval,thresh_type); 33 | imshow("threshold",dst); 34 | imshow("calcHist Demo", histImage ); 35 | } 36 | 37 | 38 | 39 | int main(int argc, char** argv) 40 | { 41 | 42 | String imageName( "/home/tau/Pictures/Screenshot from 2016-10-23 07:57:38.png" ); 43 | 44 | if (argc > 1) 45 | imageName = argv[1]; 46 | 47 | 48 | src = imread(imageName,IMREAD_GRAYSCALE); 49 | 50 | if(src.empty()) return -1; 51 | 52 | calcHist( &src, 1, 0, Mat(), hist, 1, &histSize, &histRange, true, false ); 53 | imshow("hist",hist); 54 | 55 | normalize(hist, hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() ); 56 | 57 | for( int i = 1; i < histSize; i++ ) 58 | line( histImage, Point( bin_w*(i-1), hist_h - cvRound(hist.at(i-1)) ) , 59 | Point( bin_w*(i), hist_h - cvRound(hist.at(i)) ), 60 | Scalar( 255, 0, 0), 2, 8, 0 ); 61 | line(histImage,Point(thresh,0),Point(thresh,hist_h),Scalar(255,255,255)); 62 | threshold(src,dst,thresh,thresh_maxval,thresh_type); 63 | 64 | namedWindow("calcHist Demo", WINDOW_AUTOSIZE ); 65 | cv::createTrackbar("thresh:","calcHist Demo",&thresh,256,changeThreshAndShowResult,NULL); 66 | imshow("calcHist Demo", histImage ); 67 | imshow("threshold",dst); 68 | imshow("src",src); 69 | waitKey(0); 70 | 71 | return 0; 72 | 73 | } 74 | -------------------------------------------------------------------------------- /imageproc/houghNgon.cpp: -------------------------------------------------------------------------------- 1 | // demo for the use of houghPeak to detect n-gon in the point array 2 | // given the cv::Mat src,cv::Rect ROI ,bool debugflag for debuging 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "houghPeak.h" 9 | struct LinePolar 10 | { 11 | float rho; 12 | float angle; 13 | }; 14 | 15 | bool debugflag = 1; 16 | cv::Mat src; 17 | cv::Rect rect; 18 | // first , to enlarge the background then put the ROI on the center 19 | int main() 20 | { 21 | cv::Mat regionsOfinterest(src,rect); 22 | cv::Mat img; 23 | regionsOfinterest.copyTo(img); 24 | cv::Mat roi_enlarged(rect.height*2, rect.width *2,CV_8UC1,cv::Scalar(0)); 25 | cv::Mat roi_enlarged_temp(roi_enlarged,cv::Rect(rect.width - rect.width/2, 26 | rect.height - rect.height/2, 27 | rect.width,rect.height)); 28 | 29 | if(img.channels() == 3) 30 | cv::cvtColor(img,img,CV_RGB2GRAY); 31 | if(img.channels() != 1) 32 | std::cout << "something wrong with the channel" << std::endl; 33 | if(debugflag) 34 | cv::imshow("boundingrect",img);\ 35 | cv::Canny(img,img,40,120); 36 | if(debugflag) 37 | cv::imshow("canny",img); 38 | img.copyTo(roi_enlarged_temp); 39 | if(debugflag) 40 | cv::imshow("canny,roi enlarged",roi_enlarged); 41 | 42 | // morpholo then adaptiveThreshold to extract the contour 43 | cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(15, 5) ); 44 | cv::Mat img_close; 45 | cv::Mat img_morpholo; 46 | cv::morphologyEx(roi_enlarged, img_close, CV_MOP_CLOSE, element); 47 | cv::morphologyEx(img_close, roi_enlarged, CV_MOP_OPEN, element); 48 | if(debugflag) 49 | cv::imshow("canny->morpholo",roi_enlarged); 50 | 51 | cv::adaptiveThreshold(roi_enlarged,roi_enlarged,255,cv::ADAPTIVE_THRESH_GAUSSIAN_C, 52 | cv::THRESH_BINARY,5,0); 53 | if(debugflag) 54 | cv::imshow("outerline",roi_enlarged); 55 | 56 | 57 | // sometimes the copyTo fun may change the type of the pixels 58 | cv::Mat dst(roi_enlarged.size(),CV_8UC3,cv::Scalar(0,0,0)); 59 | if(roi_enlarged.channels() == 3) 60 | cv::cvtColor(roi_enlarged,roi_enlarged,CV_RGB2GRAY); 61 | if(roi_enlarged.channels() != 1) 62 | std::cout << "channels of roi_enlarged error" << std::endl; 63 | 64 | 65 | 66 | //extract all the edge points to the edgepoints container 67 | cv::MatIterator_ it, itbeg, itend; 68 | itbeg = roi_enlarged.begin(); 69 | itend = roi_enlarged.end(); 70 | std::vector edgepoints; 71 | for( it = itbeg;it != itend; ++it) 72 | if( *it != 0) 73 | edgepoints.push_back(cv::Point( (it - itbeg)%roi_enlarged.step, 74 | (it - itbeg)/roi_enlarged.step)); 75 | std::vector> linespoint; 76 | std::vector lines; 77 | std::vector vertexes; 78 | 79 | // 80 | double distance = 3; 81 | unsigned int ngon =4; // n - gon to be detected 82 | LinePolar linepolar; 83 | for(int i = 0;i< ngon;++i){ 84 | HoughLinesPeak( linepolar,edgepoints,roi_enlarged.size(), 1, 0.001, 0., CV_PI ); 85 | lines.push_back(linepolar); 86 | float rho = lines[i].rho; 87 | float theta = lines[i].angle; 88 | double a = cos(theta), b = sin(theta); 89 | double x0 = a*rho, y0 = b*rho; 90 | cv::Point imax(cvRound(x0 + 1000*(-b)), cvRound(y0 + 1000*(a))); // get two points for the line 91 | cv::Point jmax(cvRound(x0 - 1000*(-b)), cvRound(y0 - 1000*(a))); 92 | cv::line( dst, imax, jmax, cv::Scalar(0,0,255), 1, 8 ); 93 | //all the points within the distance are to be eliminated 94 | auto iter = edgepoints.begin(); 95 | while(iter != edgepoints.end()){ 96 | double dis = fabs((jmax.x - imax.x)*((*iter).y - imax.y) - 97 | (jmax.y - imax.y)*((*iter).x - imax.x)) 98 | / sqrt((jmax.x - imax.x)*(jmax.x - imax.x) 99 | + (jmax.y - imax.y)*(jmax.y - imax.y)); 100 | if(dis < distance) 101 | iter = edgepoints.erase(iter); 102 | else ++iter; 103 | } 104 | } 105 | 106 | cv::imshow("houghline",dst); 107 | 108 | for(size_t i = 0;i < lines.size(); ++i){ 109 | for(size_t j = i+1; j < lines.size();++j){ 110 | cv::Matx22f A(std::cos(lines[i].angle), std::sin(lines[i].angle), 111 | std::cos(lines[j].angle), std::sin(lines[j].angle)); 112 | cv::Matx21f b(lines[i].rho,lines[j].rho),x; 113 | if( std::fabs(lines[i].angle- lines[j].angle) > CV_PI/4){ 114 | cv::solve(A,b,x,cv::DECOMP_LU); //方程组Ax = b 求x 115 | cv::Point vertex; 116 | vertex.x = cvRound(x(0,0)); 117 | vertex.y = cvRound(x(0,1)); 118 | vertexes.push_back(vertex); 119 | } 120 | } 121 | } 122 | // sort vertex in the order of up-left -> up-right -> down-right -> down-left 123 | // first to find one point in the quadrangle, then to determine where all the relative positions of vertexes are 124 | cv::Point central = cv::Point(0,0); 125 | for(auto a : vertexes){ 126 | central.x += a.x; 127 | central.y += a.y; 128 | } 129 | central = cv::Point(central.x /4.0 , central.y/4.0); 130 | std::vector vertexes_sorted = vertexes; 131 | 132 | for(auto a :vertexes){ 133 | if (a.x < central.x && a.y < central.y) 134 | vertexes_sorted[0] = a; 135 | if (a.x > central.x && a.y < central.y) 136 | vertexes_sorted[1] = a; 137 | if (a.x > central.x && a.y > central.y) 138 | vertexes_sorted[2] = a; 139 | if (a.x < central.x && a.y > central.y) 140 | vertexes_sorted[3] = a; 141 | } 142 | for(auto a :vertexes_sorted) std::cout << a < 7 | #include 8 | #include 9 | #include "houghPeak.h" 10 | 11 | struct LinePolar 12 | { 13 | float rho; 14 | float angle; 15 | }; 16 | 17 | void HoughLinesPeak( LinePolar& linepolar, 18 | std::vector linepoint,cv::Size size, 19 | float rho, float theta, 20 | double min_theta, double max_theta ) 21 | { 22 | float irho = 1 / rho; 23 | 24 | int width = size.width; 25 | int height = size.height; 26 | 27 | 28 | if (max_theta < min_theta ) { 29 | CV_Error( CV_StsBadArg, "max_theta must be greater than min_theta" ); 30 | } 31 | int numangle = cvRound((max_theta - min_theta) / theta); 32 | int numrho = cvRound(((width + height) * 2 + 1) / rho); 33 | 34 | cv::AutoBuffer _accum((numangle+2) * (numrho+2)); 35 | cv::AutoBuffer _tabSin(numangle); 36 | cv::AutoBuffer _tabCos(numangle); 37 | int *accum = _accum; 38 | float *tabSin = _tabSin, *tabCos = _tabCos; 39 | 40 | 41 | memset( accum, 0, sizeof(accum[0]) * (numangle+2) * (numrho+2) ); 42 | 43 | 44 | float ang = static_cast(min_theta); 45 | for(int n = 0; n < numangle; ang += theta, n++ ) 46 | { 47 | tabSin[n] = (float)(sin((double)ang) * irho); 48 | tabCos[n] = (float)(cos((double)ang) * irho); 49 | } 50 | 51 | 52 | // stage 1. fill accumulator 53 | for(auto i = linepoint.begin(); i != linepoint.end(); ++i){ 54 | for(int n = 0; n < numangle; n++ ){ 55 | int r = cvRound( (*i).x * tabCos[n] + (*i).y * tabSin[n] ); // ρ = x cos θ + y sin θ 56 | r += (numrho - 1) / 2; 57 | accum[(n+1) * (numrho+2) + r+1]++; 58 | } 59 | } 60 | 61 | // stage 2. finding peak 62 | int peak = 0 , rpeak = 0, npeak = 0; 63 | 64 | for(int r = 0; r < numrho; r++ ) 65 | for(int n = 0; n < numangle; n++ ){ 66 | int base = (n+1) * (numrho+2) + r+1; 67 | 68 | if( accum[base] > peak ){ 69 | peak = accum[base]; 70 | rpeak = r; 71 | npeak = n; 72 | } 73 | } 74 | linepolar.rho = (rpeak - (numrho - 1)*0.5f) * rho; 75 | linepolar.angle = static_cast(min_theta) + npeak * theta; 76 | } 77 | -------------------------------------------------------------------------------- /imageproc/swt.cpp: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Copyright 2012 Andrew Perrault and Saurav Kumar. 4 | This file is part of DetectText. 5 | DetectText is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | DetectText is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | You should have received a copy of the GNU General Public License 14 | along with DetectText. If not, see . 15 | */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | /*#include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include */ 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | using namespace cv; 43 | 44 | #include "swt.h" 45 | 46 | #define PI 3.14159265 47 | 48 | const Scalar BLUE (255, 0, 0); 49 | const Scalar GREEN(0, 255, 0); 50 | const Scalar RED (0, 0, 255); 51 | 52 | std::vector findBoundingBoxes( std::vector > & components, 53 | std::vector & chains, 54 | std::vector & compBB, 55 | Mat& output) { 56 | std::vector bb; 57 | bb.reserve(chains.size()); 58 | for (auto& chainit : chains) { 59 | int minx = output.cols; 60 | int miny = output.rows; 61 | int maxx = 0; 62 | int maxy = 0; 63 | for (std::vector::const_iterator cit = chainit.components.begin(); cit != chainit.components.end(); cit++) { 64 | miny = std::min(miny,compBB[*cit].first.y); 65 | minx = std::min(minx,compBB[*cit].first.x); 66 | maxy = std::max(maxy,compBB[*cit].second.y); 67 | maxx = std::max(maxx,compBB[*cit].second.x); 68 | } 69 | Point2i p0(minx,miny); 70 | Point2i p1(maxx,maxy); 71 | SWTPointPair2i pair(p0,p1); 72 | bb.push_back(pair); 73 | } 74 | return bb; 75 | } 76 | 77 | std::vector findBoundingBoxes( std::vector > & components, 78 | Mat& output) { 79 | std::vector bb; 80 | bb.reserve(components.size()); 81 | for (auto& compit : components) { 82 | int minx = output.cols; 83 | int miny = output.rows; 84 | int maxx = 0; 85 | int maxy = 0; 86 | for (auto& it : compit) { 87 | miny = std::min(miny,it.y); 88 | minx = std::min(minx,it.x); 89 | maxy = std::max(maxy,it.y); 90 | maxx = std::max(maxx,it.x); 91 | } 92 | Point2i p0(minx,miny); 93 | Point2i p1(maxx,maxy); 94 | SWTPointPair2i pair(p0,p1); 95 | bb.push_back(pair); 96 | } 97 | return bb; 98 | } 99 | 100 | void normalizeImage (const Mat& input, Mat& output) { 101 | assert ( input.depth() == CV_32F ); 102 | assert ( input.channels() == 1 ); 103 | assert ( output.depth() == CV_32F ); 104 | assert ( output.channels() == 1 ); 105 | 106 | float maxVal = 0; 107 | float minVal = 1e100; 108 | for ( int row = 0; row < input.rows; row++ ){ 109 | const float* ptr = (const float*)input.ptr(row); 110 | for ( int col = 0; col < input.cols; col++ ){ 111 | if (*ptr < 0) { } 112 | else { 113 | maxVal = std::max(*ptr, maxVal); 114 | minVal = std::min(*ptr, minVal); 115 | } 116 | ptr++; 117 | } 118 | } 119 | 120 | float difference = maxVal - minVal; 121 | for ( int row = 0; row < input.rows; row++ ) { 122 | const float* ptrin = (const float*)input.ptr(row); 123 | float* ptrout = (float*)output.ptr(row); 124 | for ( int col = 0; col < input.cols; col++ ) { 125 | if (*ptrin < 0) { 126 | *ptrout = 1; 127 | } else { 128 | *ptrout = ((*ptrin) - minVal)/difference; 129 | } 130 | ptrout++; 131 | ptrin++; 132 | } 133 | } 134 | } 135 | 136 | void renderComponents (const Mat& SWTImage, std::vector > & components, Mat& output) { 137 | output.setTo(0); 138 | 139 | for (auto& component : components) { 140 | for (auto& pit : component) { 141 | output.at(pit.y, pit.x) = SWTImage.at(pit.y, pit.x); 142 | } 143 | } 144 | for( int row = 0; row < output.rows; row++ ){ 145 | float* ptr = (float*)output.ptr(row); 146 | for ( int col = 0; col < output.cols; col++ ){ 147 | if (*ptr == 0) { 148 | *ptr = -1; 149 | } 150 | ptr++; 151 | } 152 | } 153 | float maxVal = 0; 154 | float minVal = 1e100; 155 | for( int row = 0; row < output.rows; row++ ){ 156 | const float* ptr = (const float*)output.ptr(row); 157 | for ( int col = 0; col < output.cols; col++ ){ 158 | if (*ptr == 0) { } 159 | else { 160 | maxVal = std::max(*ptr, maxVal); 161 | minVal = std::min(*ptr, minVal); 162 | } 163 | ptr++; 164 | } 165 | } 166 | float difference = maxVal - minVal; 167 | for( int row = 0; row < output.rows; row++ ){ 168 | float* ptr = (float*)output.ptr(row); 169 | for ( int col = 0; col < output.cols; col++ ){ 170 | if (*ptr < 1) { 171 | *ptr = 1; 172 | } else { 173 | *ptr = ((*ptr) - minVal)/difference; 174 | } 175 | ptr++; 176 | } 177 | } 178 | 179 | } 180 | 181 | void renderComponentsWithBoxes (Mat& SWTImage, std::vector > & components, 182 | std::vector & compBB, Mat& output) { 183 | Mat outTemp( output.size(), CV_32FC1 ); 184 | 185 | renderComponents(SWTImage, components, outTemp); 186 | 187 | std::vector bb; 188 | bb.reserve(compBB.size()); 189 | for (auto& it : compBB) { 190 | Point2i p0 = cvPoint(it.first.x, it.first.y); 191 | Point2i p1 = cvPoint(it.second.x, it.second.y); 192 | SWTPointPair2i pair(p0, p1); 193 | bb.push_back(pair); 194 | } 195 | 196 | Mat out( output.size(), CV_8UC1 ); 197 | outTemp.convertTo(out, CV_8UC1, 255.); 198 | cvtColor (out, output, CV_GRAY2RGB); 199 | 200 | int count = 0; 201 | for (auto it : bb) { 202 | Scalar c; 203 | if (count % 3 == 0) { 204 | c = BLUE; 205 | } 206 | else if (count % 3 == 1) { 207 | c = GREEN; 208 | } 209 | else { 210 | c = RED; 211 | } 212 | count++; 213 | rectangle(output, it.first, it.second, c, 2); 214 | } 215 | } 216 | 217 | void renderChainsWithBoxes (Mat& SWTImage, 218 | std::vector > & components, 219 | std::vector & chains, 220 | std::vector & compBB, 221 | Mat& output) { 222 | // keep track of included components 223 | std::vector included; 224 | included.reserve(components.size()); 225 | for (unsigned int i = 0; i != components.size(); i++) { 226 | included.push_back(false); 227 | } 228 | for (Chain& it : chains) { 229 | for (std::vector::iterator cit = it.components.begin(); cit != it.components.end(); cit++) { 230 | included[*cit] = true; 231 | } 232 | } 233 | std::vector > componentsRed; 234 | for (unsigned int i = 0; i != components.size(); i++ ) { 235 | if (included[i]) { 236 | componentsRed.push_back(components[i]); 237 | } 238 | } 239 | Mat outTemp( output.size(), CV_32FC1 ); 240 | 241 | std::cout << componentsRed.size() << " components after chaining" << std::endl; 242 | renderComponents(SWTImage, componentsRed, outTemp); 243 | std::vector bb; 244 | bb = findBoundingBoxes(components, chains, compBB, outTemp); 245 | 246 | Mat out( output.size(), CV_8UC1 ); 247 | outTemp.convertTo(out, CV_8UC1, 255); 248 | cvtColor (out, output, CV_GRAY2RGB); 249 | 250 | int count = 0; 251 | for (auto& it : bb) { 252 | CvScalar c; 253 | if (count % 3 == 0) { 254 | c = BLUE; 255 | } 256 | else if (count % 3 == 1) { 257 | c = GREEN; 258 | } 259 | else { 260 | c = RED; 261 | } 262 | count++; 263 | rectangle(output, it.first, it.second, c, 2); 264 | } 265 | } 266 | 267 | void renderChains (Mat& SWTImage, 268 | std::vector > & components, 269 | std::vector & chains, 270 | Mat& output) { 271 | // keep track of included components 272 | std::vector included; 273 | included.reserve(components.size()); 274 | for (unsigned int i = 0; i != components.size(); i++) { 275 | included.push_back(false); 276 | } 277 | for (std::vector::iterator it = chains.begin(); it != chains.end();it++) { 278 | for (std::vector::iterator cit = it->components.begin(); cit != it->components.end(); cit++) { 279 | included[*cit] = true; 280 | } 281 | } 282 | std::vector > componentsRed; 283 | for (unsigned int i = 0; i != components.size(); i++ ) { 284 | if (included[i]) { 285 | componentsRed.push_back(components[i]); 286 | } 287 | } 288 | std::cout << componentsRed.size() << " components after chaining" << std::endl; 289 | Mat outTemp( output.size(), CV_32FC1 ); 290 | renderComponents(SWTImage,componentsRed,outTemp); 291 | outTemp.convertTo(output, CV_8UC1, 255); 292 | 293 | } 294 | 295 | Mat textDetection (const Mat& input, bool dark_on_light) { 296 | assert ( input.depth() == CV_8U ); 297 | assert ( input.channels() == 3 ); 298 | 299 | std::cout << "Running textDetection with dark_on_light " << dark_on_light << std::endl; 300 | 301 | // Convert to grayscale 302 | Mat grayImage( input.size(), CV_8UC1 ); 303 | cvtColor ( input, grayImage, CV_RGB2GRAY ); 304 | // Create Canny Image 305 | double threshold_low = 175; 306 | double threshold_high = 320; 307 | Mat edgeImage( input.size(),CV_8UC1 ); 308 | Canny(grayImage, edgeImage, threshold_low, threshold_high, 3) ; 309 | imshow( "canny", edgeImage); 310 | 311 | // Create gradient X, gradient Y 312 | Mat gaussianImage( input.size(), CV_32FC1); 313 | grayImage.convertTo(gaussianImage, CV_32FC1, 1./255.); 314 | GaussianBlur( gaussianImage, gaussianImage, Size(5, 5), 0); 315 | Mat gradientX( input.size(), CV_32FC1 ); 316 | Mat gradientY( input.size(), CV_32FC1 ); 317 | Scharr(gaussianImage, gradientX, -1, 1, 0); 318 | Scharr(gaussianImage, gradientY, -1, 0, 1); 319 | GaussianBlur(gradientX, gradientX, Size(3, 3), 0); 320 | GaussianBlur(gradientY, gradientY, Size(3, 3), 0); 321 | 322 | // Calculate SWT and return ray vectors 323 | std::vector rays; 324 | Mat SWTImage( input.size(), CV_32FC1 ); 325 | for( int row = 0; row < input.rows; row++ ){ 326 | float* ptr = (float*)SWTImage.ptr(row); 327 | for ( int col = 0; col < input.cols; col++ ){ 328 | *ptr++ = -1; 329 | } 330 | } 331 | strokeWidthTransform ( edgeImage, gradientX, gradientY, dark_on_light, SWTImage, rays ); 332 | SWTMedianFilter ( SWTImage, rays ); 333 | 334 | Mat output2( input.size(), CV_32FC1 ); 335 | normalizeImage (SWTImage, output2); 336 | Mat saveSWT( input.size(), CV_8UC1 ); 337 | output2.convertTo(saveSWT, CV_8UC1, 255); 338 | imshow ( "SWT", saveSWT); 339 | 340 | 341 | 342 | // Calculate legally connect components from SWT and gradient image. 343 | // return type is a vector of vectors, where each outer vector is a component and 344 | // the inner vector contains the (y,x) of each pixel in that component. 345 | std::vector > components = findLegallyConnectedComponents(SWTImage, rays); 346 | 347 | // Filter the components 348 | std::vector > validComponents; 349 | std::vector compBB; 350 | std::vector compCenters; 351 | std::vector compMedians; 352 | std::vector compDimensions; 353 | filterComponents(SWTImage, components, validComponents, compCenters, compMedians, compDimensions, compBB ); 354 | 355 | Mat output3( input.size(), CV_8UC3 ); 356 | renderComponentsWithBoxes (SWTImage, validComponents, compBB, output3); 357 | imshow ( "components",output3); 358 | // 359 | 360 | // Make chains of components 361 | std::vector chains; 362 | chains = makeChains(input, validComponents, compCenters, compMedians, compDimensions, compBB); 363 | 364 | Mat output4( input.size(), CV_8UC1 ); 365 | renderChains ( SWTImage, validComponents, chains, output4 ); 366 | imshow ( "text", output4); 367 | 368 | Mat output5( input.size(), CV_8UC3 ); 369 | cvtColor (output4, output5, CV_GRAY2RGB); 370 | 371 | 372 | /*IplImage * output = 373 | cvCreateImage ( input.size(), CV_8UC3 ); 374 | renderChainsWithBoxes ( SWTImage, validComponents, chains, compBB, output); */ 375 | return output5; 376 | } 377 | 378 | void strokeWidthTransform (const Mat& edgeImage, 379 | Mat& gradientX, 380 | Mat& gradientY, 381 | bool dark_on_light, 382 | Mat& SWTImage, 383 | std::vector & rays) { 384 | // First pass 385 | float prec = .05; 386 | for( int row = 0; row < edgeImage.rows; row++ ){ 387 | const uchar* ptr = (const uchar*)edgeImage.ptr(row); 388 | for ( int col = 0; col < edgeImage.cols; col++ ){ 389 | if (*ptr > 0) { 390 | Ray r; 391 | 392 | SWTPoint2d p; 393 | p.x = col; 394 | p.y = row; 395 | r.p = p; 396 | std::vector points; 397 | points.push_back(p); 398 | 399 | float curX = (float)col + 0.5; 400 | float curY = (float)row + 0.5; 401 | int curPixX = col; 402 | int curPixY = row; 403 | float G_x = gradientX.at(row, col); 404 | float G_y = gradientY.at(row, col); 405 | // normalize gradient 406 | float mag = sqrt( (G_x * G_x) + (G_y * G_y) ); 407 | if (dark_on_light){ 408 | G_x = -G_x/mag; 409 | G_y = -G_y/mag; 410 | } else { 411 | G_x = G_x/mag; 412 | G_y = G_y/mag; 413 | 414 | } 415 | while (true) { 416 | curX += G_x*prec; 417 | curY += G_y*prec; 418 | if ((int)(floor(curX)) != curPixX || (int)(floor(curY)) != curPixY) { 419 | curPixX = (int)(floor(curX)); 420 | curPixY = (int)(floor(curY)); 421 | // check if pixel is outside boundary of image 422 | if (curPixX < 0 || (curPixX >= SWTImage.cols) || curPixY < 0 || (curPixY >= SWTImage.rows)) { 423 | break; 424 | } 425 | SWTPoint2d pnew; 426 | pnew.x = curPixX; 427 | pnew.y = curPixY; 428 | points.push_back(pnew); 429 | 430 | if (edgeImage.at(curPixY, curPixX) > 0) { 431 | r.q = pnew; 432 | // dot product 433 | float G_xt = gradientX.at(curPixY,curPixX); 434 | float G_yt = gradientY.at(curPixY,curPixX); 435 | mag = sqrt( (G_xt * G_xt) + (G_yt * G_yt) ); 436 | if (dark_on_light) { 437 | G_xt = -G_xt / mag; 438 | G_yt = -G_yt / mag; 439 | } else { 440 | G_xt = G_xt / mag; 441 | G_yt = G_yt / mag; 442 | 443 | } 444 | 445 | if (acos(G_x * -G_xt + G_y * -G_yt) < PI/2.0 ) { 446 | float length = sqrt( ((float)r.q.x - (float)r.p.x)*((float)r.q.x - (float)r.p.x) + ((float)r.q.y - (float)r.p.y)*((float)r.q.y - (float)r.p.y)); 447 | for (std::vector::iterator pit = points.begin(); pit != points.end(); pit++) { 448 | if (SWTImage.at(pit->y, pit->x) < 0) { 449 | SWTImage.at(pit->y, pit->x) = length; 450 | } else { 451 | SWTImage.at(pit->y, pit->x) = std::min(length, SWTImage.at(pit->y, pit->x)); 452 | } 453 | } 454 | r.points = points; 455 | rays.push_back(r); 456 | } 457 | break; 458 | } 459 | } 460 | } 461 | } 462 | ptr++; 463 | } 464 | } 465 | 466 | } 467 | 468 | void SWTMedianFilter (Mat& SWTImage, std::vector & rays) { 469 | for (auto& rit : rays) { 470 | for (auto& pit : rit.points) { 471 | pit.SWT = SWTImage.at(pit.y, pit.x); 472 | } 473 | std::sort(rit.points.begin(), rit.points.end(), &Point2dSort); 474 | float median = (rit.points[rit.points.size()/2]).SWT; 475 | for (auto& pit : rit.points) { 476 | SWTImage.at(pit.y, pit.x) = std::min(pit.SWT, median); 477 | } 478 | } 479 | } 480 | 481 | bool Point2dSort (const SWTPoint2d &lhs, const SWTPoint2d &rhs) { 482 | return lhs.SWT < rhs.SWT; 483 | } 484 | 485 | std::vector< std::vector > findLegallyConnectedComponents (Mat& SWTImage, std::vector & rays) { 486 | boost::unordered_map map; 487 | boost::unordered_map revmap; 488 | 489 | typedef boost::adjacency_list Graph; 490 | int num_vertices = 0; 491 | // Number vertices for graph. Associate each point with number 492 | for( int row = 0; row < SWTImage.rows; row++ ){ 493 | float * ptr = (float*)SWTImage.ptr(row); 494 | for (int col = 0; col < SWTImage.cols; col++ ){ 495 | if (*ptr > 0) { 496 | map[row * SWTImage.cols + col] = num_vertices; 497 | SWTPoint2d p; 498 | p.x = col; 499 | p.y = row; 500 | revmap[num_vertices] = p; 501 | num_vertices++; 502 | } 503 | ptr++; 504 | } 505 | } 506 | 507 | Graph g(num_vertices); 508 | 509 | for( int row = 0; row < SWTImage.rows; row++ ){ 510 | float * ptr = (float*)SWTImage.ptr(row); 511 | for (int col = 0; col < SWTImage.cols; col++ ){ 512 | if (*ptr > 0) { 513 | // check pixel to the right, right-down, down, left-down 514 | int this_pixel = map[row * SWTImage.cols + col]; 515 | if (col+1 < SWTImage.cols) { 516 | float right = SWTImage.at(row, col+1); 517 | if (right > 0 && ((*ptr)/right <= 3.0 || right/(*ptr) <= 3.0)) 518 | boost::add_edge(this_pixel, map.at(row * SWTImage.cols + col + 1), g); 519 | } 520 | if (row+1 < SWTImage.rows) { 521 | if (col+1 < SWTImage.cols) { 522 | float right_down = SWTImage.at(row+1, col+1); 523 | if (right_down > 0 && ((*ptr)/right_down <= 3.0 || right_down/(*ptr) <= 3.0)) 524 | boost::add_edge(this_pixel, map.at((row+1) * SWTImage.cols + col + 1), g); 525 | } 526 | float down = SWTImage.at(row+1, col); 527 | if (down > 0 && ((*ptr)/down <= 3.0 || down/(*ptr) <= 3.0)) 528 | boost::add_edge(this_pixel, map.at((row+1) * SWTImage.cols + col), g); 529 | if (col-1 >= 0) { 530 | float left_down = SWTImage.at(row+1, col-1); 531 | if (left_down > 0 && ((*ptr)/left_down <= 3.0 || left_down/(*ptr) <= 3.0)) 532 | boost::add_edge(this_pixel, map.at((row+1) * SWTImage.cols + col - 1), g); 533 | } 534 | } 535 | } 536 | ptr++; 537 | } 538 | } 539 | 540 | std::vector c(num_vertices); 541 | 542 | int num_comp = connected_components(g, &c[0]); 543 | 544 | std::vector > components; 545 | components.reserve(num_comp); 546 | std::cout << "Before filtering, " << num_comp << " components and " << num_vertices << " vertices" << std::endl; 547 | for (int j = 0; j < num_comp; j++) { 548 | std::vector tmp; 549 | components.push_back( tmp ); 550 | } 551 | for (int j = 0; j < num_vertices; j++) { 552 | SWTPoint2d p = revmap[j]; 553 | (components[c[j]]).push_back(p); 554 | } 555 | 556 | return components; 557 | } 558 | 559 | std::vector< std::vector > 560 | findLegallyConnectedComponentsRAY (Mat& SWTImage, 561 | std::vector & rays) 562 | { 563 | boost::unordered_map map; 564 | boost::unordered_map revmap; 565 | 566 | typedef boost::adjacency_list Graph; 567 | int num_vertices = 0; 568 | // Number vertices for graph. Associate each point with number 569 | for( int row = 0; row < SWTImage.rows; row++ ){ 570 | float * ptr = (float*)SWTImage.ptr(row); 571 | for (int col = 0; col < SWTImage.cols; col++ ){ 572 | if (*ptr > 0) { 573 | map[row * SWTImage.cols + col] = num_vertices; 574 | SWTPoint2d p; 575 | p.x = col; 576 | p.y = row; 577 | revmap[num_vertices] = p; 578 | num_vertices++; 579 | } 580 | ptr++; 581 | } 582 | } 583 | 584 | Graph g(num_vertices); 585 | 586 | // Traverse and add edges to graph 587 | for (std::vector::const_iterator it = rays.begin(); it != rays.end(); it++) { 588 | float lastSW = 0; 589 | int lastRow = 0; 590 | int lastCol = 0; 591 | for (std::vector::const_iterator it2 = it->points.begin(); it2 != it->points.end(); it2++) { 592 | float currentSW = SWTImage.at(it2->y, it2->x); 593 | if (lastSW == 0) {} 594 | else if (lastSW/currentSW<=3.0 || currentSW/lastSW<=3.0){ 595 | boost::add_edge(map.at(it2->y * SWTImage.cols + it2->x), map.at(lastRow * SWTImage.cols + lastCol), g); 596 | } 597 | lastSW = currentSW; 598 | lastRow = it2->y; 599 | lastCol = it2->x; 600 | } 601 | lastSW = 0; 602 | lastRow = 0; 603 | lastCol = 0; 604 | } 605 | 606 | std::vector c(num_vertices); 607 | 608 | int num_comp = connected_components(g, &c[0]); 609 | 610 | std::vector > components; 611 | components.reserve(num_comp); 612 | std::cout << "Before filtering, " << num_comp << " components and " << num_vertices << " vertices" << std::endl; 613 | for (int j = 0; j < num_comp; j++) { 614 | std::vector tmp; 615 | components.push_back( tmp ); 616 | } 617 | for (int j = 0; j < num_vertices; j++) { 618 | SWTPoint2d p = revmap[j]; 619 | (components[c[j]]).push_back(p); 620 | } 621 | 622 | return components; 623 | } 624 | 625 | void componentStats(Mat& SWTImage, 626 | const std::vector & component, 627 | float & mean, float & variance, float & median, 628 | int & minx, int & miny, int & maxx, int & maxy) 629 | { 630 | std::vector temp; 631 | temp.reserve(component.size()); 632 | mean = 0; 633 | variance = 0; 634 | minx = 1000000; 635 | miny = 1000000; 636 | maxx = 0; 637 | maxy = 0; 638 | for (std::vector::const_iterator it = component.begin(); it != component.end(); it++) { 639 | float t = SWTImage.at(it->y, it->x); 640 | mean += t; 641 | temp.push_back(t); 642 | miny = std::min(miny,it->y); 643 | minx = std::min(minx,it->x); 644 | maxy = std::max(maxy,it->y); 645 | maxx = std::max(maxx,it->x); 646 | } 647 | mean = mean / ((float)component.size()); 648 | for (std::vector::const_iterator it = temp.begin(); it != temp.end(); it++) { 649 | variance += (*it - mean) * (*it - mean); 650 | } 651 | variance = variance / ((float)component.size()); 652 | std::sort(temp.begin(),temp.end()); 653 | median = temp[temp.size()/2]; 654 | } 655 | 656 | 657 | void filterComponents(Mat& SWTImage, 658 | std::vector > & components, 659 | std::vector > & validComponents, 660 | std::vector & compCenters, 661 | std::vector & compMedians, 662 | std::vector & compDimensions, 663 | std::vector & compBB ) 664 | { 665 | validComponents.reserve(components.size()); 666 | compCenters.reserve(components.size()); 667 | compMedians.reserve(components.size()); 668 | compDimensions.reserve(components.size()); 669 | // bounding boxes 670 | compBB.reserve(components.size()); 671 | for (std::vector >::iterator it = components.begin(); it != components.end();it++) { 672 | // compute the stroke width mean, variance, median 673 | float mean, variance, median; 674 | int minx, miny, maxx, maxy; 675 | componentStats(SWTImage, (*it), mean, variance, median, minx, miny, maxx, maxy); 676 | 677 | // check if variance is less than half the mean 678 | if (variance > 0.5 * mean) { 679 | continue; 680 | } 681 | 682 | float length = (float)(maxx-minx+1); 683 | float width = (float)(maxy-miny+1); 684 | 685 | // check font height 686 | if (width > 300) { 687 | continue; 688 | } 689 | 690 | float area = length * width; 691 | float rminx = (float)minx; 692 | float rmaxx = (float)maxx; 693 | float rminy = (float)miny; 694 | float rmaxy = (float)maxy; 695 | // compute the rotated bounding box 696 | float increment = 1./36.; 697 | for (float theta = increment * PI; theta 10.) { 721 | continue; 722 | } 723 | 724 | // compute the diameter TODO finish 725 | // compute dense representation of component 726 | std::vector > denseRepr; 727 | denseRepr.reserve(maxx-minx+1); 728 | for (int i = 0; i < maxx-minx+1; i++) { 729 | std::vector tmp; 730 | tmp.reserve(maxy-miny+1); 731 | denseRepr.push_back(tmp); 732 | for (int j = 0; j < maxy-miny+1; j++) {\ 733 | denseRepr[i].push_back(0); 734 | } 735 | } 736 | for (std::vector::iterator pit = it->begin(); pit != it->end(); pit++) { 737 | (denseRepr[pit->x - minx])[pit->y - miny] = 1; 738 | } 739 | // create graph representing components 740 | const int num_nodes = it->size(); 741 | /* 742 | E edges[] = { E(0,2), 743 | E(1,1), E(1,3), E(1,4), 744 | E(2,1), E(2,3), 745 | E(3,4), 746 | E(4,0), E(4,1) }; 747 | Graph G(edges + sizeof(edges) / sizeof(E), weights, num_nodes); 748 | */ 749 | Point2dFloat center; 750 | center.x = ((float)(maxx+minx))/2.0; 751 | center.y = ((float)(maxy+miny))/2.0; 752 | 753 | SWTPoint2d dimensions; 754 | dimensions.x = maxx - minx + 1; 755 | dimensions.y = maxy - miny + 1; 756 | 757 | SWTPoint2d bb1; 758 | bb1.x = minx; 759 | bb1.y = miny; 760 | 761 | SWTPoint2d bb2; 762 | bb2.x = maxx; 763 | bb2.y = maxy; 764 | SWTPointPair2d pair(bb1,bb2); 765 | 766 | compBB.push_back(pair); 767 | compDimensions.push_back(dimensions); 768 | compMedians.push_back(median); 769 | compCenters.push_back(center); 770 | validComponents.push_back(*it); 771 | } 772 | std::vector > tempComp; 773 | std::vector tempDim; 774 | std::vector tempMed; 775 | std::vector tempCenters; 776 | std::vector tempBB; 777 | tempComp.reserve(validComponents.size()); 778 | tempCenters.reserve(validComponents.size()); 779 | tempDim.reserve(validComponents.size()); 780 | tempMed.reserve(validComponents.size()); 781 | tempBB.reserve(validComponents.size()); 782 | for (unsigned int i = 0; i < validComponents.size(); i++) { 783 | int count = 0; 784 | for (unsigned int j = 0; j < validComponents.size(); j++) { 785 | if (i != j) { 786 | if (compBB[i].first.x <= compCenters[j].x && compBB[i].second.x >= compCenters[j].x && 787 | compBB[i].first.y <= compCenters[j].y && compBB[i].second.y >= compCenters[j].y) { 788 | count++; 789 | } 790 | } 791 | } 792 | if (count < 2) { 793 | tempComp.push_back(validComponents[i]); 794 | tempCenters.push_back(compCenters[i]); 795 | tempMed.push_back(compMedians[i]); 796 | tempDim.push_back(compDimensions[i]); 797 | tempBB.push_back(compBB[i]); 798 | } 799 | } 800 | validComponents = tempComp; 801 | compDimensions = tempDim; 802 | compMedians = tempMed; 803 | compCenters = tempCenters; 804 | compBB = tempBB; 805 | 806 | compDimensions.reserve(tempComp.size()); 807 | compMedians.reserve(tempComp.size()); 808 | compCenters.reserve(tempComp.size()); 809 | validComponents.reserve(tempComp.size()); 810 | compBB.reserve(tempComp.size()); 811 | 812 | std::cout << "After filtering " << validComponents.size() << " components" << std::endl; 813 | } 814 | 815 | bool sharesOneEnd( Chain c0, Chain c1) { 816 | if (c0.p == c1.p || c0.p == c1.q || c0.q == c1.q || c0.q == c1.p) { 817 | return true; 818 | } 819 | else { 820 | return false; 821 | } 822 | } 823 | 824 | bool chainSortDist (const Chain &lhs, const Chain &rhs) { 825 | return lhs.dist < rhs.dist; 826 | } 827 | 828 | bool chainSortLength (const Chain &lhs, const Chain &rhs) { 829 | return lhs.components.size() > rhs.components.size(); 830 | } 831 | 832 | std::vector makeChains( const Mat& colorImage, 833 | std::vector > & components, 834 | std::vector & compCenters, 835 | std::vector & compMedians, 836 | std::vector & compDimensions, 837 | std::vector & compBB) { 838 | assert (compCenters.size() == components.size()); 839 | // make vector of color averages 840 | std::vector colorAverages; 841 | colorAverages.reserve(components.size()); 842 | for (std::vector >::iterator it = components.begin(); it != components.end();it++) { 843 | Point3dFloat mean; 844 | mean.x = 0; 845 | mean.y = 0; 846 | mean.z = 0; 847 | int num_points = 0; 848 | for (std::vector::iterator pit = it->begin(); pit != it->end(); pit++) { 849 | mean.x += (float) colorImage.at(pit->y, (pit->x)*3 ); 850 | mean.y += (float) colorImage.at(pit->y, (pit->x)*3+1 ); 851 | mean.z += (float) colorImage.at(pit->y, (pit->x)*3+2 ); 852 | num_points++; 853 | } 854 | mean.x = mean.x / ((float)num_points); 855 | mean.y = mean.y / ((float)num_points); 856 | mean.z = mean.z / ((float)num_points); 857 | colorAverages.push_back(mean); 858 | } 859 | 860 | // form all eligible pairs and calculate the direction of each 861 | std::vector chains; 862 | for ( unsigned int i = 0; i < components.size(); i++ ) { 863 | for ( unsigned int j = i + 1; j < components.size(); j++ ) { 864 | // TODO add color metric 865 | if ( (compMedians[i]/compMedians[j] <= 2.0 || compMedians[j]/compMedians[i] <= 2.0) && 866 | (compDimensions[i].y/compDimensions[j].y <= 2.0 || compDimensions[j].y/compDimensions[i].y <= 2.0)) { 867 | float dist = (compCenters[i].x - compCenters[j].x) * (compCenters[i].x - compCenters[j].x) + 868 | (compCenters[i].y - compCenters[j].y) * (compCenters[i].y - compCenters[j].y); 869 | float colorDist = (colorAverages[i].x - colorAverages[j].x) * (colorAverages[i].x - colorAverages[j].x) + 870 | (colorAverages[i].y - colorAverages[j].y) * (colorAverages[i].y - colorAverages[j].y) + 871 | (colorAverages[i].z - colorAverages[j].z) * (colorAverages[i].z - colorAverages[j].z); 872 | if (dist < 9*(float)(std::max(std::min(compDimensions[i].x,compDimensions[i].y),std::min(compDimensions[j].x,compDimensions[j].y))) 873 | *(float)(std::max(std::min(compDimensions[i].x,compDimensions[i].y),std::min(compDimensions[j].x,compDimensions[j].y))) 874 | && colorDist < 1600) { 875 | Chain c; 876 | c.p = i; 877 | c.q = j; 878 | std::vector comps; 879 | comps.push_back(c.p); 880 | comps.push_back(c.q); 881 | c.components = comps; 882 | c.dist = dist; 883 | float d_x = (compCenters[i].x - compCenters[j].x); 884 | float d_y = (compCenters[i].y - compCenters[j].y); 885 | /* 886 | float d_x = (compBB[i].first.x - compBB[j].second.x); 887 | float d_y = (compBB[i].second.y - compBB[j].second.y); 888 | */ 889 | float mag = sqrt(d_x*d_x + d_y*d_y); 890 | d_x = d_x / mag; 891 | d_y = d_y / mag; 892 | Point2dFloat dir; 893 | dir.x = d_x; 894 | dir.y = d_y; 895 | c.direction = dir; 896 | chains.push_back(c); 897 | 898 | /*std::cerr << c.p << " " << c.q << std::endl; 899 | std::cerr << c.direction.x << " " << c.direction.y << std::endl; 900 | std::cerr << compCenters[c.p].x << " " << compCenters[c.p].y << std::endl; 901 | std::cerr << compCenters[c.q].x << " " << compCenters[c.q].y << std::endl; 902 | std::cerr << std::endl; 903 | std::cerr << colorDist << std::endl; */ 904 | } 905 | } 906 | } 907 | } 908 | std::cout << chains.size() << " eligible pairs" << std::endl; 909 | std::sort(chains.begin(), chains.end(), &chainSortDist); 910 | 911 | std::cerr << std::endl; 912 | const float strictness = PI/6.0; 913 | //merge chains 914 | int merges = 1; 915 | while (merges > 0) { 916 | for (unsigned int i = 0; i < chains.size(); i++) { 917 | chains[i].merged = false; 918 | } 919 | merges = 0; 920 | std::vector newchains; 921 | for (unsigned int i = 0; i < chains.size(); i++) { 922 | for (unsigned int j = 0; j < chains.size(); j++) { 923 | if (i != j) { 924 | if (!chains[i].merged && !chains[j].merged && sharesOneEnd(chains[i],chains[j])) { 925 | if (chains[i].p == chains[j].p) { 926 | if (acos(chains[i].direction.x * -chains[j].direction.x + chains[i].direction.y * -chains[j].direction.y) < strictness) { 927 | /* if (chains[i].p == chains[i].q || chains[j].p == chains[j].q) { 928 | std::cout << "CRAZY ERROR" << std::endl; 929 | } else if (chains[i].p == chains[j].p && chains[i].q == chains[j].q) { 930 | std::cout << "CRAZY ERROR" << std::endl; 931 | } else if (chains[i].p == chains[j].q && chains[i].q == chains[j].p) { 932 | std::cout << "CRAZY ERROR" << std::endl; 933 | } 934 | std::cerr << 1 <::iterator it = chains[j].components.begin(); it != chains[j].components.end(); it++) { 944 | chains[i].components.push_back(*it); 945 | } 946 | float d_x = (compCenters[chains[i].p].x - compCenters[chains[i].q].x); 947 | float d_y = (compCenters[chains[i].p].y - compCenters[chains[i].q].y); 948 | chains[i].dist = d_x * d_x + d_y * d_y; 949 | 950 | float mag = sqrt(d_x*d_x + d_y*d_y); 951 | d_x = d_x / mag; 952 | d_y = d_y / mag; 953 | Point2dFloat dir; 954 | dir.x = d_x; 955 | dir.y = d_y; 956 | chains[i].direction = dir; 957 | chains[j].merged = true; 958 | merges++; 959 | /*j=-1; 960 | i=0; 961 | if (i == chains.size() - 1) i=-1; 962 | std::stable_sort(chains.begin(), chains.end(), &chainSortLength);*/ 963 | } 964 | } else if (chains[i].p == chains[j].q) { 965 | if (acos(chains[i].direction.x * chains[j].direction.x + chains[i].direction.y * chains[j].direction.y) < strictness) { 966 | /* 967 | if (chains[i].p == chains[i].q || chains[j].p == chains[j].q) { 968 | std::cout << "CRAZY ERROR" << std::endl; 969 | } else if (chains[i].p == chains[j].p && chains[i].q == chains[j].q) { 970 | std::cout << "CRAZY ERROR" << std::endl; 971 | } else if (chains[i].p == chains[j].q && chains[i].q == chains[j].p) { 972 | std::cout << "CRAZY ERROR" << std::endl; 973 | } 974 | std::cerr << 2 <::iterator it = chains[j].components.begin(); it != chains[j].components.end(); it++) { 986 | chains[i].components.push_back(*it); 987 | } 988 | float d_x = (compCenters[chains[i].p].x - compCenters[chains[i].q].x); 989 | float d_y = (compCenters[chains[i].p].y - compCenters[chains[i].q].y); 990 | float mag = sqrt(d_x*d_x + d_y*d_y); 991 | chains[i].dist = d_x * d_x + d_y * d_y; 992 | 993 | d_x = d_x / mag; 994 | d_y = d_y / mag; 995 | 996 | Point2dFloat dir; 997 | dir.x = d_x; 998 | dir.y = d_y; 999 | chains[i].direction = dir; 1000 | chains[j].merged = true; 1001 | merges++; 1002 | /*j=-1; 1003 | i=0; 1004 | if (i == chains.size() - 1) i=-1; 1005 | std::stable_sort(chains.begin(), chains.end(), &chainSortLength); */ 1006 | } 1007 | } else if (chains[i].q == chains[j].p) { 1008 | if (acos(chains[i].direction.x * chains[j].direction.x + chains[i].direction.y * chains[j].direction.y) < strictness) { 1009 | /* if (chains[i].p == chains[i].q || chains[j].p == chains[j].q) { 1010 | std::cout << "CRAZY ERROR" << std::endl; 1011 | } else if (chains[i].p == chains[j].p && chains[i].q == chains[j].q) { 1012 | std::cout << "CRAZY ERROR" << std::endl; 1013 | } else if (chains[i].p == chains[j].q && chains[i].q == chains[j].p) { 1014 | std::cout << "CRAZY ERROR" << std::endl; 1015 | } 1016 | std::cerr << 3 <::iterator it = chains[j].components.begin(); it != chains[j].components.end(); it++) { 1025 | chains[i].components.push_back(*it); 1026 | } 1027 | float d_x = (compCenters[chains[i].p].x - compCenters[chains[i].q].x); 1028 | float d_y = (compCenters[chains[i].p].y - compCenters[chains[i].q].y); 1029 | float mag = sqrt(d_x*d_x + d_y*d_y); 1030 | chains[i].dist = d_x * d_x + d_y * d_y; 1031 | 1032 | 1033 | d_x = d_x / mag; 1034 | d_y = d_y / mag; 1035 | Point2dFloat dir; 1036 | dir.x = d_x; 1037 | dir.y = d_y; 1038 | 1039 | chains[i].direction = dir; 1040 | chains[j].merged = true; 1041 | merges++; 1042 | /*j=-1; 1043 | i=0; 1044 | if (i == chains.size() - 1) i=-1; 1045 | std::stable_sort(chains.begin(), chains.end(), &chainSortLength); */ 1046 | } 1047 | } else if (chains[i].q == chains[j].q) { 1048 | if (acos(chains[i].direction.x * -chains[j].direction.x + chains[i].direction.y * -chains[j].direction.y) < strictness) { 1049 | /* if (chains[i].p == chains[i].q || chains[j].p == chains[j].q) { 1050 | std::cout << "CRAZY ERROR" << std::endl; 1051 | } else if (chains[i].p == chains[j].p && chains[i].q == chains[j].q) { 1052 | std::cout << "CRAZY ERROR" << std::endl; 1053 | } else if (chains[i].p == chains[j].q && chains[i].q == chains[j].p) { 1054 | std::cout << "CRAZY ERROR" << std::endl; 1055 | } 1056 | std::cerr << 4 <::iterator it = chains[j].components.begin(); it != chains[j].components.end(); it++) { 1065 | chains[i].components.push_back(*it); 1066 | } 1067 | float d_x = (compCenters[chains[i].p].x - compCenters[chains[i].q].x); 1068 | float d_y = (compCenters[chains[i].p].y - compCenters[chains[i].q].y); 1069 | chains[i].dist = d_x * d_x + d_y * d_y; 1070 | 1071 | float mag = sqrt(d_x*d_x + d_y*d_y); 1072 | d_x = d_x / mag; 1073 | d_y = d_y / mag; 1074 | Point2dFloat dir; 1075 | dir.x = d_x; 1076 | dir.y = d_y; 1077 | chains[i].direction = dir; 1078 | chains[j].merged = true; 1079 | merges++; 1080 | /*j=-1; 1081 | i=0; 1082 | if (i == chains.size() - 1) i=-1; 1083 | std::stable_sort(chains.begin(), chains.end(), &chainSortLength);*/ 1084 | } 1085 | } 1086 | } 1087 | } 1088 | } 1089 | } 1090 | for (unsigned int i = 0; i < chains.size(); i++) { 1091 | if (!chains[i].merged) { 1092 | newchains.push_back(chains[i]); 1093 | } 1094 | } 1095 | chains = newchains; 1096 | std::stable_sort(chains.begin(), chains.end(), &chainSortLength); 1097 | } 1098 | 1099 | std::vector newchains; 1100 | newchains.reserve(chains.size()); 1101 | for (std::vector::iterator cit = chains.begin(); cit != chains.end(); cit++) { 1102 | if (cit->components.size() >= 3) { 1103 | newchains.push_back(*cit); 1104 | } 1105 | } 1106 | chains = newchains; 1107 | std::cout << chains.size() << " chains after merging" << std::endl; 1108 | return chains; 1109 | } 1110 | 1111 | -------------------------------------------------------------------------------- /imageproc/swt.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012 Andrew Perrault and Saurav Kumar. 3 | This file is part of DetectText. 4 | DetectText is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | DetectText is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | You should have received a copy of the GNU General Public License 13 | along with DetectText. If not, see . 14 | */ 15 | 16 | #ifndef SWT_H 17 | #define SWT_H 18 | 19 | #include 20 | struct SWTPoint2d { 21 | int x; 22 | int y; 23 | float SWT; 24 | }; 25 | 26 | typedef std::pair SWTPointPair2d; 27 | typedef std::pair SWTPointPair2i; 28 | 29 | struct Point2dFloat { 30 | float x; 31 | float y; 32 | }; 33 | struct Ray { 34 | SWTPoint2d p; 35 | SWTPoint2d q; 36 | std::vector points; 37 | }; 38 | struct Point3dFloat { 39 | float x; 40 | float y; 41 | float z; 42 | }; 43 | 44 | 45 | struct Chain { 46 | int p; 47 | int q; 48 | float dist; 49 | bool merged; 50 | Point2dFloat direction; 51 | std::vector components; 52 | }; 53 | 54 | bool Point2dSort (SWTPoint2d const & lhs, 55 | SWTPoint2d const & rhs); 56 | cv::Mat textDetection (const cv::Mat& input, bool dark_on_light); 57 | 58 | void strokeWidthTransform (const cv::Mat& edgeImage, 59 | cv::Mat& gradientX, 60 | cv::Mat& gradientY, 61 | bool dark_on_light, 62 | cv::Mat& SWTImage, 63 | std::vector & rays); 64 | 65 | void SWTMedianFilter (cv::Mat& SWTImage, std::vector & rays); 66 | 67 | std::vector< std::vector > findLegallyConnectedComponents (cv::Mat& SWTImage, std::vector & rays); 68 | 69 | std::vector< std::vector > 70 | findLegallyConnectedComponentsRAY (IplImage * SWTImage, 71 | std::vector & rays); 72 | 73 | void componentStats(IplImage * SWTImage, const std::vector & component, 74 | float & mean, float & variance, float & median, 75 | int & minx, int & miny, int & maxx, int & maxy); 76 | 77 | void filterComponents(cv::Mat& SWTImage, 78 | std::vector > & components, 79 | std::vector > & validComponents, 80 | std::vector & compCenters, 81 | std::vector & compMedians, 82 | std::vector & compDimensions, 83 | std::vector & compBB ); 84 | 85 | std::vector makeChains( const cv::Mat& colorImage, 86 | std::vector > & components, 87 | std::vector & compCenters, 88 | std::vector & compMedians, 89 | std::vector & compDimensions, 90 | std::vector & compBB); 91 | 92 | 93 | 94 | 95 | #endif // SWT_H 96 | -------------------------------------------------------------------------------- /imageproc/swtExample.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012 Andrew Perrault and Saurav Kumar. 3 | This file is part of DetectText. 4 | DetectText is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | DetectText is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | You should have received a copy of the GNU General Public License 13 | along with DetectText. If not, see . 14 | */ 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "swt.h" 25 | 26 | using namespace std; 27 | using namespace cv; 28 | 29 | void convertToFloatImage ( Mat& byteImage, Mat& floatImage ) 30 | { 31 | byteImage.convertTo(floatImage, CV_32FC1, 1 / 255.); 32 | } 33 | 34 | class FeatureError: public std::exception { 35 | std::string message; 36 | public: 37 | FeatureError(const std::string & msg, const std::string & file) { 38 | std::stringstream ss; 39 | 40 | ss << msg << " " << file; 41 | message = msg.c_str(); 42 | } 43 | ~FeatureError() throw () { 44 | } 45 | }; 46 | 47 | Mat loadByteImage(const char * name) { 48 | Mat image = imread(name); 49 | 50 | if (image.empty()) { 51 | return Mat(); 52 | } 53 | cvtColor(image, image, CV_BGR2RGB); 54 | return image; 55 | } 56 | 57 | Mat loadFloatImage(const char * name) { 58 | Mat image = imread(name); 59 | 60 | if (image.empty()) { 61 | return Mat(); 62 | } 63 | cvtColor(image, image, CV_BGR2RGB); 64 | Mat floatingImage(image.size(), CV_32FC3); 65 | image.convertTo(floatingImage, CV_32F, 1 / 255.); 66 | return floatingImage; 67 | } 68 | 69 | int mainTextDetection(int argc, char** argv) { 70 | Mat byteQueryImage = loadByteImage(argv[1]); 71 | if (byteQueryImage.empty()) { 72 | cerr << "couldn't load query image" << endl; 73 | return -1; 74 | } 75 | 76 | // Detect text in the image 77 | Mat output = textDetection(byteQueryImage, atoi(argv[2])); 78 | //cv::imshow("output",output); 79 | cv::waitKey(); 80 | return 0; 81 | } 82 | 83 | int main(int argc, char** argv) { 84 | if ((argc != 3)) { 85 | cerr << "usage: " << argv[0] << " imagefile resultImage darkText" << endl; 86 | return -1; 87 | } 88 | return mainTextDetection(argc, argv); 89 | } 90 | -------------------------------------------------------------------------------- /include/ColorDetector.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ColorDetector.h" 3 | 4 | cv::Mat ColorDetector::process(const cv::Mat &image) { 5 | // re-allocate binary map if necessary 6 | // same size as input image, but 1-channel 7 | result.create(image.size(),CV_8U); 8 | // processing loop above goes here 9 | cv::Mat_::const_iterator it = image.begin(); 10 | cv::Mat_::const_iterator itend = image.end(); 11 | cv::Mat_::const_iterator itout = result.begin(); 12 | for( ;it!=itend; ++it,++itout) 13 | { 14 | if(getDistanceToTargetColor(*it) <= maxDist) 15 | *itout = 255; 16 | else 17 | *itout = 0; 18 | 19 | } 20 | 21 | return result; 22 | } 23 | 24 | 25 | // Sets the color distance threshold. 26 | // Threshold must be positive, 27 | // otherwise distance threshold is set to 0. 28 | void ColorDetector::setColorDistanceThreshold(int distance) { 29 | 30 | if (distance<0) 31 | distance=0; 32 | maxDist= distance; 33 | } 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /include/ColorDetector.h: -------------------------------------------------------------------------------- 1 | /* 2 | * opencv computer vision programming cook book 2nd edition 3 | * example code 4 | * including the ColorDetector.cpp, and colordetector_demo.cpp for demostration 5 | */ 6 | 7 | #ifndef COLORDETECTOR_H 8 | #define COLORDETECTOR_H 9 | #include 10 | #include "ColorDetector.h" 11 | class ColorDetector 12 | { 13 | public: 14 | 15 | ColorDetector() = default; //default constructor 16 | ColorDetector(int maxDist, cv::Vec3b target) : maxDist(100), target(0,0,0) {} 17 | 18 | int maxDist; // minimum acceptable distance 19 | 20 | cv::Vec3b target; // target color 21 | cv::Mat result; // image containing resulting binary map 22 | 23 | 24 | int getDistanceToTargetColor(const cv::Vec3b& color) const { 25 | return getColorDistance(color, target); 26 | } 27 | int getColorDistance(const cv::Vec3b& color1,const cv::Vec3b& color2) const { 28 | return abs(color1[0]-color2[0])+abs(color1[1]-color2[1])+abs(color1[2]-color2[2]); 29 | } 30 | // Gets the color distance threshold 31 | int getColorDistanceThreshold() const { 32 | 33 | return maxDist; 34 | } 35 | 36 | void setTargetColor(uchar blue,uchar green,uchar red) { 37 | 38 | target = cv::Vec3b(blue, green, red); 39 | } 40 | 41 | // Sets the color to be detected 42 | void setTargetColor(cv::Vec3b color) { 43 | target=color; 44 | } 45 | 46 | // Gets the color to be detected 47 | cv::Vec3b getTargetColor() const { 48 | 49 | return target; 50 | } 51 | 52 | 53 | cv::Mat process(const cv::Mat &image); 54 | void setColorDistanceThreshold(int distance); 55 | }; 56 | 57 | 58 | 59 | 60 | #endif // COLORDETECTOR_H 61 | -------------------------------------------------------------------------------- /include/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /include/colorExtractor.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | void colorExtractor(const cv::Mat& image,cv::Mat& dst,cv::InputArray lowerb,cv::InputArray upperb); 6 | enum colors {BLUE=1,YELLOW,WHITE,BLACK}; 7 | //fun colorExractor return the mask; 8 | cv::Mat& colorExtractor(const cv::Mat& image,cv::Mat& dst,int color); 9 | 10 | 11 | 12 | void colorExtractor(const cv::Mat& image,cv::Mat& dst,cv::InputArray lowerb,cv::InputArray upperb){ 13 | if(image.channels() == 3){ 14 | cv::Mat img; 15 | cv::cvtColor(image,img,CV_BGR2HSV); 16 | 17 | cv::Mat mask; 18 | cv::inRange(img,lowerb,upperb,mask); 19 | if(debugflag) 20 | cv::imshow("mask",mask); 21 | std::vector channels(3,cv::Mat()); 22 | std::vector dischannels(3,cv::Mat()); 23 | cv::split(image,channels); 24 | channels[0].copyTo(dischannels[0],mask); 25 | channels[1].copyTo(dischannels[1],mask); 26 | channels[2].copyTo(dischannels[2],mask); 27 | cv::merge(dischannels,dst); 28 | if(debugflag) 29 | cv::imshow("extract the color",dst); 30 | }else std::cout << "Channels error of the input image in colorExtractor" << std::endl; 31 | 32 | } 33 | cv::Mat& colorExtractor(const cv::Mat& image,cv::Mat& dst,int color){ 34 | cv::Mat mask; 35 | if(image.channels() == 3){ 36 | cv::Mat img; 37 | cv::cvtColor(image,img,CV_BGR2HSV); 38 | 39 | cv::Scalar lowerb,upperb; 40 | if(color == BLUE){ 41 | lowerb = cv::Scalar(100,25,25); 42 | upperb = cv::Scalar(130,255,255); 43 | } 44 | if(color == YELLOW){ 45 | lowerb = cv::Scalar(20,25,25); 46 | upperb = cv::Scalar(40,255,255); 47 | } 48 | if(color == WHITE){ 49 | lowerb = cv::Scalar(0,0,220); 50 | upperb = cv::Scalar(255,60,255); 51 | } 52 | if(color == BLACK){ 53 | lowerb = cv::Scalar(0,150,0); 54 | upperb = cv::Scalar(255,255,25); 55 | } 56 | 57 | 58 | 59 | cv::inRange(img,lowerb,upperb,mask); 60 | if(debugflag) 61 | cv::imshow("mask",mask); 62 | std::vector channels(3,cv::Mat()); 63 | std::vector dischannels(3,cv::Mat()); 64 | cv::split(image,channels); 65 | channels[0].copyTo(dischannels[0],mask); 66 | channels[1].copyTo(dischannels[1],mask); 67 | channels[2].copyTo(dischannels[2],mask); 68 | cv::merge(dischannels,dst); 69 | if(debugflag) 70 | cv::imshow("extract the color",dst); 71 | }else std::cout << "Channels error of the input image in colorExtractor" << std::endl; 72 | return mask; 73 | } 74 | -------------------------------------------------------------------------------- /include/colorRectify.cpp: -------------------------------------------------------------------------------- 1 | // to figue out the lowerb and upperb in colorExtractor 2 | #include "plate.h" 3 | #include 4 | using namespace std; 5 | 6 | int minHue(0), maxHue(180),minSat(0),maxSat(255),minVal(0),maxVal(255); 7 | char msg[10]; 8 | cv::Scalar lowerb(minHue,minSat,minVal), 9 | upperb(maxHue,maxSat,maxVal); 10 | cv::Mat img = cv::imread("/home/tau/Pictures/chepai.jpg"); 11 | 12 | int main() 13 | { 14 | cv::imshow("img",img); 15 | cv::Mat dst; 16 | colorExtractor(img,dst,lowerb,upperb); 17 | cv::imshow("dst",dst); 18 | 19 | 20 | void minHue_callback(int pos,void*); 21 | void maxHue_callback(int pos,void*); 22 | void minSat_callback(int pos,void*); 23 | void maxSat_callback(int pos,void*); 24 | void minVal_callback(int pos,void*); 25 | void maxVal_callback(int pos,void*); 26 | 27 | cv::createTrackbar("minHue","dst",&minHue, 255,minHue_callback); 28 | cv::createTrackbar("maxHue","dst",&maxHue, 180,maxHue_callback); 29 | cv::createTrackbar("minSat","dst",&minSat, 255,minSat_callback); 30 | cv::createTrackbar("maxSat","dst",&maxSat, 255,maxSat_callback); 31 | cv::createTrackbar("minVal","dst",&minVal, 255,minVal_callback); 32 | cv::createTrackbar("maxVal","dst",&maxVal, 255,maxVal_callback); 33 | 34 | while (cv::waitKey(0) != 27); 35 | return 0; 36 | } 37 | void minHue_callback(int pos,void*){ 38 | minHue = pos; 39 | cv::Scalar lowerb(minHue,minSat,minVal), 40 | upperb(maxHue,maxSat,maxVal); 41 | cv::Mat dst; 42 | colorExtractor(img,dst,lowerb,upperb); 43 | cv::imshow("dst",dst); 44 | //sprintf(msg,"minHue = %d",minHue); 45 | //cv::displayOverlay("dst",msg); 46 | } 47 | void maxHue_callback(int pos,void*){ 48 | maxHue = pos; 49 | cv::Scalar lowerb(minHue,minSat,minVal), 50 | upperb(maxHue,maxSat,maxVal); 51 | cv::Mat dst; 52 | one.colorExtractor(img,dst,lowerb,upperb); 53 | cv::imshow("dst",dst); 54 | //sprintf(msg,"maxHue = %d",maxHue); 55 | //cv::displayOverlay("dst",msg); 56 | } 57 | void minSat_callback(int pos,void*){ 58 | minSat = pos; 59 | cv::Scalar lowerb(minHue,minSat,minVal), 60 | upperb(maxHue,maxSat,maxVal); 61 | cv::Mat dst; 62 | colorExtractor(img,dst,lowerb,upperb); 63 | cv::imshow("dst",dst); 64 | //sprintf(msg,"minHue = %d",minSat); 65 | //cv::displayOverlay("dst",msg); 66 | } 67 | void maxSat_callback(int pos,void*){ 68 | maxSat = pos; 69 | cv::Scalar lowerb(minHue,minSat,minVal), 70 | upperb(maxHue,maxSat,maxVal); 71 | cv::Mat dst; 72 | colorExtractor(img,dst,lowerb,upperb); 73 | cv::imshow("dst",dst); 74 | //sprintf(msg,"minHue = %d",maxSat); 75 | //cv::displayOverlay("dst",msg); 76 | } 77 | void minVal_callback(int pos,void*){ 78 | minVal = pos; 79 | cv::Scalar lowerb(minHue,minSat,minVal), 80 | upperb(maxHue,maxSat,maxVal); 81 | cv::Mat dst; 82 | colorExtractor(img,dst,lowerb,upperb); 83 | cv::imshow("dst",dst); 84 | //sprintf(msg,"minHue = %d",minVal); 85 | //cv::displayOverlay("dst",msg); 86 | } 87 | void maxVal_callback(int pos,void*){ 88 | maxVal = pos; 89 | cv::Scalar lowerb(minHue,minSat,minVal), 90 | upperb(maxHue,maxSat,maxVal); 91 | cv::Mat dst; 92 | colorExtractor(img,dst,lowerb,upperb); 93 | cv::imshow("dst",dst); 94 | //sprintf(msg,"minHue = %d",maxVal); 95 | //cv::displayOverlay("dst",msg); 96 | } 97 | -------------------------------------------------------------------------------- /include/harrisdetector.h: -------------------------------------------------------------------------------- 1 | /* 2 | * opencv computer vision application programming cookbook 2nd edition 3 | * example class file 4 | */ 5 | 6 | #ifndef HARRISDETECTOR_H 7 | #define HARRISDETECTOR_H 8 | 9 | #include "harrisdetector.h" 10 | #include 11 | class HarrisDetector 12 | { 13 | private: 14 | // 32-bit float image of corner strength 15 | cv::Mat cornerStrength; 16 | // 32-bit float image of thresholded corners 17 | cv::Mat cornerTh; 18 | // image of local maxima (internal) 19 | cv::Mat localMax; 20 | // size of neighborhood for derivatives smoothing 21 | int neighbourhood; 22 | // aperture for gradient computation 23 | int aperture; 24 | // Harris parameter 25 | double k; 26 | // maximum strength for threshold computation 27 | double maxStrength; 28 | // calculated threshold (internal) 29 | double threshold; 30 | // size of neighborhood for non-max suppression 31 | int nonMaxSize; 32 | // kernel for non-max suppression 33 | cv::Mat kernel; 34 | public: 35 | HarrisDetector() : neighbourhood(3), aperture(3), k(0.01), maxStrength(0.0), 36 | threshold(0.01),nonMaxSize(3) 37 | { 38 | // create kernel used in non-maxima suppression 39 | setLocalMaxWindowSize(nonMaxSize); 40 | } 41 | void setLocalMaxWindowSize(int size) 42 | { 43 | nonMaxSize = size; 44 | kernel.create(nonMaxSize, nonMaxSize, CV_8U); 45 | } 46 | // Compute Harris corners 47 | void detect(const cv::Mat& image) 48 | { 49 | // Harris computation 50 | cv::cornerHarris(image,cornerStrength,neighbourhood,aperture, k); 51 | // neighborhood size 52 | // aperture size 53 | // Harris parameter 54 | // internal threshold computation 55 | cv::minMaxLoc(cornerStrength,0,&maxStrength);//Finds the global minimum and maximum in an array. 56 | // local maxima detection 57 | 58 | cv::Mat dilated; // temporary image 59 | cv::dilate(cornerStrength,dilated,cv::Mat()); // 膨胀If element=Mat(), a 3times 3 rectangular structuring element is used 60 | cv::compare(cornerStrength,dilated,localMax,cv::CMP_EQ); 61 | } 62 | 63 | // Get the corner map from the computed Harris values 64 | cv::Mat getCornerMap(double qualityLevel) 65 | { 66 | cv::Mat cornerMap; 67 | // thresholding the corner strength 68 | threshold= qualityLevel*maxStrength; 69 | cv::threshold(cornerStrength,cornerTh,threshold,255,cv::THRESH_BINARY);// convert to 8-bit image 70 | cornerTh.convertTo(cornerMap,CV_8U); 71 | // non-maxima suppression 72 | cv::bitwise_and(cornerMap,localMax,cornerMap);//Performs a per-element bitwise conjunction of 73 | //two matrices or of matrix and scalar. 74 | return cornerMap; 75 | } 76 | 77 | // Get the feature points from the computed Harris values 78 | void getCorners(std::vector &points,double qualityLevel) 79 | { 80 | // Get the corner map 81 | cv::Mat cornerMap= getCornerMap(qualityLevel); 82 | // Get the corners 83 | getCorners(points, cornerMap);//这里调用了下面的函数 84 | } 85 | // Get the feature points from the computed corner map 86 | void getCorners(std::vector &points,const cv::Mat& cornerMap) 87 | { 88 | // Iterate over the pixels to obtain all features 89 | for( int y = 0; y < cornerMap.rows; y++ ) 90 | { 91 | const uchar* rowPtr = cornerMap.ptr(y); 92 | for( int x = 0; x < cornerMap.cols; x++ ) 93 | { 94 | // if it is a feature point 95 | if (rowPtr[x]) 96 | { 97 | points.push_back(cv::Point(x,y)); 98 | } 99 | } 100 | } 101 | } 102 | 103 | // Draw circles at feature point locations on an image 104 | void drawOnImage(cv::Mat &image, 105 | const std::vector &points, 106 | cv::Scalar color= cv::Scalar(255,255,255), 107 | int radius=3, int thickness=1) 108 | { 109 | std::vector::const_iterator it = points.begin(); 110 | // for all corners 111 | while (it!=points.end()) 112 | { 113 | // draw a circle at each corner location 114 | cv::circle(image,*it,radius,color,thickness); 115 | ++it; 116 | } 117 | } 118 | 119 | 120 | 121 | }; 122 | 123 | 124 | 125 | 126 | 127 | 128 | #endif // HARRISDETECTOR_H 129 | -------------------------------------------------------------------------------- /include/houghPeak.h: -------------------------------------------------------------------------------- 1 | // Find the most voted line by houghLines 2 | #ifndef HOUGHLINESPEAK_H 3 | #define HOUGHLINESPEAK_H 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | struct LinePolar 10 | { 11 | float rho; 12 | float angle; 13 | }; 14 | 15 | void HoughLinesPeak( LinePolar& linepolar, 16 | std::vector linepoint,cv::Size size, 17 | float rho, float theta, 18 | double min_theta, double max_theta ) 19 | { 20 | float irho = 1 / rho; 21 | 22 | int width = size.width; 23 | int height = size.height; 24 | 25 | 26 | if (max_theta < min_theta ) { 27 | CV_Error( CV_StsBadArg, "max_theta must be greater than min_theta" ); 28 | } 29 | int numangle = cvRound((max_theta - min_theta) / theta); 30 | int numrho = cvRound(((width + height) * 2 + 1) / rho); 31 | 32 | cv::AutoBuffer _accum((numangle+2) * (numrho+2)); 33 | cv::AutoBuffer _tabSin(numangle); 34 | cv::AutoBuffer _tabCos(numangle); 35 | int *accum = _accum; 36 | float *tabSin = _tabSin, *tabCos = _tabCos; 37 | 38 | 39 | memset( accum, 0, sizeof(accum[0]) * (numangle+2) * (numrho+2) ); 40 | 41 | 42 | float ang = static_cast(min_theta); 43 | for(int n = 0; n < numangle; ang += theta, n++ ) 44 | { 45 | tabSin[n] = (float)(sin((double)ang) * irho); 46 | tabCos[n] = (float)(cos((double)ang) * irho); 47 | } 48 | 49 | 50 | // stage 1. fill accumulator 51 | for(auto i = linepoint.begin(); i != linepoint.end(); ++i){ 52 | for(int n = 0; n < numangle; n++ ){ 53 | int r = cvRound( (*i).x * tabCos[n] + (*i).y * tabSin[n] ); // ρ = x cos θ + y sin θ 54 | r += (numrho - 1) / 2; 55 | accum[(n+1) * (numrho+2) + r+1]++; 56 | } 57 | } 58 | 59 | // stage 2. finding peak 60 | int peak = 0 , rpeak = 0, npeak = 0; 61 | 62 | for(int r = 0; r < numrho; r++ ) 63 | for(int n = 0; n < numangle; n++ ){ 64 | int base = (n+1) * (numrho+2) + r+1; 65 | 66 | if( accum[base] > peak ) 67 | peak = accum[base]; 68 | rpeak = r; 69 | npeak = n; 70 | } 71 | } 72 | linepolar.rho = (rpeak - (numrho - 1)*0.5f) * rho; 73 | linepolar.angle = static_cast(min_theta) + npeak * theta; 74 | } 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /include/ransacLines.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 试了好多次,itmax 要达到 1000000 次以上才有比较好的结果.. 还以为程序出了毛病debug了一晚上.. 3 | * 这几天试着看能不能优化一下 4 | * ------------------2016. 5.17 23:55 5 | * 按照http://blog.csdn.net/traumland/article/details/51284129 提供的计算迭代次数k的方法 6 | * 需要大概 143数量集的 迭代次数 7 | * 1百万次都算少的了 8 | * 但是按照这个说法, ransac就不能用了? 他们是如何优化的? 9 | * -----------------2016. 5.18 00:40 10 | * 搞错了 , 应该是40次左右就能达到99%, 还是程序本身的问题, 难道是rand()的伪随机? 11 | * ----------------2016. 5.19 16:00 12 | *使用了c++11 中的随机函数, 对于非指定边数的不规则形状(内凹, 很多杂乱的边缘)的点效果较差, 因为这个函数本身就只是用来识别直线特征的 13 | * 40次可以达到效果, 1000次基本符合我的预期 14 | * ----------------2016. 5.20 12.33 15 | *-------------------------------------------------------------------------------------- 16 | * 注意: 17 | *最后一步没有用确定出inlier的点进行重新拟合,有的算法书会用到这一点. 如果需要,加个fitline函数就行了,或者自己去写,也很简单 18 | */ 19 | 20 | #ifndef RANSACLINES_H 21 | #define RANSACLINES_H 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | 30 | //distance -- max distance to the random line for voting 31 | //ngon -- n-gon to be detected 32 | //itmax -- max iteration times 33 | void ransacLines(std::vector& input,std::vector& lines, 34 | double distance , unsigned int ngon,unsigned int itmax ){ 35 | 36 | if(!input.empty()) 37 | for(int i = 0; i < ngon; ++i){ 38 | unsigned int Mmax = 0; 39 | cv::Point imax; 40 | cv::Point jmax; 41 | cv::Vec4d line; 42 | size_t t1 , t2; 43 | 44 | std::random_device rd; // only used once to initialise (seed) engine 45 | std::mt19937 rng(rd()); // random-number engine used (Mersenne-Twister in this case) 46 | std::uniform_int_distribution uni(0,input.size()-1); // guaranteed unbiased // 概率相同 47 | 48 | unsigned int it = itmax; 49 | while(--it){ 50 | t1 = uni(rng); 51 | t2 = uni(rng); 52 | t2 = (t1 == t2 ? uni(rng): t2); 53 | unsigned int M = 0; 54 | cv::Point i = input[t1]; 55 | cv::Point j = input[t2]; 56 | for(auto a : input){ 57 | double dis = fabs((j.x - i.x)*(a.y - i.y) - (j.y - i.y)*(a.x - i.x)) / 58 | sqrt((j.x - i.x)*(j.x - i.x) + (j.y - i.y)*(j.y - i.y)); 59 | 60 | if( dis < distance) 61 | ++M; 62 | } 63 | if(M > Mmax ){ 64 | Mmax = M; 65 | imax = i; 66 | jmax = j; 67 | } 68 | } 69 | line[0] = imax.x; 70 | line[1] = imax.y; 71 | line[2] = jmax.x; 72 | line[3] = jmax.y; 73 | lines.push_back(line); 74 | auto iter = input.begin(); 75 | while(iter != input.end()){ 76 | double dis = fabs((jmax.x - imax.x)*((*iter).y - imax.y) - 77 | (jmax.y - imax.y)*((*iter).x - imax.x)) 78 | / sqrt((jmax.x - imax.x)*(jmax.x - imax.x) 79 | + (jmax.y - imax.y)*(jmax.y - imax.y)); 80 | if(dis < distance) 81 | iter = input.erase(iter); //erase the dis within , then point to 82 | // the next element 83 | else ++iter; 84 | } 85 | } 86 | else std::cout << "no input to ransacLines" << std::endl; 87 | } 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /lazer-light/Item-lists.md: -------------------------------------------------------------------------------- 1 | Item lists 2 | 3 | 两个摄像头,..第一眼,.....两个淘宝20包邮,..............很不好用 4 | 5 | 点、线、十字激光,.........淘宝一元到十几元不等..............亮了 6 | 7 | 8 | 联想笔记本T6600,3GB ddr3,仅有两个usb接口....................由于同一host usb带宽不足,两个摄像头不能同时使用(xp与ubuntu) 9 | 10 | 945台式机 E2180,2GB ddr2 ....................卡顿严重,原因可能是驱动,也许是平台太老了,摄像头同样卡顿严重(在xp和ubuntu都严重卡顿) 11 | 12 | 手里有个八、九年前的十元摄像头,很流畅,问题应该是出在摄像头,试过和新的混合使用,成像上差太多没有继续 13 | 14 | 试遍了谷歌前四页所提的方法,甚至试图去改驱动源码(没编译成功),不如买对新的更方便些,但是时间有限,没有继续下去 15 | 16 | 关键字:libv4l2: error turning on stream: No space left on device 17 | 18 | VIDIOC_STREAMON: No space left on device 19 | 20 | 21 | 台式机 22 | 23 | tau@tau-MS-7236:~$ lsusb -t 24 | 25 | /: Bus 04.Port 1: Dev 1, Class=root_hub, Driver=uhci_hcd/2p, 12M 26 | 27 | /: Bus 03.Port 1: Dev 1, Class=root_hub, Driver=uhci_hcd/2p, 12M 28 | 29 | /: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=uhci_hcd/2p, 12M 30 | 31 | |__ Port 1: Dev 2, If 0, Class=Video, Driver=uvcvideo, 12M 32 | 33 | |__ Port 1: Dev 2, If 1, Class=Video, Driver=uvcvideo, 12M 34 | 35 | |__ Port 1: Dev 2, If 2, Class=Audio, Driver=snd-usb-audio, 12M 36 | 37 | |__ Port 1: Dev 2, If 3, Class=Audio, Driver=snd-usb-audio, 12M 38 | 39 | /: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=uhci_hcd/2p, 12M 40 | 41 | |__ Port 1: Dev 2, If 0, Class=Video, Driver=uvcvideo, 12M 42 | 43 | |__ Port 1: Dev 2, If 1, Class=Video, Driver=uvcvideo, 12M 44 | 45 | |__ Port 1: Dev 2, If 2, Class=Audio, Driver=snd-usb-audio, 12M 46 | 47 | |__ Port 1: Dev 2, If 3, Class=Audio, Driver=snd-usb-audio, 12M 48 | 49 | 笔记本 50 | 51 | tau@tau:~/Documents/kernel-git$ lsusb -t 52 | 53 | /: Bus 08.Port 1: Dev 1, Class=root_hub, Driver=uhci_hcd/2p, 12M 54 | 55 | /: Bus 07.Port 1: Dev 1, Class=root_hub, Driver=uhci_hcd/2p, 12M 56 | 57 | /: Bus 06.Port 1: Dev 1, Class=root_hub, Driver=uhci_hcd/2p, 12M 58 | 59 | /: Bus 05.Port 1: Dev 1, Class=root_hub, Driver=uhci_hcd/2p, 12M 60 | 61 | /: Bus 04.Port 1: Dev 1, Class=root_hub, Driver=uhci_hcd/2p, 12M 62 | 63 | /: Bus 03.Port 1: Dev 1, Class=root_hub, Driver=uhci_hcd/2p, 12M 64 | 65 | /: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=ehci-pci/6p, 480M 66 | 67 | |__ Port 1: Dev 5, If 0, Class=Video, Driver=uvcvideo, 480M 68 | 69 | |__ Port 1: Dev 5, If 1, Class=Video, Driver=uvcvideo, 480M 70 | 71 | |__ Port 1: Dev 5, If 2, Class=Audio, Driver=snd-usb-audio, 480M 72 | 73 | |__ Port 1: Dev 5, If 3, Class=Audio, Driver=snd-usb-audio, 480M 74 | 75 | |__ Port 3: Dev 3, If 0, Class=Video, Driver=uvcvideo, 480M 76 | 77 | |__ Port 3: Dev 3, If 1, Class=Video, Driver=uvcvideo, 480M 78 | 79 | |__ Port 3: Dev 3, If 2, Class=Audio, Driver=snd-usb-audio, 480M 80 | 81 | |__ Port 3: Dev 3, If 3, Class=Audio, Driver=snd-usb-audio, 480M 82 | 83 | /: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=ehci-pci/6p, 480M 84 | 85 | -------------------------------------------------------------------------------- /lazer-light/README.md: -------------------------------------------------------------------------------- 1 | 由于购买的摄像头不合适, 没办法及时调试 (具体信息详见Itemlist) 2 | 3 | 哪怕弄不到源码, 只要有相关录像和相机参数这个项目也能继续下去. 4 | 5 | 原计划自己照书上算法写出程序暂时搁置. 6 | 7 | 查遍了opencv的module, 包括额外的包opencv_contrib, 没有关于线激光扫描的模块以及例程. 8 | 9 | 目前Google不到相关的源码, 而线激光扫描到现在仍是重要的应用, 是因为太简单用不到opencv还是商业用途不能开源? 10 | 11 | 12 | 13 | 主要的流程 14 | 15 | 捕获图像->灰度化->滤波降噪->二值化->立体校正->找光线重心(算数或加权平均值) 16 | 17 | ->triangulatePoints->得到点云->保存到文件 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /lazer-light/calibration/README.md: -------------------------------------------------------------------------------- 1 | According to YuhuaZou's experiment, calibration in opencv is less accurate than in Matlab 2 | 3 | So all the camera internal and external parameters are determined by Matlab toolbox Calib 4 | -------------------------------------------------------------------------------- /lazer-light/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | double canny_threshold1 = 50; 7 | double canny_threshold2 = 150; 8 | double threshold_value = 200, threshold_maxval = 255, threshold_type = cv::THRESH_BINARY; 9 | int laser_strip_width_max = 20; 10 | int laser_strip_width_min = 3; 11 | 12 | void mean(cv::Mat src,std::vector& dst); 13 | void save_result(cv::Mat result, const std::string s); 14 | int main() 15 | { 16 | std::cout << "canny_threshold1=50 \n" 17 | "canny_threshold2=150 \n" 18 | "threshold_value =200,threshold_maxval=255,threshold_type=THRESH_BINARY \n" 19 | << std::endl; 20 | cv::VideoCapture camera1(0); 21 | cv::VideoCapture camera2(1); 22 | 23 | if(!camera1.isOpened()) 24 | return -1; 25 | if(!camera2.isOpened()) 26 | return -1; 27 | 28 | 29 | cv::Mat edge1; 30 | cv::Mat edge2; 31 | cv::namedWindow("edge1",1); 32 | cv::namedWindow("edge2",1); 33 | 34 | cv::Mat M1,M2,D1,D2,R,T;// 由标定参数获得,需要赋值 35 | cv::Size img_size,newimg_size; //Size of the image used for stereo calibration.需要赋值 36 | //New image resolution after rectification. 37 | 38 | cv::Rect roi1,roi2; //校正后输出矩形 39 | cv::Mat R1,R2,P1,P2,Q; //输出的旋转、投影、视差映射矩阵 40 | cv::stereoRectify( M1, D1, M2, D2, img_size, R, T, R1, R2, P1, P2, Q, 41 | cv::CALIB_ZERO_DISPARITY, -1, newimg_size, &roi1, &roi2 ); 42 | 43 | 44 | cv::Mat map11, map12, map21, map22; 45 | cv::initUndistortRectifyMap(M1, D1, R1, P1, newimg_size, CV_16SC2, map11, map12); //Computes the undistortion 46 | //and rectification transformation map. 47 | cv::initUndistortRectifyMap(M2, D2, R2, P2, newimg_size, CV_16SC2, map21, map22); 48 | 49 | 50 | 51 | for(;;) //Whether to use while or for is largely a matter of personal preference. 52 | { 53 | cv::Mat frame1, frame2; 54 | camera1 >> frame1; 55 | camera2 >> frame2; 56 | cv::cvtColor(frame1, edge1, CV_BGR2GRAY); 57 | cv::cvtColor(frame2, edge2, CV_BGR2GRAY); 58 | cv::GaussianBlur(edge1, edge1, cv::Size(7,7), 1.5, 1.5);//gaussian smoothing 59 | cv::GaussianBlur(edge2, edge2, cv::Size(7,7), 1.5, 1.5); 60 | cv::threshold(edge1, edge1, threshold_value,threshold_maxval,threshold_type); 61 | cv::threshold(edge2, edge2, threshold_value,threshold_maxval,threshold_type); 62 | cv::imshow("edge1",edge1); 63 | cv::imshow("edge2",edge2); 64 | 65 | cv::remap(edge1, edge1, map11, map12, cv::INTER_LINEAR); //双线性插补 66 | cv::remap(edge2, edge2, map21, map22, cv::INTER_LINEAR); 67 | std::vector left; 68 | std::vector right; 69 | 70 | cv::Mat result(4,left.size(),CV_32FC1); 71 | cv::Mat result3d; 72 | mean(edge1 , left); 73 | mean(edge2 , right); 74 | cv::triangulatePoints(P1,P2,left,right,result); 75 | cv::convertPointsFromHomogeneous(cv::Mat(result.t()).reshape(4, 1),result3d);//transform the 4xN array to Nx4 76 | //then convert it to Nx3 77 | save_result(result3d, "Point_Cloud.txt"); 78 | 79 | if(cv::waitKey(30)>=0) break; 80 | 81 | 82 | } 83 | 84 | } 85 | 86 | 87 | void mean(cv::Mat src,std::vector& dst) //Find laserstripe's center and convert it to array 88 | { 89 | 90 | for(int i=0;i<=src.rows;i++) //遍历灰度图像 91 | { 92 | int min=0,max=src.cols ,mid=0; 93 | for(int j=0;j<=src.cols;j++) 94 | { 95 | if (src.at(i,j) > threshold_value ) 96 | { 97 | if (j > min) 98 | { 99 | max = j; 100 | } 101 | if (j < max) 102 | { 103 | min = j; 104 | } 105 | } 106 | 107 | 108 | } 109 | 110 | if( (max-min) < laser_strip_width_max && (max-min)> laser_strip_width_min) 111 | { 112 | mid = (float) ((min+max)/2); 113 | dst.push_back(cv::Point2f(mid,(float)i)); 114 | 115 | } 116 | } 117 | 118 | } 119 | 120 | 121 | void save_result(cv::Mat result, const std::string s) 122 | { 123 | std::ofstream fstrm(s, std::ofstream::app);//if use fstream, 124 | //The following string can't be written to the file 125 | if (!fstrm) 126 | { 127 | std::cout << s <<" cannot be opened" << std::endl; 128 | return; 129 | } 130 | for(int y = 0; y < result.rows; y++) 131 | { 132 | for(int x = 0; x < result.cols; x++) 133 | { 134 | fstrm << result.at(y, x); 135 | } 136 | fstrm << ';' << std::endl; 137 | } 138 | fstrm.close(); 139 | 140 | } 141 | --------------------------------------------------------------------------------