// 阈值分割 vector<CharSegment> OCR::segment(Plate plate) { Mat input = plate.plateImg; vector<CharSegment> output; Mat thresholdImage; threshold(input, thresholdImage, 60, 255, CV_THRESH_BINARY_INV); if (DEBUG) imshow("Threshold plate", thresholdImage); Mat img_contours; thresholdImage.copyTo(img_contours); // 找到可能的车牌的轮廓 vector< vector< Point> > contours; findContours(img_contours, contours, // 检测的轮廓数组,每一个轮廓用一个point类型的vector表示 CV_RETR_EXTERNAL, // 表示只检测外轮廓 CV_CHAIN_APPROX_NONE); // 轮廓的近似办法,这里存储所有的轮廓点 // 在白色的图上画出蓝色的轮廓 cv::Mat result; thresholdImage.copyTo(result); cvtColor(result, result, CV_GRAY2RGB); cv::drawContours(result, contours, -1, // 所有的轮廓都画出 cv::Scalar(255, 0, 0), // 颜色 1); // 线粗 // 对每个轮廓检测和提取最小区域的有界矩形区域 vector<vector<Point> >::iterator itc = contours.begin(); char res[20]; int i = 0; // 若没有达到设定的宽高比要求,移去该区域 while (itc != contours.end()) { Rect mr = boundingRect(Mat(*itc)); rectangle(result, mr, Scalar(0, 255, 0)); // 裁剪图像 Mat auxRoi(thresholdImage, mr); if (verifySizes(auxRoi)) { auxRoi = preprocessChar(auxRoi); output.push_back(CharSegment(auxRoi, mr)); //保存每个字符图片 sprintf(res, "PlateNumber%d.jpg", i); i++; imwrite(res, auxRoi); rectangle(result, mr, Scalar(0, 125, 255)); } ++itc; } if (DEBUG) cout << "Num chars: " << output.size() << "\n"; if (DEBUG) imshow("SEgmented Chars", result); return output; }
void DetectRegions::part2( const cv::Mat& input, std::vector<img_Plate>& output, cv::Mat& img_threshold, const std::string& out_id ) { cv::Mat my_input; input.copyTo(my_input); //Find contours of possibles plates std::vector< std::vector< cv::Point> > contours; findContours( img_threshold, contours, // a vector of contours CV_RETR_EXTERNAL, // retrieve the external contours // CV_CHAIN_APPROX_NONE ); // all pixels of each contours CV_CHAIN_APPROX_SIMPLE ); //Start to iterate to each contour founded std::vector< std::vector<cv::Point> >::iterator itc = contours.begin(); std::vector<cv::RotatedRect> rects; cv::Mat my_input_rect; input.copyTo(my_input_rect); //Remove patch that are no inside limits of aspect ratio and area. while (itc != contours.end()) { //Create bounding rect of object cv::RotatedRect mr = minAreaRect(cv::Mat(*itc)); if (!verifySizes(mr)) { itc = contours.erase(itc); // rotated rectangle drawing cv::Point2f rect_points[4]; mr.points( rect_points ); for (int j = 0; j < 4; ++j) line( my_input_rect, rect_points[j], rect_points[ (j + 1) % 4 ], cv::Scalar(255,0,0), 1, 8 ); } else { ++itc; rects.push_back(mr); // rotated rectangle drawing cv::Point2f rect_points[4]; mr.points( rect_points ); for (int j = 0; j < 4; ++j) line( my_input_rect, rect_points[j], rect_points[ (j + 1) % 4 ], cv::Scalar(0,255,0), 2, 8 ); } } D_IMG_SAVE( my_input_rect, "img_" << out_id << "Rect.png" ); // Draw blue contours on a white image cv::Mat result; input.copyTo(result); cv::drawContours( result, contours, -1, // draw all contours cv::Scalar(255,0,0), // in blue 1 ); // with a thickness of 1 // 3 ); // with a thickness of 1 D_IMG_SAVE( result, "04_img_" << out_id << "Contours.png" ); // std::cerr << "rects.size : " << rects.size() << std::endl; std::vector<cv::Mat> Mats; for (unsigned int i = 0; i < rects.size(); ++i) { //For better rect cropping for each posible box //Make floodfill algorithm because the plate has white background //And then we can retrieve more clearly the contour box circle(result, rects[i].center, 3, cv::Scalar(0,255,0), -1); //get the min size between width and height // float minSize = ( (rects[i].size.width < rects[i].size.height) float minSize = ( (rects[i].size.width > rects[i].size.height) ? (rects[i].size.width) : (rects[i].size.height) ); minSize = minSize - minSize * 0.5; //initialize rand and get 5 points around center for floodfill algorithm srand ( time(NULL) ); //Initialize floodfill parameters and variables cv::Mat mask; mask.create(input.rows + 2, input.cols + 2, CV_8UC1); mask = cv::Scalar::all(0); int loDiff = 30; int upDiff = 30; int connectivity = 4; int newMaskVal = 255; // int NumSeeds = 100; cv::Rect ccomp; int flags = connectivity + (newMaskVal << 8) + CV_FLOODFILL_FIXED_RANGE + CV_FLOODFILL_MASK_ONLY; int max_size = rects[i].size.width * rects[i].size.height; cv::Rect b_rect = rects[i].boundingRect(); int min_x = b_rect.x; int min_y = b_rect.y; int max_x = min_x + b_rect.width; int max_y = min_y + b_rect.height; for (int local_y = min_y; local_y < max_y; local_y += 5) for (int local_x = min_x; local_x < max_x; local_x += 5) { cv::Point seed; seed.x = local_x; seed.y = local_y; if (Collision( contours[i], seed )) { cv::Mat tmp_mask; tmp_mask.create( input.rows + 2, input.cols + 2, CV_8UC1 ); tmp_mask = cv::Scalar::all(0); int area = floodFill( input, tmp_mask, seed, cv::Scalar(255,0,0), &ccomp, cv::Scalar(loDiff, loDiff, loDiff), cv::Scalar(upDiff, upDiff, upDiff), flags ); { cv::Point c( ccomp.x + ccomp.width / 2, ccomp.y + ccomp.height / 2 ); cv::Size s( ccomp.width, ccomp.height ); cv::RotatedRect tmp_rect( c, s, 0 ); // rotated rectangle drawing cv::Point2f rect_points[4]; tmp_rect.points( rect_points ); for (int j = 0; j < 4; ++j) line( my_input, rect_points[j], rect_points[ (j + 1) % 4 ], cv::Scalar(0,255,255), 1, 8 ); } bool rect_invalid = ( ccomp.x < min_x || ccomp.x > max_x || ccomp.y < min_y || ccomp.y > max_y ); cv::Point left_top( min_x, min_y ); cv::Point right_top( max_x, min_y ); cv::Point left_bottom( min_x, max_y ); cv::Point right_bottom( max_x, max_y ); if (area > max_size) { circle( result, seed, 1, cv::Scalar(255,0,0), -1 ); circle( my_input, seed, 1, cv::Scalar(255,0,0), -1 ); } else if (rect_invalid) { circle( result, seed, 1, cv::Scalar(255,0,0), -1 ); circle( my_input, seed, 1, cv::Scalar(255,0,0), -1 ); } else { circle( result, seed, 1, cv::Scalar(0,255,0), -1 ); circle( my_input, seed, 1, cv::Scalar(0,255,0), -1 ); floodFill( input, mask, seed, cv::Scalar(255,0,0), &ccomp, cv::Scalar(loDiff, loDiff, loDiff), cv::Scalar(upDiff, upDiff, upDiff), flags ); } } else { circle( result, seed, 1, cv::Scalar(255,0,0), -1 ); circle( my_input, seed, 1, cv::Scalar(255,0,0), -1 ); } } // for (int j = 0; j < NumSeeds; ++j) { // rotated rectangle drawing cv::Point2f rect_points[4]; rects[i].points( rect_points ); for (int j = 0; j < 4; ++j) line( my_input, rect_points[j], rect_points[ (j + 1) % 4 ], cv::Scalar(255,255,255), 2, 8 ); D_IMG_SAVE( mask, "img_" << out_id << "" << i << "_01_Mask.png" ); } //cvWaitKey(0); //Check new floodfill mask match for a correct patch. //Get all points detected for get Minimal rotated Rect std::vector<cv::Point> pointsInterest; cv::Mat_<uchar>::iterator itMask = mask.begin<uchar>(); cv::Mat_<uchar>::iterator end = mask.end<uchar>(); for (; itMask != end; ++itMask) if (*itMask == 255) pointsInterest.push_back(itMask.pos()); if (pointsInterest.size() < 2) continue; cv::RotatedRect minRect = minAreaRect(pointsInterest); if (verifySizes(minRect)) { // rotated rectangle drawing cv::Point2f rect_points[4]; minRect.points( rect_points ); for( int j = 0; j < 4; j++ ) line( result, rect_points[j], rect_points[(j+1)%4], cv::Scalar(0,0,255), 1, 8 ); //Get rotation matrix float r = (float)minRect.size.width / (float)minRect.size.height; float angle=minRect.angle; if (r < 1) angle = 90 + angle; cv::Mat rotmat = getRotationMatrix2D(minRect.center, angle,1); //Create and rotate image cv::Mat img_rotated; warpAffine(input, img_rotated, rotmat, input.size(), CV_INTER_CUBIC); //Crop image cv::Size rect_size = minRect.size; if (r < 1) std::swap( rect_size.width, rect_size.height ); cv::Mat img_crop; getRectSubPix(img_rotated, rect_size, minRect.center, img_crop); D_IMG_SAVE( img_crop, "img_" << out_id << "" << i << "_02_crop.png" ); cv::Mat resultResized; resultResized.create(33,144, CV_8UC3); resize(img_crop, resultResized, resultResized.size(), 0, 0, cv::INTER_CUBIC); D_IMG_SAVE( resultResized, "img_" << out_id << "" << i << "_03_resultResized.png" ); output.push_back( img_Plate( resultResized, minRect.boundingRect() ) ); // //Equalize croped image // cv::Mat grayResult; // cvtColor(resultResized, grayResult, CV_BGR2GRAY); // // blur(grayResult, grayResult, Size(3,3)); // grayResult = histeq(grayResult); // D_IMG_SAVE( grayResult, "img_" << out_id << "" << i << "_04_grayResult.png" ); // output.push_back( Plate( grayResult, minRect.boundingRect() ) ); } // if (verifySizes(minRect)) } // for (int i = 0; i < rects.size(); ++i) D_IMG_SAVE( result, "10_img_" << out_id << "Contours.png" ); D_IMG_SAVE( my_input, "11_img_" << out_id << "my_input.png" ); }
//! 字符分割与排序 int CCharsSegment::charsSegment(Mat input, vector<Mat>& resultVec) { if( !input.data ) { return -3; } //判断车牌颜色以此确认threshold方法 int plateType = getPlateType(input); cvtColor(input, input, CV_RGB2GRAY); //Threshold input image Mat img_threshold; if (1 == plateType) threshold(input, img_threshold, 10, 255, CV_THRESH_OTSU+CV_THRESH_BINARY); else threshold(input, img_threshold, 10, 255, CV_THRESH_OTSU+CV_THRESH_BINARY_INV); if(m_debug) { stringstream ss(stringstream::in | stringstream::out); ss << "image/tmp/debug_char_threshold" << ".jpg"; imwrite(ss.str(), img_threshold); } //去除车牌上方的柳钉以及下方的横线等干扰 clearLiuDing(img_threshold); if(m_debug) { stringstream ss(stringstream::in | stringstream::out); ss << "image/tmp/debug_char_clearLiuDing" << ".jpg"; imwrite(ss.str(), img_threshold); } Mat img_contours; img_threshold.copyTo(img_contours); vector< vector< Point> > contours; findContours(img_contours, contours, // a vector of contours CV_RETR_EXTERNAL, // retrieve the external contours CV_CHAIN_APPROX_NONE); // all pixels of each contours //Start to iterate to each contour founded vector<vector<Point> >::iterator itc= contours.begin(); vector<Rect> vecRect; //Remove patch that are no inside limits of aspect ratio and area. //将不符合特定尺寸的图块排除出去 while (itc != contours.end()) { Rect mr = boundingRect(Mat(*itc)); Mat auxRoi(img_threshold, mr); if (verifySizes(auxRoi)) { vecRect.push_back(mr); } ++itc; } if (vecRect.size() == 0) return -3; vector<Rect> sortedRect; //对符合尺寸的图块按照从左到右进行排序 SortRect(vecRect, sortedRect); int specIndex = 0; //获得指示城市的特定Rect,如苏A的"A" specIndex = GetSpecificRect(sortedRect); if(m_debug) { if (specIndex < sortedRect.size()) { Mat specMat(img_threshold, sortedRect[specIndex]); stringstream ss(stringstream::in | stringstream::out); ss << "image/tmp/debug_specMat" <<".jpg"; imwrite(ss.str(), specMat); } } //根据特定Rect向左反推出中文字符 //这样做的主要原因是根据findContours方法很难捕捉到中文字符的准确Rect,因此仅能 //退过特定算法来指定 Rect chineseRect; if (specIndex < sortedRect.size()) chineseRect = GetChineseRect(sortedRect[specIndex]); else return -3; if(m_debug) { Mat chineseMat(img_threshold, chineseRect); stringstream ss(stringstream::in | stringstream::out); ss << "image/tmp/debug_chineseMat" <<".jpg"; imwrite(ss.str(), chineseMat); } //新建一个全新的排序Rect //将中文字符Rect第一个加进来,因为它肯定是最左边的 //其余的Rect只按照顺序去6个,车牌只可能是7个字符!这样可以避免阴影导致的“1”字符 vector<Rect> newSortedRect; newSortedRect.push_back(chineseRect); RebuildRect(sortedRect, newSortedRect, specIndex); if (newSortedRect.size() == 0) return -3; for (int i = 0; i < newSortedRect.size(); i++) { Rect mr = newSortedRect[i]; Mat auxRoi(img_threshold, mr); if (1) { auxRoi = preprocessChar(auxRoi); if(m_debug) { stringstream ss(stringstream::in | stringstream::out); ss << "image/tmp/debug_char_auxRoi_" << i <<".jpg"; imwrite(ss.str(), auxRoi); } resultVec.push_back(auxRoi); } } return 0; }
//! 定位车牌图像 //! src 原始图像 //! resultVec 一个Mat的向量,存储所有抓取到的图像 //! 成功返回0,否则返回-1 int CPlateLocate::plateLocate(Mat src, vector<Mat>& resultVec) { Mat src_blur, src_gray; Mat grad; int scale = SOBEL_SCALE; int delta = SOBEL_DELTA; int ddepth = SOBEL_DDEPTH; if( !src.data ) { return -1; } //高斯均衡。Size中的数字影响车牌定位的效果。 GaussianBlur( src, src_blur, Size(m_GaussianBlurSize, m_GaussianBlurSize), 0, 0, BORDER_DEFAULT ); /// Convert it to gray cvtColor( src_blur, src_gray, CV_RGB2GRAY ); /// Generate grad_x and grad_y Mat grad_x, grad_y; Mat abs_grad_x, abs_grad_y; /// Gradient X //Scharr( src_gray, grad_x, ddepth, 1, 0, scale, delta, BORDER_DEFAULT ); Sobel( src_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT ); convertScaleAbs( grad_x, abs_grad_x ); /// Gradient Y //Scharr( src_gray, grad_y, ddepth, 0, 1, scale, delta, BORDER_DEFAULT ); Sobel( src_gray, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT ); convertScaleAbs( grad_y, abs_grad_y ); /// Total Gradient (approximate) addWeighted( abs_grad_x, SOBEL_X_WEIGHT, abs_grad_y, SOBEL_Y_WEIGHT, 0, grad ); Mat img_threshold; threshold(grad, img_threshold, 0, 255, CV_THRESH_OTSU+CV_THRESH_BINARY); //threshold(grad, img_threshold, 75, 255, CV_THRESH_BINARY); Mat element = getStructuringElement(MORPH_RECT, Size(m_MorphSizeWidth, m_MorphSizeHeight) ); morphologyEx(img_threshold, img_threshold, CV_MOP_CLOSE, element); //Find 轮廓 of possibles plates vector< vector< Point> > contours; findContours(img_threshold, contours, // a vector of contours CV_RETR_EXTERNAL, // 提取外部轮廓 CV_CHAIN_APPROX_NONE); // all pixels of each contours //Start to iterate to each contour founded vector<vector<Point> >::iterator itc = contours.begin(); vector<RotatedRect> rects; //Remove patch that are no inside limits of aspect ratio and area. int t = 0; while (itc != contours.end()) { //Create bounding rect of object RotatedRect mr = minAreaRect(Mat(*itc)); //large the rect for more if( !verifySizes(mr)) { itc = contours.erase(itc); } else { ++itc; rects.push_back(mr); } } for(int i=0; i< rects.size(); i++) { RotatedRect minRect = rects[i]; if(verifySizes(minRect)) { // rotated rectangle drawing // Get rotation matrix // 旋转这部分代码确实可以将某些倾斜的车牌调整正, // 但是它也会误将更多正的车牌搞成倾斜!所以综合考虑,还是不使用这段代码。 // 2014-08-14,由于新到的一批图片中发现有很多车牌是倾斜的,因此决定再次尝试 // 这段代码。 float r = (float)minRect.size.width / (float)minRect.size.height; float angle = minRect.angle; Size rect_size = minRect.size; if (r < 1) { angle = 90 + angle; swap(rect_size.width, rect_size.height); } //如果抓取的方块旋转超过m_angle角度,则不是车牌,放弃处理 if (angle - m_angle < 0 && angle + m_angle > 0) { //Create and rotate image Mat rotmat = getRotationMatrix2D(minRect.center, angle, 1); Mat img_rotated; warpAffine(src, img_rotated, rotmat, src.size(), CV_INTER_CUBIC); Mat resultMat; resultMat = showResultMat(img_rotated, rect_size, minRect.center); resultVec.push_back(resultMat); } } } return 0; }
void RegionDetector::getMask() { //find contours vector <vector<Point> > contours; cv::findContours(img_temp,contours, CV_RETR_EXTERNAL, //retrive external contours CV_CHAIN_APPROX_NONE); // all pixels of each contour //extract rectangle of minimal area //Start to iterate to each contour found vector <vector<Point> >::iterator itc = contours.begin(); vector <RotatedRect> rects; //Remove patch that has no inside limits of aspect ratio and area while (itc!=contours.end()) { //create bounding rect of object RotatedRect rr = minAreaRect(Mat(*itc)); if (!verifySizes(rr)) { itc = contours.erase(itc); } else { std::cout<<"bene"; itc++; rects.push_back(rr); } } std::cout << rects.size(); // Draw blue contours on a white image Mat result; img_temp.copyTo(result); cv::drawContours(result,contours, -1, // draw all contours cv::Scalar(255),// in blue 1); // thickness imshow("nn",result); for(int i=0; i< rects.size(); i++) { std::cout<<"true\n"; //For better rect cropping for each posible box //Make floodfill algorithm because the plate has white background //And then we can retrieve more clearly the contour box circle(result, rects[i].center, 3, Scalar(0,255,0), -1); //get the min size between width and height float minSize=(rects[i].size.width < rects[i].size.height)?rects[i].size.width:rects[i].size.height; minSize=minSize-minSize*0.5; //initialize rand and get 5 points around center for floodfill algorithm srand ( time(NULL) ); //Initialize floodfill parameters and variables Mat mask; mask.create(img_temp.rows + 2, img_temp.cols + 2, CV_8UC1); mask= Scalar::all(0); int loDiff = 30; int upDiff = 30; int connectivity = 4; int newMaskVal = 255; int NumSeeds = 10; Rect ccomp; int flags = connectivity + (newMaskVal << 8 ) + CV_FLOODFILL_FIXED_RANGE + CV_FLOODFILL_MASK_ONLY; for(int j=0; j<NumSeeds; j++) { Point seed; seed.x=rects[i].center.x+rand()%(int)minSize-(minSize/2); seed.y=rects[i].center.y+rand()%(int)minSize-(minSize/2); circle(result, seed, 1, Scalar(0,255,255), -1); // int area = floodFill(img_temp, mask, seed, Scalar(255,0,0), &ccomp, Scalar(loDiff, loDiff, loDiff), Scalar(upDiff, upDiff, upDiff), flags); } imshow("MASK", mask); } }
vector<Plate> DetectRegions::segment(Mat input){ vector<Plate> output; //convert image to gray Mat img_gray; //= *new Mat(input.size().width,input.size().height, CV_8UC1); cvtColor(input, img_gray, CV_BGR2GRAY); blur(img_gray, img_gray, Size(5,5)); //Finde vertical lines. Car plates have high density of vertical lines Mat img_sobel; Sobel(img_gray, img_sobel, CV_8U, 1, 0, 3, 1, 0, BORDER_DEFAULT); if(showSteps) imshow("Sobel", img_sobel); //threshold image Mat img_threshold; threshold(img_sobel, img_threshold, 0, 255, CV_THRESH_OTSU+CV_THRESH_BINARY); if(showSteps) imshow("Threshold", img_threshold); //Morphplogic operation close Mat element = getStructuringElement(MORPH_RECT, Size(17, 3) ); morphologyEx(img_threshold, img_threshold, CV_MOP_CLOSE, element); if(showSteps) imshow("Close", img_threshold); //Find contours of possibles plates vector< vector< Point> > contours; findContours(img_threshold, contours, // a vector of contours CV_RETR_EXTERNAL, // retrieve the external contours CV_CHAIN_APPROX_NONE); // all pixels of each contours //Start to iterate to each contour founded vector<vector<Point> >::iterator itc= contours.begin(); vector<RotatedRect> rects; //Remove patch that are no inside limits of aspect ratio and area. while (itc!=contours.end()) { //Create bounding rect of object RotatedRect mr= minAreaRect(Mat(*itc)); if( !verifySizes(mr)){ itc= contours.erase(itc); }else{ ++itc; rects.push_back(mr); } } // Draw blue contours on a white image cv::Mat result; input.copyTo(result); cv::drawContours(result,contours, -1, // draw all contours cv::Scalar(255,0,0), // in blue 1); // with a thickness of 1 for(int i=0; i< rects.size(); i++){ //For better rect cropping for each posible box //Make floodfill algorithm because the plate has white background //And then we can retrieve more clearly the contour box circle(result, rects[i].center, 3, Scalar(0,255,0), -1); //get the min size between width and height float minSize=(rects[i].size.width < rects[i].size.height)?rects[i].size.width:rects[i].size.height; minSize=minSize-minSize*0.5; //initialize rand and get 5 points around center for floodfill algorithm srand ( time(NULL) ); //Initialize floodfill parameters and variables Mat mask; mask.create(input.rows + 2, input.cols + 2, CV_8UC1); mask= Scalar::all(0); int loDiff = 30; int upDiff = 30; int connectivity = 4; int newMaskVal = 255; int NumSeeds = 10; Rect ccomp; int flags = connectivity + (newMaskVal << 8 ) + CV_FLOODFILL_FIXED_RANGE + CV_FLOODFILL_MASK_ONLY; for(int j=0; j<NumSeeds; j++){ Point seed; seed.x=rects[i].center.x+rand()%(int)minSize-(minSize/2); seed.y=rects[i].center.y+rand()%(int)minSize-(minSize/2); circle(result, seed, 1, Scalar(0,255,255), -1); int area = floodFill(input, mask, seed, Scalar(255,0,0), &ccomp, Scalar(loDiff, loDiff, loDiff), Scalar(upDiff, upDiff, upDiff), flags); } if(showSteps) imshow("MASK", mask); //cvWaitKey(0); //Check new floodfill mask match for a correct patch. //Get all points detected for get Minimal rotated Rect vector<Point> pointsInterest; Mat_<uchar>::iterator itMask= mask.begin<uchar>(); Mat_<uchar>::iterator end= mask.end<uchar>(); for( ; itMask!=end; ++itMask) if(*itMask==255) pointsInterest.push_back(itMask.pos()); RotatedRect minRect = minAreaRect(pointsInterest); if(verifySizes(minRect)){ // rotated rectangle drawing Point2f rect_points[4]; minRect.points( rect_points ); for( int j = 0; j < 4; j++ ) line( result, rect_points[j], rect_points[(j+1)%4], Scalar(0,0,255), 1, 8 ); //Get rotation matrix float r= (float)minRect.size.width / (float)minRect.size.height; float angle=minRect.angle; if(r<1) angle=90+angle; Mat rotmat= getRotationMatrix2D(minRect.center, angle,1); //Create and rotate image Mat img_rotated; warpAffine(input, img_rotated, rotmat, input.size(), CV_INTER_CUBIC); //Crop image Size rect_size=minRect.size; if(r < 1) swap(rect_size.width, rect_size.height); Mat img_crop; getRectSubPix(img_rotated, rect_size, minRect.center, img_crop); Mat resultResized; resultResized.create(33,144, CV_8UC3); resize(img_crop, resultResized, resultResized.size(), 0, 0, INTER_CUBIC); //Equalize croped image Mat grayResult; cvtColor(resultResized, grayResult, CV_BGR2GRAY); blur(grayResult, grayResult, Size(3,3)); grayResult=histeq(grayResult); if(saveRegions){ stringstream ss(stringstream::in | stringstream::out); ss << "tmp/" << filename << "_" << i << ".jpg"; imwrite(ss.str(), grayResult); } output.push_back(Plate(grayResult,minRect.boundingRect())); } } if(showSteps) imshow("Contours", result); return output; }
//Segment the chars from plate vector<CharSegment> OCR::segment(Plate plate){ Mat input=plate.plateImg; vector<CharSegment> output; //Threshold input image Mat img_threshold; //To make char image clearly // threshold(input, img_threshold, 60, 255, CV_THRESH_BINARY_INV); //Spain // threshold(input, img_threshold, 150~160, 255, CV_THRESH_BINARY); //China // TODO: IMPORTANT threshold(input, img_threshold, 175, 255, CV_THRESH_BINARY); //China if(debug) { imshow("OCR_Threshold_Binary", img_threshold); } Mat img_contours; img_threshold.copyTo(img_contours); //Find contours of possibles characters vector< vector< Point> > contours; findContours(img_contours, contours, // a vector of contours CV_RETR_EXTERNAL, // retrieve the external contours CV_CHAIN_APPROX_NONE); // all pixels of each contours // Draw blue contours on a white image cv::Mat result; img_threshold.copyTo(result); cvtColor(result, result, CV_GRAY2RGB); cv::drawContours(result, contours, -1, // draw all contours cv::Scalar(255,0,0), // in BLUE 1); // with a thickness of 1 //Start to iterate to each contour founded vector<vector<Point> >::iterator itc = contours.begin(); //Remove patch that are no inside limits of aspect ratio and area. while (itc!=contours.end()) { //Create bounding rect of object Rect mr = boundingRect(Mat(*itc)); rectangle(result, mr, Scalar(0,255,0)); //Possible chars in GREEN //Crop image Mat auxRoi(img_threshold, mr); if(verifySizes(auxRoi)){ auxRoi=preprocessChar(auxRoi); output.push_back(CharSegment(auxRoi, mr)); rectangle(result, mr, Scalar(0,0,255)); //Possible chars in RED } ++itc; } if(debug) { cout << "OCR number of chars: " << output.size() << "\n"; imshow("OCR Chars", result); cvWaitKey(0); } return output; }