RotatedRect::RotatedRect(const Point2f& _point1, const Point2f& _point2, const Point2f& _point3) { Point2f _center = 0.5f * (_point1 + _point3); Vec2f vecs[2]; vecs[0] = Vec2f(_point1 - _point2); vecs[1] = Vec2f(_point2 - _point3); double x = std::max(norm(_point1), std::max(norm(_point2), norm(_point3))); double a = std::min(norm(vecs[0]), norm(vecs[1])); // check that given sides are perpendicular CV_Assert( std::fabs(vecs[0].ddot(vecs[1])) * a <= FLT_EPSILON * 9 * x * (norm(vecs[0]) * norm(vecs[1])) ); // wd_i stores which vector (0,1) or (1,2) will make the width // One of them will definitely have slope within -1 to 1 int wd_i = 0; if( std::fabs(vecs[1][1]) < std::fabs(vecs[1][0]) ) wd_i = 1; int ht_i = (wd_i + 1) % 2; float _angle = std::atan(vecs[wd_i][1] / vecs[wd_i][0]) * 180.0f / (float) CV_PI; float _width = (float) norm(vecs[wd_i]); float _height = (float) norm(vecs[ht_i]); center = _center; size = Size2f(_width, _height); angle = _angle; }
void Label::DoLayout(Rect boundaryRectangle) { LOGD(LOGTAG_INPUT,"Adding myself(Label) to layout. Rect = (%d,%d,%d,%d)",boundaryRectangle.x,boundaryRectangle.y,boundaryRectangle.width,boundaryRectangle.height); if (scaleToFit) { FitTextToBoundary(Size2f(boundaryRectangle.width,boundaryRectangle.height)); } Point2i newPoint = Point2i( boundaryRectangle.x, boundaryRectangle.y); if (centerX || centerY) { if (centerY) newPoint.y = boundaryRectangle.y + boundaryRectangle.height/2; if (centerX) newPoint.x = boundaryRectangle.x + boundaryRectangle.width/2; SetCenter(newPoint); } else { Position = newPoint; } }
vector<RotatedRect> CControl::GetRotatedRect(Point2i n_ptcenter) { float n_degreestep; if(nControlOptions.nAngelCout<10) n_degreestep=180/nControlOptions.nAngelCout; else n_degreestep=360/nControlOptions.nAngelCout; vector<RotatedRect> n_rotatedrectvct; for(int i=0;i<nControlOptions.nAngelCout;i++) { float n_degree=i*n_degreestep; RotatedRect n_rotatedrect(Point2f(n_ptcenter.x,n_ptcenter.y),Size2f(nControlOptions.nWidth,nControlOptions.nHeight),n_degree); //检查角点 Point2f n_vertices[4]; n_rotatedrect.points(n_vertices); bool n_inside=true; for(int j=0;j<4;j++) if(!CheckBoundary(n_vertices[j])) n_inside=false; //检查外包矩形是否在图像内(旋转需要) Rect n_boundrect=n_rotatedrect.boundingRect(); Point2f n_pttl=n_boundrect.tl(); Point2f n_ptbr=n_boundrect.br(); if(!CheckBoundary(n_pttl)||!CheckBoundary(n_ptbr)) n_inside=false; if(!n_inside) continue; n_rotatedrectvct.push_back(n_rotatedrect); } return n_rotatedrectvct; }
vector<RotatedRect> CControl::GetRotatedRect(Point2i n_ptcenter,vector<float> n_anglevct) { vector<RotatedRect> n_rotatedrectvct; for(int i=0;i<n_anglevct.size();i++) { float n_degree=n_anglevct.at(i); RotatedRect n_rotatedrect(Point2f(n_ptcenter.x,n_ptcenter.y),Size2f(nControlOptions.nWidth,nControlOptions.nHeight),n_degree); //检查角点 Point2f n_vertices[4]; n_rotatedrect.points(n_vertices); bool n_inside=true; for(int j=0;j<4;j++) if(!CheckBoundary(n_vertices[j])) n_inside=false; /* //检查外包矩形是否在图像内(旋转需要) Rect n_boundrect=n_rotatedrect.boundingRect(); Point2f n_pttl=n_boundrect.tl(); Point2f n_ptbr=n_boundrect.br(); if(!CheckBoundary(n_pttl)||!CheckBoundary(n_ptbr)) n_inside=false; */ if(!n_inside) continue; n_rotatedrectvct.push_back(n_rotatedrect); } return n_rotatedrectvct; }
RotatedRect CamShift( const Mat& probImage, Rect& window, TermCriteria criteria ) { CvConnectedComp comp; CvBox2D box; CvMat _probImage = probImage; cvCamShift(&_probImage, window, (CvTermCriteria)criteria, &comp, &box); window = comp.rect; return RotatedRect(Point2f(box.center), Size2f(box.size), box.angle); }
vector<Rect> CharacterSegmenter::combineCloseBoxes( vector<Rect> charBoxes, float biggestCharWidth) { vector<Rect> newCharBoxes; for (int i = 0; i < charBoxes.size(); i++) { if (i == charBoxes.size() - 1) { newCharBoxes.push_back(charBoxes[i]); break; } float bigWidth = (charBoxes[i + 1].x + charBoxes[i + 1].width - charBoxes[i].x); float w1Diff = abs(charBoxes[i].width - biggestCharWidth); float w2Diff = abs(charBoxes[i + 1].width - biggestCharWidth); float bigDiff = abs(bigWidth - biggestCharWidth); bigDiff *= 1.3; // Make it a little harder to merge boxes. if (bigDiff < w1Diff && bigDiff < w2Diff) { Rect bigRect(charBoxes[i].x, charBoxes[i].y, bigWidth, charBoxes[i].height); newCharBoxes.push_back(bigRect); if (this->config->debugCharSegmenter) { for (int z = 0; z < charAnalysis->thresholds.size(); z++) { Point center(bigRect.x + bigRect.width / 2, bigRect.y + bigRect.height / 2); RotatedRect rrect(center, Size2f(bigRect.width, bigRect.height + (bigRect.height / 2)), 0); ellipse(imgDbgCleanStages[z], rrect, Scalar(0,255,0), 1); } cout << "Merging 2 boxes -- " << i << " and " << i + 1 << endl; } i++; } else { newCharBoxes.push_back(charBoxes[i]); } } return newCharBoxes; }
int getGroundTruth(map<string, vector<CPlate>>& xmlMap, const char* path) { #ifdef OS_WINDOWS XMLNode::setGlobalOptions(XMLNode::char_encoding_GBK); #endif XMLNode xMainNode = XMLNode::openFileHelper(path, "tagset"); int n = xMainNode.nChildNode("image"); // this prints the "coefficient" value for all the "NumericPredictor" tags: for (int i = 0; i < n; i++) { XMLNode imageNode = xMainNode.getChildNode("image", i); string imageName = imageNode.getChildNode("imageName").getText(); vector<CPlate> plateVec; int m = imageNode.getChildNode("taggedRectangles").nChildNode("taggedRectangle"); for (int j = 0; j < m; j++) { XMLNode plateNode = imageNode.getChildNode("taggedRectangles").getChildNode("taggedRectangle", j); int x = atoi(plateNode.getAttribute("x")); int y = atoi(plateNode.getAttribute("y")); int width = atoi(plateNode.getAttribute("width")); int height = atoi(plateNode.getAttribute("height")); int angle = atoi(plateNode.getAttribute("rotation")); string plateStr = plateNode.getText(); RotatedRect rr(Point2f(float(x), float(y)), Size2f(float(width), float(height)), angle); CPlate plate; plate.setPlateStr(plateStr); plate.setPlatePos(rr); plateVec.push_back(plate); } xmlMap[imageName] = plateVec; } return 0; }
void FindObjectMain::process_camshift() { // Some user defined parameters int vmin = config.vmin; int vmax = config.vmax; int smin = config.smin; float hranges[] = { 0, 180 }; const float* phranges = hranges; // Create aligned, RGB images if(!object_image) { object_image = cvCreateImage( cvSize(object_image_w, object_image_h), 8, 3); } if(!scene_image) { scene_image = cvCreateImage( cvSize(scene_image_w, scene_image_h), 8, 3); } // Temporary row pointers unsigned char **object_rows = new unsigned char*[object_image_h]; unsigned char **scene_rows = new unsigned char*[scene_image_h]; for(int i = 0; i < object_image_h; i++) { object_rows[i] = (unsigned char*)(object_image->imageData + i * object_image_w * 3); } for(int i = 0; i < scene_image_h; i++) { scene_rows[i] = (unsigned char*)(scene_image->imageData + i * scene_image_w * 3); } // Transfer object & scene to RGB images for OpenCV if(!prev_object) prev_object = new unsigned char[object_image_w * object_image_h * 3]; // Back up old object image memcpy(prev_object, object_image->imageData, object_image_w * object_image_h * 3); BC_CModels::transfer(object_rows, get_input(object_layer)->get_rows(), 0, 0, 0, 0, 0, 0, object_x1, object_y1, object_w, object_h, 0, 0, object_w, object_h, get_input(object_layer)->get_color_model(), BC_RGB888, 0, 0, 0); BC_CModels::transfer(scene_rows, get_input(scene_layer)->get_rows(), 0, 0, 0, 0, 0, 0, scene_x1, scene_y1, scene_w, scene_h, 0, 0, scene_w, scene_h, get_input(scene_layer)->get_color_model(), BC_RGB888, 0, 0, 0); delete [] object_rows; delete [] scene_rows; // from camshiftdemo.cpp // Compute new object if(memcmp(prev_object, object_image->imageData, object_image_w * object_image_h * 3) || !hist.dims) { Mat image(object_image); Mat hsv, hue, mask; cvtColor(image, hsv, CV_RGB2HSV); int _vmin = vmin, _vmax = vmax; //printf("FindObjectMain::process_camshift %d\n", __LINE__); inRange(hsv, Scalar(0, smin, MIN(_vmin,_vmax)), Scalar(180, 256, MAX(_vmin, _vmax)), mask); int ch[] = { 0, 0 }; hue.create(hsv.size(), hsv.depth()); mixChannels(&hsv, 1, &hue, 1, ch, 1); Rect selection = Rect(0, 0, object_w, object_h); trackWindow = selection; int hsize = 16; Mat roi(hue, selection), maskroi(mask, selection); calcHist(&roi, 1, 0, maskroi, hist, 1, &hsize, &phranges); normalize(hist, hist, 0, 255, CV_MINMAX); } // compute scene Mat image(scene_image); Mat hsv, hue, mask, backproj; cvtColor(image, hsv, CV_RGB2HSV); int _vmin = vmin, _vmax = vmax; inRange(hsv, Scalar(0, smin, MIN(_vmin,_vmax)), Scalar(180, 256, MAX(_vmin, _vmax)), mask); int ch[] = {0, 0}; hue.create(hsv.size(), hsv.depth()); mixChannels(&hsv, 1, &hue, 1, ch, 1); //printf("FindObjectMain::process_camshift %d %d %d\n", __LINE__, hist.dims, hist.size[1]); RotatedRect trackBox = RotatedRect( Point2f((object_x1 + object_x2) / 2, (object_y1 + object_y2) / 2), Size2f(object_w, object_h), 0); trackWindow = Rect(0, 0, scene_w, scene_h); if(hist.dims > 0) { calcBackProject(&hue, 1, 0, hist, backproj, &phranges); backproj &= mask; //printf("FindObjectMain::process_camshift %d\n", __LINE__); // if(trackWindow.width <= 0 || // trackWindow.height <= 0) // { // trackWindow.width = object_w; // trackWindow.height = object_h; // } trackBox = CamShift(backproj, trackWindow, TermCriteria( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1 )); //printf("FindObjectMain::process_camshift %d\n", __LINE__); // if( trackWindow.area() <= 1 ) // { // int cols = backproj.cols; // int rows = backproj.rows; // int r = (MIN(cols, rows) + 5) / 6; // trackWindow = Rect(trackWindow.x - r, trackWindow.y - r, // trackWindow.x + r, trackWindow.y + r) & // Rect(0, 0, cols, rows); // } } // printf("FindObjectMain::process_camshift %d %d %d %d %d\n", // __LINE__, // trackWindow.x, // trackWindow.y, // trackWindow.width, // trackWindow.height); // Draw mask over scene if(config.draw_keypoints) { for(int i = 0; i < scene_h; i++) { switch(get_input(scene_layer)->get_color_model()) { case BC_YUV888: { unsigned char *input = backproj.data + i * scene_image_w; unsigned char *output = get_input(scene_layer)->get_rows()[i + scene_y1] + scene_x1 * 3; for(int j = 0; j < scene_w; j++) { output[0] = *input; output[1] = 0x80; output[2] = 0x80; output += 3; input++; } break; } } } } // Get object outline in the scene layer // printf("FindObjectMain::process_camshift %d %d %d %d %d %d\n", // __LINE__, // (int)trackBox.center.x, // (int)trackBox.center.y, // (int)trackBox.size.width, // (int)trackBox.size.height, // (int)trackBox.angle); double angle = trackBox.angle * 2 * M_PI / 360; double angle1 = atan2(-(double)trackBox.size.height / 2, -(double)trackBox.size.width / 2) + angle; double angle2 = atan2(-(double)trackBox.size.height / 2, (double)trackBox.size.width / 2) + angle; double angle3 = atan2((double)trackBox.size.height / 2, (double)trackBox.size.width / 2) + angle; double angle4 = atan2((double)trackBox.size.height / 2, -(double)trackBox.size.width / 2) + angle; double radius = sqrt(SQR(trackBox.size.height / 2) + SQR(trackBox.size.width / 2)); border_x1 = (int)(trackBox.center.x + cos(angle1) * radius) + scene_x1; border_y1 = (int)(trackBox.center.y + sin(angle1) * radius) + scene_y1; border_x2 = (int)(trackBox.center.x + cos(angle2) * radius) + scene_x1; border_y2 = (int)(trackBox.center.y + sin(angle2) * radius) + scene_y1; border_x3 = (int)(trackBox.center.x + cos(angle3) * radius) + scene_x1; border_y3 = (int)(trackBox.center.y + sin(angle3) * radius) + scene_y1; border_x4 = (int)(trackBox.center.x + cos(angle4) * radius) + scene_x1; border_y4 = (int)(trackBox.center.y + sin(angle4) * radius) + scene_y1; }
void QRFinder::FindFinderPatterns(cv::Mat& inputImg, Rect regionOfInterest, vector<FinderPattern*> & finderPatterns, vector<Drawable*> & debugVector) { struct timespec start,end; SET_TIME(&start); //Get parameters from config edgeThreshold = config->GetIntegerParameter("EdgeThreshold"); debugLevel = config->GetIntegerParameter("QR Debug Level"); int verticalResolution = config->GetIntegerParameter("YResolution"); nonMaxEnabled = config->GetBooleanParameter("EdgeNonMax"); minimumFinderPatternScore = config->GetIntegerParameter("MinimumFPScore"); detectorSize = config->GetIntegerParameter("DetectorSize"); int yBorder = detectorSize; vector<Point3i> exclusionZones; //Calculate limits int maxColumn = regionOfInterest.x + regionOfInterest.width; maxColumn -= detectorSize; int maxRow = regionOfInterest.y + regionOfInterest.height; int xStart = regionOfInterest.x; int yStart = regionOfInterest.y; xStart += detectorSize; yStart += detectorSize; maxColumn -= detectorSize; maxRow -= detectorSize; if (debugLevel > 0) debugVector.push_back(new DebugRectangle(Rect(Point2i(xStart,yStart),Point2i(maxColumn,maxRow)),Colors::Aqua,1)); //Find horizontal edges SET_TIME(&start); FindEdgesClosed(inputImg,regionOfInterest,edgeArray,edgeThreshold,nonMaxEnabled,detectorSize); SET_TIME(&end); double edgeTime_local = calc_time_double(start,end); edgeTime = (edgeTime + edgeTime_local)/2.0; config->SetLabelValue("EdgeTime",(float)edgeTime/1000.0f); //If debug level set, find all vertical edges and draw them if (debugLevel <= -2) { for (int x = 1; x < verticalEdgeArray.rows-1; x ++) { FindEdgesVerticalClosed(inputImg,x); const short * verticalEdgePtr = verticalEdgeArray.ptr<short>(x); for (int y = 0; y < (verticalEdgeArray.cols);y++) { short transition = verticalEdgePtr[y]; if (transition < 0) { debugVector.push_back(new DebugCircle(Point2i(x,y),0,Colors::Fuchsia,true)); } else if (transition > 0) { debugVector.push_back(new DebugCircle(Point2i(x,y),0,Colors::Cyan,true)); } } } } //END int bw[6] = { 0 }; int k = 0; //LOGV(LOGTAG_QR,"ImgSize=[%d,%d] EdgeSize=[%d,%d]",inputImg.rows,inputImg.cols,edgeArray.rows,edgeArray.cols); int yDirection = 1; int yCenter = yStart + (maxRow - yStart)/2; int y = yStart, absOffset = 0; LOGV(LOGTAG_QR,"Beginning search. y[%d->%d], Center=%d, x[%d->%d]",yStart,maxRow,yCenter,xStart,maxColumn); while (y < maxRow && y >= yStart) { y = yCenter + absOffset * yDirection; if (yDirection == 1) absOffset += verticalResolution; //Increment every other frame yDirection = -yDirection; //Change direction every frame k = 0; bw[0] = bw[1] = bw[2] = bw[3] = bw[4] = bw[5] = 0; const short * edgeRowPtr = edgeArray.ptr<short>(y); for (int x = xStart; x < maxColumn; x++) { if (isInRadius(exclusionZones,Point2i(x,y))) continue; int transition = edgeRowPtr[x]; //getTransition(Mi,x,threshold); if (k == 0) //Haven't found edge yet { if (transition < 0) /* Light->dark transistion */ { k++; } } else //Found at least one edge { if ((k & 1) == 1) //Counting dark { if (transition > 0) //dark to light { ++k; } } else //Counting light { if (transition < 0) //light to dark { ++k; } } } if (k > 0) ++bw[k-1]; if (FP_DEBUG_ENABLED && (debugLevel == -1 || debugLevel == -2)) { if (transition < 0) debugVector.push_back(new DebugCircle(Point2i(x,y),0,Colors::Lime,true)); else if (transition > 0) debugVector.push_back(new DebugCircle(Point2i(x,y),0,Colors::Yellow,true)); } if (k == 6) { int result = 0; result = CheckRatios(bw,NULL); if (result == 1) { //LOGV(LOGTAG_QR,"Ratio check pass"); //Center based on initial ratio float patternCenterWidth = (float)bw[2]; int tempXCenter = (x - bw[4] - bw[3]) - (int)round(patternCenterWidth/2.0f); float xOffset = (patternCenterWidth/6.0f); //y coordinate of center. If check fails, returns 0. int tempYCenterArray[] = {0,0,0}; int * verticalPatternSizes[3]; for (int i = 0;i < 3;i++) verticalPatternSizes[i] = new int[5]; tempYCenterArray[0] = FindCenterVertical(inputImg, tempXCenter, y, bw, debugVector,verticalPatternSizes[0]); tempYCenterArray[1] = FindCenterVertical(inputImg, tempXCenter - xOffset, y, bw, debugVector,verticalPatternSizes[1]); tempYCenterArray[2] = FindCenterVertical(inputImg, tempXCenter + xOffset, y, bw, debugVector,verticalPatternSizes[2]); int tempYCenter = 0; int passCount = 0; float avgYSize = 0; int averageVerticalSize[5] = {0,0,0,0,0}; for (int yTest = 0; yTest < 3; yTest++) { if (tempYCenterArray[yTest] > 0) { passCount++; tempYCenter += tempYCenterArray[yTest]; for (int i=0;i<5;i++) { averageVerticalSize[i] += (verticalPatternSizes[yTest])[i]; avgYSize += (verticalPatternSizes[yTest])[i]; } } } if (passCount >= 2) { //LOGV(LOGTAG_QR,"Vertical test pass-1"); tempYCenter = (int)round((float)tempYCenter / (float)passCount); avgYSize = (float)avgYSize / (float)passCount; int allowedVariance = (int)avgYSize >> 2; bool yVarianceTest = true; for (int yTest = 0; yTest < 3; yTest++) { if (tempYCenterArray[yTest] > 0) { if (abs(tempYCenterArray[yTest] - tempYCenter) > allowedVariance) { yVarianceTest = false; break; } } } if (yVarianceTest) { //LOGV(LOGTAG_QR,"Vertical test pass-2. Passcount=%d",passCount); //Average the vertical pattern sizes for (int i=0;i<5;i++) { averageVerticalSize[i] = idiv(averageVerticalSize[i],passCount); } //LOGV(LOGTAG_QR,"Averaged sizes. Center=%d",averageVerticalSize[2]); int tempXCenterArray[] = {0,0,0}; int xSizeArray[] = {0,0,0}; int yOffset = idiv(averageVerticalSize[2],6.0f); //LOGV(LOGTAG_QR,"Yoffset=%d,yCenter=%d",yOffset,tempYCenter); tempXCenterArray[0] = FindCenterHorizontal(tempXCenter, tempYCenter-yOffset, bw, xSizeArray[0], debugVector); tempXCenterArray[1] = FindCenterHorizontal(tempXCenter, tempYCenter, bw, xSizeArray[1], debugVector); tempXCenterArray[2] = FindCenterHorizontal(tempXCenter, tempYCenter+yOffset, bw, xSizeArray[2], debugVector); tempXCenter = 0; passCount = 0; float avgXSize = 0; for (int xTest = 0; xTest < 3; xTest++) { if (tempXCenterArray[xTest] > 0) { passCount++; tempXCenter += tempXCenterArray[xTest]; avgXSize += xSizeArray[xTest]; } } if (passCount >= 2) { //LOGV(LOGTAG_QR,"Horizontal test pass"); tempXCenter = (int)round((float)tempXCenter / (float)passCount); avgXSize = (float)avgXSize/(float)passCount; //allowedVariance = (int)round((float)avgYSize/1.5f); float aspectRatio = avgXSize/avgYSize; if (aspectRatio > 0.33f && aspectRatio < 3.0f) { //LOGV(LOGTAG_QR,"Size test pass"); Point2i finderPatternCenter = Point2i(tempXCenter,tempYCenter); //Center of finder pattern int finderPatternSize = MAX(avgXSize,avgYSize); int fpRadius = (int)round((float)finderPatternSize/2.0f); int fpRadiusExclude = ipow(finderPatternSize,2); //LOGD(LOGTAG_QR,"Creating new pattern[%d,%d]",avgXSize,avgYSize); //Create a new pattern FinderPattern * newPattern = new FinderPattern(finderPatternCenter,Size2i(avgXSize,avgYSize)); Size2f patternSearchSize = Size2f(avgXSize,avgYSize); vector<Point2i> corners; struct timespec fastStart,fastEnd; SET_TIME(&fastStart); fastQRFinder->LocateFPCorners(inputImg,newPattern,corners,debugVector); // fastQRFinder->CheckAlignmentPattern(inputImg,finderPatternCenter,patternSearchSize,corners,debugVector); SET_TIME(&fastEnd); double fastFPTime_Local = calc_time_double(fastStart,fastEnd); fastFPTime += fastFPTime_Local; if (corners.size() == 4) { //if (validatePattern(newPattern,finderPatterns)) //{ newPattern->patternCorners = corners; exclusionZones.push_back(Point3i(finderPatternCenter.x,finderPatternCenter.y, fpRadiusExclude)); finderPatterns.push_back(newPattern); if (FP_DEBUG_ENABLED && debugLevel > 0) { debugVector.push_back(new DebugCircle(finderPatternCenter,fpRadius,Colors::MediumSpringGreen,1,true)); for (int i=0;i<corners.size();i++) { if (FP_DEBUG_ENABLED && debugLevel > 0) debugVector.push_back(new DebugCircle(corners[i],10,Colors::DodgerBlue,2)); } } //} //else //{ // //LOGV(LOGTAG_QR,"Compare check failed"); // if (FP_DEBUG_ENABLED && debugLevel > 0) // debugVector.push_back(new DebugCircle(finderPatternCenter,fpRadius,Colors::HotPink,2)); //} } else { //LOGV(LOGTAG_QR,"FAST check failed"); if (FP_DEBUG_ENABLED && debugLevel > 0) debugVector.push_back(new DebugCircle(finderPatternCenter,fpRadius,Colors::Red,2)); delete newPattern; } } else { //LOGV(LOGTAG_QR,"Size check failed"); //Size check failed if (FP_DEBUG_ENABLED && debugLevel > 1) debugVector.push_back(new DebugCircle(Point2i(tempXCenter,tempYCenter),13, Colors::HotPink,1)); } } else { //LOGV(LOGTAG_QR,"Horizontal check failed"); //Vertical check succeeded, but horizontal re-check failed if (FP_DEBUG_ENABLED && debugLevel > 1) debugVector.push_back(new DebugCircle(Point2i(tempXCenter,tempYCenter),12, Colors::OrangeRed,1)); } } else { //LOGV(LOGTAG_QR,"Variance test failed. AllowedVariance = %d, yCenters = %d,%d,%d [avg=%d], AvgYSize=%d",allowedVariance,tempYCenterArray[0],tempYCenterArray[1],tempYCenterArray[2],tempYCenter,avgYSize); //Vertical variance test failed if (FP_DEBUG_ENABLED && debugLevel > 1) debugVector.push_back(new DebugCircle(Point2i(tempXCenter,tempYCenter),14, Colors::MediumSpringGreen,1)); } } else { //Ratios were correct but vertical check failed if (FP_DEBUG_ENABLED && debugLevel > 2) { if (tempYCenter == 0) //ratio fail debugVector.push_back(new DebugCircle(Point2i(tempXCenter,y),10,Colors::Aqua,1)); else if (tempYCenter == -1) //topcheck fail debugVector.push_back(new DebugCircle(Point2i(tempXCenter,y),10,Colors::Orange,1)); else if (tempYCenter == -2) //bottomcheck fail debugVector.push_back(new DebugCircle(Point2i(tempXCenter,y),10,Colors::Lime,1)); } } }
Size2f getSize() const { return Size2f(0, 0); }
int DetectorBarcode::Detect() { std::cout << "DetectorBarcode::Detect()" << std::endl; assert(this->image_.data != NULL); bool debugstripecode = true; //@TODO MAKE SURE TO SET ME TO FALSE IN PRODUCTION bool useAdaptiveThersholding = true; int dpi = 400; //this works well for all scales and sizes.. Mat matImageK; cvtColor(this->image_, matImageK, cv::COLOR_BGR2GRAY); cv::Mat matThres; // VARIABLES // double bar_height_mm_min = 3.7; //[7.5mm=our NMNH c39] [10.7mm=NMNH cover c39] double bar_height_mm_max = 20; double bar_ar_min = 4; double bar_ar_max = 110; int min_characters = 5; //minimum characters in barcode string double bar_dist_group_mm_max = 9.0; //Maximum distance between any grouped bar to be part of the bar group // COMPUTE // double bar_height_px_min = bar_height_mm_min/25.4*dpi; double bar_height_px_max = bar_height_mm_max/25.4*dpi; double bar_area_px_min = bar_height_px_min*(bar_height_px_min*1.0/bar_ar_max); //Dont allow the area to be less than 1px row bar_area_px_min = bar_area_px_min < bar_height_px_min ? bar_height_px_min : bar_area_px_min; double bar_area_px_max = bar_height_px_max*(bar_height_px_max*1.0/bar_ar_min); double bar_dist_group_px_max = bar_dist_group_mm_max/25.4*dpi; if (useAdaptiveThersholding) { //int AT_blocksize = dpi*0.05; int AT_blocksize = bar_height_px_min*0.5; int AT_iseven=AT_blocksize%2; AT_blocksize += 1+AT_iseven; //Makes sure the blocksize is an even number //cout << "AT_blocksize=" << AT_blocksize << endl; adaptiveThreshold(matImageK, matThres, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, AT_blocksize, 20); } else { threshold(matImageK, matThres, 127, 255, THRESH_BINARY_INV); } if (debugstripecode) { //cout << "dpi=" << dpi << endl; imwrite("/Users/tzaman/Desktop/bc/matImage.tif", this->image_); imwrite("/Users/tzaman/Desktop/bc/matThres.tif", matThres); } vector< vector<Point> > contours; vector<Vec4i> hierarchy; findContours( matThres, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE ); //cout << "contours.size()=" << contours.size() << endl; if (contours.size() == 0) { string strErr = "No contours found."; cout << strErr << endl; return RET_NONE_FOUND; } //RANSAC vars int min_inliers = (min_characters+2)*5*0.75; //+2 (start&stop), *5 (stripes per char), *0.x (margin) double max_px_dist = (bar_height_px_min+bar_height_px_max)*0.5*0.05; //Maximum distance from RANSAC line to a point vector<RotatedRect> stripeCandidates; for(int i = 0; i >= 0; i = hierarchy[i][0] ) { double cArea = contourArea( contours[i],false); if (cArea < bar_area_px_min*0.5){ continue; } if (cArea > bar_area_px_max){ continue; } //cout << "[" << i << "]" << " cArea=" << cArea << endl; RotatedRect rotRect= minAreaRect(contours[i]); double ar = max(double(rotRect.size.width),double(rotRect.size.height)) / min(double(rotRect.size.width),double(rotRect.size.height)); if (ar < bar_ar_min){ continue; } if (ar > bar_ar_max){ continue; } double width = std::min(rotRect.size.width, rotRect.size.height); double height = std::max(rotRect.size.width, rotRect.size.height); //Check the length if (height < bar_height_px_min){ //Stripe too small continue; } if (height > bar_height_px_max ){ //Stripe too long continue; } //cout << i << " rotRect: sz=" << rotRect.size << " cp=" << rotRect.center << " a=" << rotRect.angle << " ar=" << ar << endl; Rect rCrop = boundingRect(contours[i]); //Below parameter is dynamic, plz note double min_area_fill=0.15;// = 0.25 ;// 0.4 means 40% of the bounding rectangle of the contour needs to be filled //The min_area_fill threshold should be dependent on the width in pixels, because there's more noise in thinner ones if (width<3){ min_area_fill = 0.05; } else if (width <5){ min_area_fill = 0.10; } //Check if the rectangle is actually filled well int fullarea = rCrop.area(); if ( (double(cArea)/double(fullarea)) < min_area_fill){ continue; } //cout << i << " fullarea=" << fullarea << " carea=" << cArea << endl; if (debugstripecode){ imwrite("/Users/tzaman/Desktop/seg/" + std::to_string(i) + ".tif", matImageK(rCrop)); } stripeCandidates.push_back(rotRect); } if (debugstripecode){ Mat matBarcodeFull = this->image_.clone(); for (int j=0; j<stripeCandidates.size(); j++){ util::rectangle(matBarcodeFull, stripeCandidates[j], cv::Scalar(255,0,0), 2); } imwrite("/Users/tzaman/Desktop/bc/_candidates.tif", matBarcodeFull); } //cout << "stripeCandidates.size()=" << stripeCandidates.size() << endl; if (stripeCandidates.size() < min_inliers){ string strErr = "Code 39 did not find enough bars to accurately make a code."; cout << strErr << endl; return RET_NONE_FOUND; } std::vector<Point> vecPtRectCenter = util::vecrotrect2vecpt(stripeCandidates); std::vector<std::vector<int> > vecGroupIdxs = util::groupPoints(vecPtRectCenter, bar_dist_group_px_max, min_inliers); //std::vector<std::vector<cv::Point> > vecGroupPts(vecGroupIdxs.size()); std::vector<std::vector<cv::RotatedRect> > vecGroupRects(vecGroupIdxs.size()); //Relate indexes to points and add to group vector for (int i=0; i<vecGroupIdxs.size(); i++){ //vecGroupPts[i].resize(vecGroupIdxs[i].size()); vecGroupRects[i].resize(vecGroupIdxs[i].size()); for (int j=0; j<vecGroupIdxs[i].size(); j++){ //cout << i << "," << j << endl; //vecGroupPts[i][j] = vecPtRectCenter[vecGroupIdxs[i][j]]; vecGroupRects[i][j] = stripeCandidates[vecGroupIdxs[i][j]]; } } //Draw all groups //if(debugstripecode){ // for (int i=0; i<vecGroupPts.size(); i++){ // Mat matGroup = matImage.clone(); // for (int j=0; j<vecGroupPts[i].size(); j++){ // circle(matGroup, vecGroupPts[i][j], 5, Scalar(255,0,255), 1, CV_AA,0); // } // imwrite("/Users/tzaman/Desktop/bc/_group_" + std::to_string(i) + ".tif", matGroup); // } //} //exit(-1); //cout << "vecGroupPts.size()=" << vecGroupPts.size() << endl; //Erase small groups //for (int i=vecGroupPts.size()-1; i>=0; i--){ // if (vecGroupPts[i].size() < min_inliers){ // //Skipping group, too small. // vecGroupIdxs.erase(vecGroupIdxs.begin()+i); // vecGroupPts.erase(vecGroupPts.begin()+i); // } //} //cout << "vecGroupPts.size()=" << vecGroupPts.size() << endl; if (vecGroupIdxs.size() == 0) { string strErr = "Code 39 failed to ransac bars in a line."; cout << strErr << endl; return RET_NONE_FOUND; } //Now cycle over the groups vector<vector<int> > vecVecInlierIdx; vector<Vec4f> vecLines; vector<int> vecFromGroup; //Keeps track of which group the vecvecInlierIdx belongs to for (int i = 0; i < vecGroupRects.size(); i++) { Ransac(vecGroupRects[i], min_inliers, max_px_dist, vecVecInlierIdx, vecLines, this->image_); vecFromGroup.resize(vecVecInlierIdx.size(), i); } if (vecLines.size() == 0) { string strErr = "Code 39 failed to ransac bars in a line."; cout << strErr << endl; return RET_NONE_FOUND; } else { //cout << "Code39 ransac succesfull" << endl; } //for (int i=0; i<vecGroupIdxs.size(); i++){ // cout << "Group " << i << " (" << vecGroupIdxs[i].size() << ") : "; // for (int j=0; j<vecGroupIdxs[i].size(); j++){ // cout << vecGroupIdxs[i][j] << " "; // } // cout << endl; //} //Convert back vecVecInlierIdx to original indices for (int i=0; i<vecVecInlierIdx.size(); i++){ //cout << "vecVecInlierIdx[" << i << "] is from group " << vecFromGroup[i] << endl; for (int j=0; j<vecVecInlierIdx[i].size(); j++){ //cout << " " << vecVecInlierIdx[i][j] << " -> " << vecGroupIdxs[vecFromGroup[i]][vecVecInlierIdx[i][j]] << endl; vecVecInlierIdx[i][j] = vecGroupIdxs[vecFromGroup[i]][vecVecInlierIdx[i][j]]; } } for (int i=0; i < vecLines.size(); i++){ int numpts = vecVecInlierIdx[i].size(); cout << "Potential barcode #" << i << " with " << numpts << " points." << endl; //double angle=atan2(vecLines[i][1],vecLines[i][0])*180/M_PI; //For some reason it clips from [-90,90] double angle_rad = atan2(vecLines[i][1],vecLines[i][0]); //For some reason it clips from [-90,90] double angle_deg = angle_rad*180.0/M_PI; //cout << " angle_deg=" << angle_deg << endl; vector<double> bar_heights(numpts); vector<double> bar_widths(numpts); vector<double> coords_x(numpts); //Loop over all found and ransac-verified stripes in this barcode vector<cv::RotatedRect> stripesVerified(numpts); for (int j=0; j < numpts; j++){ //cout << vecVecInlierIdx[i][j] << endl; //cout << "checking out stripecandidate[" << vecVecInlierIdx[i][j] << "] #" << vecVecInlierIdx[i][j] << endl; stripesVerified[j] = stripeCandidates[vecVecInlierIdx[i][j]]; double dim_smallest = min(stripesVerified[j].size.width, stripesVerified[j].size.height); //For rotation invariance double dim_tallest = max(stripesVerified[j].size.width, stripesVerified[j].size.height); //For rotation invariance bar_heights[j] = dim_tallest; bar_widths[j] = dim_smallest; //Rotate the points straight Point2f ptRot = util::rotatePoint(stripesVerified[j].center, Point(matImageK.cols, matImageK.rows), angle_rad); //cout << ptRot << endl; coords_x[j] = ptRot.x; } double height_median = util::calcMedian(bar_heights); double width_mean = util::calcMean(bar_widths); //cout << "height_median=" << height_median <<" width_mean=" << width_mean << endl; //Find the start and end position for reading vector<size_t> coords_sorted_index; vector<double> coords_x_sorted; sort(coords_x, coords_x_sorted, coords_sorted_index); //cout << coords_x_sorted[0] << " -> " << coords_x_sorted[coords_x_sorted.size()-1] << endl; //Get extrema-stripes Point2f pt_stripe_left = stripeCandidates[vecVecInlierIdx[i][coords_sorted_index[0]]].center; Point2f pt_stripe_right = stripeCandidates[vecVecInlierIdx[i][coords_sorted_index[coords_sorted_index.size() - 1]]].center; //cout << "pt_stripe_left=" << pt_stripe_left << endl; //cout << "pt_stripe_right=" << pt_stripe_right << endl; Point2f pt_barcode_center = (pt_stripe_left + pt_stripe_right) * 0.5; //cout << "pt_barcode_center=" << pt_barcode_center << endl; //Calculate width of the barcode double barcode_width = util::pointDist(pt_stripe_left, pt_stripe_right); //cout << "barcode_width=" << barcode_width << endl; //Make the rotated rectangle around the barcode cv::RotatedRect rotrect_candidate(pt_barcode_center, Size2f(barcode_width, height_median), angle_deg); const double add_width_on_sides_before_decoding = 7.0; // this number will be multiplied by average bar width //Add margin (of a few median widths) rotrect_candidate.size += Size2f(width_mean * add_width_on_sides_before_decoding, 0); const double height_retrainer_for_collapse = 0.25; //Extract the barcode itself cv::RotatedRect rotrect_candidate_thin = rotrect_candidate; //Crop off some margin in thickness because we dont want to collapse the entire barcode. if (rotrect_candidate_thin.size.width < rotrect_candidate_thin.size.height) { rotrect_candidate_thin.size.width *= height_retrainer_for_collapse; } else { rotrect_candidate_thin.size.height *= height_retrainer_for_collapse; } openbarcode::code code_candidate; code_candidate.rotrect = rotrect_candidate_thin; this->code_candidates_.push_back(code_candidate); } return RET_SUCCESS; }