Mat CharacterAnalysis::findOuterBoxMask() { double min_parent_area = config->templateHeightPx * config->templateWidthPx * 0.10; // Needs to be at least 10% of the plate area to be considered. int winningIndex = -1; int winningParentId = -1; int bestCharCount = 0; double lowestArea = 99999999999999; if (this->config->debugCharAnalysis) cout << "CharacterAnalysis::findOuterBoxMask" << endl; for (int imgIndex = 0; imgIndex < allContours.size(); imgIndex++) { //vector<bool> charContours = filter(thresholds[imgIndex], allContours[imgIndex], allHierarchy[imgIndex]); int charsRecognized = 0; int parentId = -1; bool hasParent = false; for (int i = 0; i < charSegments[imgIndex].size(); i++) { if (charSegments[imgIndex][i]) charsRecognized++; if (charSegments[imgIndex][i] && allHierarchy[imgIndex][i][3] != -1) { parentId = allHierarchy[imgIndex][i][3]; hasParent = true; } } if (charsRecognized == 0) continue; if (hasParent) { double boxArea = contourArea(allContours[imgIndex][parentId]); if (boxArea < min_parent_area) continue; if ((charsRecognized > bestCharCount) || (charsRecognized == bestCharCount && boxArea < lowestArea)) //(boxArea < lowestArea) { bestCharCount = charsRecognized; winningIndex = imgIndex; winningParentId = parentId; lowestArea = boxArea; } } } if (this->config->debugCharAnalysis) cout << "Winning image index is: " << winningIndex << endl; if (winningIndex != -1 && bestCharCount >= 3) { int longestChildIndex = -1; double longestChildLength = 0; // Find the child with the longest permiter/arc length ( just for kicks) for (int i = 0; i < allContours[winningIndex].size(); i++) { for (int j = 0; j < allContours[winningIndex].size(); j++) { if (allHierarchy[winningIndex][j][3] == winningParentId) { double arclength = arcLength(allContours[winningIndex][j], false); if (arclength > longestChildLength) { longestChildIndex = j; longestChildLength = arclength; } } } } Mat mask = Mat::zeros(thresholds[winningIndex].size(), CV_8U); // get rid of the outline by drawing a 1 pixel width black line drawContours(mask, allContours[winningIndex], winningParentId, // draw this contour cv::Scalar(255,255,255), // in CV_FILLED, 8, allHierarchy[winningIndex], 0 ); // Morph Open the mask to get rid of any little connectors to non-plate portions int morph_elem = 2; int morph_size = 3; Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) ); //morphologyEx( mask, mask, MORPH_CLOSE, element ); morphologyEx( mask, mask, MORPH_OPEN, element ); //morph_size = 1; //element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) ); //dilate(mask, mask, element); // Drawing the edge black effectively erodes the image. This may clip off some extra junk from the edges. // We'll want to do the contour again and find the larges one so that we remove the clipped portion. vector<vector<Point> > contoursSecondRound; findContours(mask, contoursSecondRound, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); int biggestContourIndex = -1; double largestArea = 0; for (int c = 0; c < contoursSecondRound.size(); c++) { double area = contourArea(contoursSecondRound[c]); if (area > largestArea) { biggestContourIndex = c; largestArea = area; } } if (biggestContourIndex != -1) { mask = Mat::zeros(thresholds[winningIndex].size(), CV_8U); vector<Point> smoothedMaskPoints; approxPolyDP(contoursSecondRound[biggestContourIndex], smoothedMaskPoints, 2, true); vector<vector<Point> > tempvec; tempvec.push_back(smoothedMaskPoints); //fillPoly(mask, smoothedMaskPoints.data(), smoothedMaskPoints, Scalar(255,255,255)); drawContours(mask, tempvec, 0, // draw this contour cv::Scalar(255,255,255), // in CV_FILLED, 8, allHierarchy[winningIndex], 0 ); } if (this->config->debugCharAnalysis) { vector<Mat> debugImgs; Mat debugImgMasked = Mat::zeros(thresholds[winningIndex].size(), CV_8U); thresholds[winningIndex].copyTo(debugImgMasked, mask); debugImgs.push_back(mask); debugImgs.push_back(thresholds[winningIndex]); debugImgs.push_back(debugImgMasked); Mat dashboard = drawImageDashboard(debugImgs, CV_8U, 1); displayImage(config, "Winning outer box", dashboard); } hasPlateMask = true; return mask; } hasPlateMask = false; Mat fullMask = Mat::zeros(thresholds[0].size(), CV_8U); bitwise_not(fullMask, fullMask); return fullMask; }
bool LicensePlate::IsPossibleLPUsingCharFeature(Rect LPRect) { Mat ROI = LPRedChannel(LPRect).clone(); medianBlur(ROI, ROI, 3); threshold(ROI, ROI, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU); Mat RR = ROI.clone(); Mat HClose = getStructuringElement(MORPH_CROSS, Size(45, 1)); morphologyEx(RR, RR, MORPH_CLOSE, HClose); vector<int> ar; vector<Rect> rt; GetComponent(RR, ar, rt); int maxRectArea = 0, p = -1; for(int i = 0; i < ar.size(); i++) { if(maxRectArea < ar[i]) { maxRectArea = ar[i]; p = i; } } assert(p != -1); LPRect.x += rt[p].x; LPRect.y += rt[p].y; LPRect.width = rt[p].width; LPRect.height = rt[p].height; ROI = ROI(rt[p]); int xHist[MAXC]; memset(xHist, 0, sizeof(xHist)); for(int r = 0; r < ROI.rows; r++) for(int c = 0; c < ROI.cols; c++) { if(ROI.at<uchar>(r, c)) xHist[c]++; } int N = ROI.cols; int rk[MAXC]; memcpy(rk, xHist, sizeof(rk)); sort(rk, rk + N); int bottomThreshold = 0; int topThreshold = 0; int percent = N / 3; for(int i = 0; i < percent; i++) { bottomThreshold += rk[i]; topThreshold += rk[N - i - 1]; } bottomThreshold /= percent; topThreshold /= percent; bottomThreshold = max(topThreshold / 10, bottomThreshold); bool isTop[MAXC]; for(int i = 0; i < N; i++) { isTop[i] = (xHist[i] >= bottomThreshold); } Mat hist(1, ROI.cols, CV_8UC1); vector<pair<int, int> > interval, unionInterval; for(int i = 0; i < ROI.cols; i++) { hist.at<uchar>(0, i) = isTop[i]; if(isTop[i]) interval.push_back(make_pair(i, i + 1)); } int L, R = -100; for(int i = 0; i < interval.size(); i++) { int l = interval[i].first; int r = interval[i].second; if(l - R < 5) { R = r; } else { if(R != -100) unionInterval.push_back(make_pair(L, R)); L = l, R = r; } } if(R != -100) unionInterval.push_back(make_pair(L, R)); if(unionInterval.size() < 3 || unionInterval.size() > 10) return 0; return 1; }
vector<Plate> DetectRegions::segment(Mat input){ vector<Plate> output; //convert image to gray Mat img_gray; 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< (int)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); 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 << ".\\output\\possible_plates\\" << filename << "_" << i << ".JPG"; imwrite(ss.str(), grayResult); } output.push_back(Plate(grayResult,minRect.boundingRect())); } } if(showSteps) imshow("Contours", result); return output; }