int Plane::Clip(const Triangle &triangle, Triangle &t1, Triangle &t2) const { bool side[3]; side[0] = IsOnPositiveSide(triangle.a); side[1] = IsOnPositiveSide(triangle.b); side[2] = IsOnPositiveSide(triangle.c); int nPos = (side[0] ? 1 : 0) + (side[1] ? 1 : 0) + (side[2] ? 1 : 0); if (nPos == 0) // Everything should be clipped? return 0; // We will output at least one triangle, so copy the input to t1 for processing. t1 = triangle; if (nPos == 3) // All vertices of the triangle are in positive side? return 1; if (nPos == 1) { if (side[1]) { vec tmp = t1.a; t1.a = t1.b; t1.b = t1.c; t1.c = tmp; } else if (side[2]) { vec tmp = t1.a; t1.a = t1.c; t1.c = t1.b; t1.b = tmp; } // After the above cycling, t1.a is the triangle on the positive side. float t; Intersects(LineSegment(t1.a, t1.b), &t); t1.b = t1.a + (t1.b-t1.a)*t; Intersects(LineSegment(t1.a, t1.c), &t); t1.c = t1.a + (t1.c-t1.a)*t; return 1; } // Must be nPos == 2. if (!side[1]) { vec tmp = t1.a; t1.a = t1.b; t1.b = t1.c; t1.c = tmp; } else if (!side[2]) { vec tmp = t1.a; t1.a = t1.c; t1.c = t1.b; t1.b = tmp; } // After the above cycling, t1.a is the triangle on the negative side. float t, r; Intersects(LineSegment(t1.a, t1.b), &t); vec ab = t1.a + (t1.b-t1.a)*t; Intersects(LineSegment(t1.a, t1.c), &r); vec ac = t1.a + (t1.c-t1.a)*r; t1.a = ab; t2.a = t1.c; t2.b = ac; t2.c = ab; return 2; }
void RowBitmapLineSegmentFinder::reset() { currentRowIndex = -1; firstLineSegmentPreviousRow = LineSegment(); secondLineSegmentPreviousRow = LineSegment(); }
LineSegment Plane::Project(const LineSegment &lineSegment) const { return LineSegment(Project(lineSegment.a), Project(lineSegment.b)); }
vector<PlateLine> PlateLines::getLines(Mat edges, float sensitivityMultiplier, bool vertical) { if (this->debug) cout << "PlateLines::getLines" << endl; int HORIZONTAL_SENSITIVITY = pipelineData->config->plateLinesSensitivityHorizontal; int VERTICAL_SENSITIVITY = pipelineData->config->plateLinesSensitivityVertical; vector<Vec2f> allLines; vector<PlateLine> filteredLines; int sensitivity; if (vertical) sensitivity = VERTICAL_SENSITIVITY * (1.0 / sensitivityMultiplier); else sensitivity = HORIZONTAL_SENSITIVITY * (1.0 / sensitivityMultiplier); HoughLines( edges, allLines, 1, CV_PI/180, sensitivity, 0, 0 ); for( size_t i = 0; i < allLines.size(); i++ ) { float rho = allLines[i][0], theta = allLines[i][1]; Point pt1, pt2; double a = cos(theta), b = sin(theta); double x0 = a*rho, y0 = b*rho; double angle = theta * (180 / CV_PI); pt1.x = cvRound(x0 + 1000*(-b)); pt1.y = cvRound(y0 + 1000*(a)); pt2.x = cvRound(x0 - 1000*(-b)); pt2.y = cvRound(y0 - 1000*(a)); if (vertical) { if (angle < 20 || angle > 340 || (angle > 160 && angle < 210)) { // good vertical LineSegment line; if (pt1.y <= pt2.y) line = LineSegment(pt2.x, pt2.y, pt1.x, pt1.y); else line = LineSegment(pt1.x, pt1.y, pt2.x, pt2.y); // Get rid of the -1000, 1000 stuff. Terminate at the edges of the image // Helps with debugging/rounding issues later LineSegment top(0, 0, edges.cols, 0); LineSegment bottom(0, edges.rows, edges.cols, edges.rows); Point p1 = line.intersection(bottom); Point p2 = line.intersection(top); PlateLine plateLine; plateLine.line = LineSegment(p1.x, p1.y, p2.x, p2.y); plateLine.confidence = (1.0 - MIN_CONFIDENCE) * ((float) (allLines.size() - i)) / ((float)allLines.size()) + MIN_CONFIDENCE; filteredLines.push_back(plateLine); } } else { if ( (angle > 70 && angle < 110) || (angle > 250 && angle < 290)) { // good horizontal LineSegment line; if (pt1.x <= pt2.x) line = LineSegment(pt1.x, pt1.y, pt2.x, pt2.y); else line =LineSegment(pt2.x, pt2.y, pt1.x, pt1.y); // Get rid of the -1000, 1000 stuff. Terminate at the edges of the image // Helps with debugging/ rounding issues later int newY1 = line.getPointAt(0); int newY2 = line.getPointAt(edges.cols); PlateLine plateLine; plateLine.line = LineSegment(0, newY1, edges.cols, newY2); plateLine.confidence = (1.0 - MIN_CONFIDENCE) * ((float) (allLines.size() - i)) / ((float)allLines.size()) + MIN_CONFIDENCE; filteredLines.push_back(plateLine); } } } return filteredLines; }
void CharacterAnalysis::analyze() { thresholds = produceThresholds(img_gray, config); /* // Morph Close the gray image to make it easier to detect blobs int morph_elem = 1; int morph_size = 1; Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) ); for (int i = 0; i < thresholds.size(); i++) { //morphologyEx( mask, mask, MORPH_CLOSE, element ); morphologyEx( thresholds[i], thresholds[i], MORPH_OPEN, element ); //dilate( thresholds[i], thresholds[i], element ); } */ timespec startTime; getTime(&startTime); for (int i = 0; i < thresholds.size(); i++) { vector<vector<Point> > contours; vector<Vec4i> hierarchy; Mat tempThreshold(thresholds[i].size(), CV_8U); thresholds[i].copyTo(tempThreshold); findContours(tempThreshold, contours, // a vector of contours hierarchy, CV_RETR_TREE, // retrieve all contours CV_CHAIN_APPROX_SIMPLE ); // all pixels of each contours allContours.push_back(contours); allHierarchy.push_back(hierarchy); } if (config->debugTiming) { timespec endTime; getTime(&endTime); cout << " -- Character Analysis Find Contours Time: " << diffclock(startTime, endTime) << "ms." << endl; } //Mat img_equalized = equalizeBrightness(img_gray); getTime(&startTime); for (int i = 0; i < thresholds.size(); i++) { vector<bool> goodIndices = this->filter(thresholds[i], allContours[i], allHierarchy[i]); charSegments.push_back(goodIndices); if (config->debugCharAnalysis) cout << "Threshold " << i << " had " << getGoodIndicesCount(goodIndices) << " good indices." << endl; } if (config->debugTiming) { timespec endTime; getTime(&endTime); cout << " -- Character Analysis Filter Time: " << diffclock(startTime, endTime) << "ms." << endl; } this->plateMask = findOuterBoxMask(); if (hasPlateMask) { // Filter out bad contours now that we have an outer box mask... for (int i = 0; i < thresholds.size(); i++) { charSegments[i] = filterByOuterMask(allContours[i], allHierarchy[i], charSegments[i]); } } int bestFitScore = -1; int bestFitIndex = -1; for (int i = 0; i < thresholds.size(); i++) { //vector<bool> goodIndices = this->filter(thresholds[i], allContours[i], allHierarchy[i]); //charSegments.push_back(goodIndices); int segmentCount = getGoodIndicesCount(charSegments[i]); if (segmentCount > bestFitScore) { bestFitScore = segmentCount; bestFitIndex = i; bestCharSegments = charSegments[i]; bestThreshold = thresholds[i]; bestContours = allContours[i]; bestHierarchy = allHierarchy[i]; bestCharSegmentsCount = segmentCount; } } if (this->config->debugCharAnalysis) cout << "Best fit score: " << bestFitScore << " Index: " << bestFitIndex << endl; if (bestFitScore <= 1) return; //getColorMask(img, allContours, allHierarchy, charSegments); if (this->config->debugCharAnalysis) { Mat img_contours(bestThreshold.size(), CV_8U); bestThreshold.copyTo(img_contours); cvtColor(img_contours, img_contours, CV_GRAY2RGB); vector<vector<Point> > allowedContours; for (int i = 0; i < bestContours.size(); i++) { if (bestCharSegments[i]) allowedContours.push_back(bestContours[i]); } drawContours(img_contours, bestContours, -1, // draw all contours cv::Scalar(255,0,0), // in blue 1); // with a thickness of 1 drawContours(img_contours, allowedContours, -1, // draw all contours cv::Scalar(0,255,0), // in green 1); // with a thickness of 1 displayImage(config, "Matching Contours", img_contours); } //charsegments = this->getPossibleCharRegions(img_threshold, allContours, allHierarchy, STARTING_MIN_HEIGHT + (bestFitIndex * HEIGHT_STEP), STARTING_MAX_HEIGHT + (bestFitIndex * HEIGHT_STEP)); this->linePolygon = getBestVotedLines(img_gray, bestContours, bestCharSegments); if (this->linePolygon.size() > 0) { this->topLine = LineSegment(this->linePolygon[0].x, this->linePolygon[0].y, this->linePolygon[1].x, this->linePolygon[1].y); this->bottomLine = LineSegment(this->linePolygon[3].x, this->linePolygon[3].y, this->linePolygon[2].x, this->linePolygon[2].y); //this->charArea = getCharSegmentsBetweenLines(bestThreshold, bestContours, this->linePolygon); filterBetweenLines(bestThreshold, bestContours, bestHierarchy, linePolygon, bestCharSegments); this->charArea = getCharArea(); if (this->charArea.size() > 0) { this->charBoxTop = LineSegment(this->charArea[0].x, this->charArea[0].y, this->charArea[1].x, this->charArea[1].y); this->charBoxBottom = LineSegment(this->charArea[3].x, this->charArea[3].y, this->charArea[2].x, this->charArea[2].y); this->charBoxLeft = LineSegment(this->charArea[3].x, this->charArea[3].y, this->charArea[0].x, this->charArea[0].y); this->charBoxRight = LineSegment(this->charArea[2].x, this->charArea[2].y, this->charArea[1].x, this->charArea[1].y); } } this->thresholdsInverted = isPlateInverted(); }
bool Localization::Calculate(vector<LineSegment> &clusteredLines, bool circleDetected, const Point2f &FieldHullRealCenter, const vector<cv::Point2f> &FieldHullReal, const Point2d &resultCircleRotated, const vector<Point2f> &goalPosition, const bool &confiused, vector<LineContainer> &AllLines, vector<FeatureContainer> &AllFeatures) { if (_cameraProjections == NULL) { ROS_ERROR("Error in programming"); return false; } AllLines.reserve(clusteredLines.size()); AllFeatures.reserve(5); const double MAX_FASHER = 200.; atLeastOneObservation = false; double UPDATENORMAL = params.loc->UPDATENORMAL() * params.loc->TOTALGAIN(); double UPDATESTRONG = params.loc->UPDATESTRONG() * params.loc->TOTALGAIN(); double UPDATEWEAK = params.loc->UPDATEWEAK() * params.loc->TOTALGAIN(); LineSegment HorLine(cv::Point(0, -10), cv::Point(0, 10)); LineSegment VerLine(cv::Point(10, 0), cv::Point(-10, 0)); for (size_t i = 0; i < clusteredLines.size(); i++) { LineSegment lineSeg = clusteredLines[i]; if (lineSeg.GetLength() > params.loc->minLineLen()) { cv::Point2d mid = lineSeg.GetMiddle(); if (lineSeg.GetAbsMinAngleDegree(VerLine) < 45) { LineType thisType = VerUndef; double angleDiffVer = lineSeg.GetExteriorAngleDegree(VerLine); if (angleDiffVer < -90) angleDiffVer += 180; if (angleDiffVer > 90) angleDiffVer += -180; if (lineSeg.DistanceFromLine(cv::Point(0, 0)) > params.loc->VerLineMinDistanceToUpdate()) { LandmarkType ltype = CenterL; double estimatedY = 0; if (mid.y > 0) { thisType = VerLeft; estimatedY = B2 - mid.y; ltype = LeftL; } else { thisType = VerRight; estimatedY = -B2 + abs(mid.y); ltype = RightL; } addObservation(Point2d(0, estimatedY), 0, MAX_FASHER * getUpdateCoef(UPDATENORMAL, lineSeg), ltype); } else if (lineSeg.DistanceFromLine(FieldHullRealCenter) > (params.loc->VerLineMinDistanceToUpdate() / 2.) && cv::contourArea(FieldHullReal) > 4) { LandmarkType ltype = CenterL; LineSegment l2Test = lineSeg; if (lineSeg.P1.x > lineSeg.P2.x) { l2Test.P1 = lineSeg.P2; l2Test.P2 = lineSeg.P1; } double estimatedY = 0; if (l2Test.GetSide(FieldHullRealCenter) < 0) { thisType = VerLeftNear; estimatedY = B2 - mid.y; ltype = LeftL; } else { thisType = VerRightNear; estimatedY = -B2 + abs(mid.y); ltype = RightL; } addObservation(Point2d(0, estimatedY), 0, MAX_FASHER * getUpdateCoef(UPDATENORMAL, lineSeg), ltype); } AllLines.push_back(LineContainer(lineSeg, thisType)); } else { LineType thisType = HorUndef; double angleDiffHor = lineSeg.GetExteriorAngleDegree(HorLine); if (angleDiffHor < -90) angleDiffHor += 180; if (angleDiffHor > 90) angleDiffHor += -180; if (circleDetected && DistanceFromLineSegment(lineSeg, resultCircleRotated) < 1) { thisType = HorCenter; double estimatedX = -mid.x; addObservation(Point2d(estimatedX, 0), MAX_FASHER * UPDATENORMAL, 0, CenterL); } if (goalPosition.size() >= 2 && lineSeg.DistanceFromLine(goalPosition[0]) < 0.5 && lineSeg.DistanceFromLine(goalPosition[1]) < 0.5) { LandmarkType typel = CenterL; double estimatedX = 0; if (mid.x > 0) { typel = FrontL; estimatedX = A2 - mid.x; } else { typel = BackL; estimatedX = -A2 + abs(mid.x); } double lowPC = getUpdateCoef( (goalPosition.size() == 2 ? UPDATESTRONG : UPDATENORMAL), lineSeg); addObservation(Point2d(estimatedX, 0), MAX_FASHER * lowPC, 0, typel); thisType = HorGoal; } AllLines.push_back(LineContainer(lineSeg, thisType)); } } } for (size_t i = 0; i < AllLines.size(); i++) { LineContainer hI = AllLines[i]; if (hI.type != HorUndef) continue; for (size_t j = i; j < AllLines.size(); j++) { LineContainer hJ = AllLines[j]; if (hJ.type != HorUndef) continue; cv::Point2d midI = hI.lineTransformed.GetMiddle(); cv::Point2d midJ = hJ.lineTransformed.GetMiddle(); double distanceToEachOther = dist3D_Segment_to_Segment( hI.lineTransformed, hJ.lineTransformed); double midPointsLSAngleToOne = hI.lineTransformed.GetAbsMinAngleDegree( LineSegment(midI, midJ)); if (distanceToEachOther < E * 1.5 && distanceToEachOther > E * 0.5 && hI.lineTransformed.GetAbsMinAngleDegree( hJ.lineTransformed) < 30 && midPointsLSAngleToOne > 25) { bool hJ_Is_Goal_Line = hJ.lineTransformed.DistanceFromLine( cv::Point(0, 0)) > hI.lineTransformed.DistanceFromLine(cv::Point(0, 0)); cv::Point2d mid = hJ_Is_Goal_Line ? midJ : midI; double estimatedX = 0; if ((hJ_Is_Goal_Line ? hJ.lineTransformed : hI.lineTransformed).DistanceFromLine( cv::Point(0, 0)) > 1.2) { LandmarkType typel = CenterL; if (mid.x > 0) { estimatedX = A2 - mid.x; typel = FrontL; } else { estimatedX = -A2 + abs(mid.x); typel = BackL; } double lowPC = getUpdateCoef(UPDATESTRONG, hJ_Is_Goal_Line ? hJ.lineTransformed : hI.lineTransformed); addObservation(Point2d(estimatedX, 0), MAX_FASHER * lowPC, 0, typel); } } } } if (circleDetected) { double estimatedX = -resultCircleRotated.x; double estimatedY = -resultCircleRotated.y; addObservation(Point2d(estimatedX, estimatedY), MAX_FASHER * UPDATEWEAK, MAX_FASHER * UPDATEWEAK, CenterL); } if (atLeastOneObservation) { updateVertexIdx(); if ((nodeCounter % 30 == 0) && PreviousVertexId > 0) { optimizer.initializeOptimization(); optimizer.optimize(1); Vector3d tmpV; optimizer.vertex(PreviousVertexId)->getEstimateData(tmpV.data()); location.x = tmpV(0); location.y = tmpV(1); } } return true; }
void PlateCorners::scoreVerticals(int v1, int v2) { float score = 0; // Lower is better LineSegment left; LineSegment right; float charHeightToPlateWidthRatio = config->plateWidthMM / config->charHeightMM; float idealPixelWidth = this->charHeight * (charHeightToPlateWidthRatio * 1.03); // Add 3% so we don't clip any characters if (v1 == NO_LINE && v2 == NO_LINE) { //return; Point centerTop = charRegion->getCharBoxTop().midpoint(); Point centerBottom = charRegion->getCharBoxBottom().midpoint(); LineSegment centerLine = LineSegment(centerBottom.x, centerBottom.y, centerTop.x, centerTop.y); left = centerLine.getParallelLine(idealPixelWidth / 2); right = centerLine.getParallelLine(-1 * idealPixelWidth / 2 ); score += SCORING_MISSING_SEGMENT_PENALTY_VERTICAL * 2; } else if (v1 != NO_LINE && v2 != NO_LINE) { left = this->plateLines->verticalLines[v1]; right = this->plateLines->verticalLines[v2]; } else if (v1 == NO_LINE && v2 != NO_LINE) { right = this->plateLines->verticalLines[v2]; left = right.getParallelLine(idealPixelWidth); score += SCORING_MISSING_SEGMENT_PENALTY_VERTICAL; } else if (v1 != NO_LINE && v2 == NO_LINE) { left = this->plateLines->verticalLines[v1]; right = left.getParallelLine(-1 * idealPixelWidth); score += SCORING_MISSING_SEGMENT_PENALTY_VERTICAL; } // Make sure this line is to the left of our license plate letters if (left.isPointBelowLine(charRegion->getCharBoxLeft().midpoint()) == false) return; // Make sure this line is to the right of our license plate letters if (right.isPointBelowLine(charRegion->getCharBoxRight().midpoint())) return; ///////////////////////////////////////////////////////////////////////// // Score "Distance from the edge... ///////////////////////////////////////////////////////////////////////// float leftDistanceFromEdge = abs((float) (left.p1.x + left.p2.x) / 2); float rightDistanceFromEdge = abs(this->inputImage.cols - ((float) (right.p1.x + right.p2.x) / 2)); float distanceFromEdge = leftDistanceFromEdge + rightDistanceFromEdge; score += distanceFromEdge * SCORING_VERTICALDISTANCE_FROMEDGE_WEIGHT; ///////////////////////////////////////////////////////////////////////// // Score "Boxiness" of the 4 lines. How close is it to a parallelogram? ///////////////////////////////////////////////////////////////////////// float verticalAngleDiff = abs(left.angle - right.angle); score += (verticalAngleDiff) * SCORING_BOXINESS_WEIGHT; ////////////////////////////////////////////////////////////////////////// // SCORE the shape wrt character position and height relative to position ////////////////////////////////////////////////////////////////////////// Point leftMidLinePoint = left.closestPointOnSegmentTo(charRegion->getCharBoxLeft().midpoint()); Point rightMidLinePoint = right.closestPointOnSegmentTo(charRegion->getCharBoxRight().midpoint()); float plateDistance = abs(idealPixelWidth - distanceBetweenPoints(leftMidLinePoint, rightMidLinePoint)); score += plateDistance * SCORING_VERTICALDISTANCE_WEIGHT; if (score < this->bestVerticalScore) { float scorecomponent; if (this->config->debugPlateCorners) { cout << "xx xx Score: charHeight " << this->charHeight << endl; cout << "xx xx Score: idealwidth " << idealPixelWidth << endl; cout << "xx xx Score: v1,v2= " << v1 << "," << v2 << endl; cout << "xx xx Score: Left= " << left.str() << endl; cout << "xx xx Score: Right= " << right.str() << endl; cout << "Vertical breakdown Score:" << endl; cout << " -- Boxiness Score: " << verticalAngleDiff << " -- Weight (" << SCORING_BOXINESS_WEIGHT << ")" << endl; scorecomponent = verticalAngleDiff * SCORING_BOXINESS_WEIGHT; cout << " -- -- Score: " << scorecomponent << " = " << scorecomponent / score * 100 << "% of score" << endl; cout << " -- Distance From Edge Score: " << distanceFromEdge << " -- Weight (" << SCORING_VERTICALDISTANCE_FROMEDGE_WEIGHT << ")" << endl; scorecomponent = distanceFromEdge * SCORING_VERTICALDISTANCE_FROMEDGE_WEIGHT; cout << " -- -- Score: " << scorecomponent << " = " << scorecomponent / score * 100 << "% of score" << endl; cout << " -- Distance Score: " << plateDistance << " -- Weight (" << SCORING_VERTICALDISTANCE_WEIGHT << ")" << endl; scorecomponent = plateDistance * SCORING_VERTICALDISTANCE_WEIGHT; cout << " -- -- Score: " << scorecomponent << " = " << scorecomponent / score * 100 << "% of score" << endl; cout << " -- Score: " << score << endl; } this->bestVerticalScore = score; bestLeft = LineSegment(left.p1.x, left.p1.y, left.p2.x, left.p2.y); bestRight = LineSegment(right.p1.x, right.p1.y, right.p2.x, right.p2.y); } }
LineSegment Ray::ToLineSegment(float dStart, float dEnd) const { return LineSegment(GetPoint(dStart), GetPoint(dEnd)); }
static inline void appendLineSegments(const IntShapeIntervals& intervals, SegmentList& result) { for (unsigned i = 0; i < intervals.size(); i++) result.append(LineSegment(intervals[i].x1(), intervals[i].x2() + 1)); }
LineSegment AABB::Edge(int edgeIndex) const { assume(0 <= edgeIndex && edgeIndex <= 11); switch(edgeIndex) { default: // For release builds where assume() is disabled, return always the first option if out-of-bounds. /* For documentation, here's the segments that are returned: case 0: return LineSegment(CornerPoint(0), CornerPoint(1)); case 1: return LineSegment(CornerPoint(0), CornerPoint(2)); case 2: return LineSegment(CornerPoint(0), CornerPoint(4)); case 3: return LineSegment(CornerPoint(1), CornerPoint(3)); case 4: return LineSegment(CornerPoint(1), CornerPoint(5)); case 5: return LineSegment(CornerPoint(2), CornerPoint(3)); case 6: return LineSegment(CornerPoint(2), CornerPoint(6)); case 7: return LineSegment(CornerPoint(3), CornerPoint(7)); case 8: return LineSegment(CornerPoint(4), CornerPoint(5)); case 9: return LineSegment(CornerPoint(4), CornerPoint(6)); case 10: return LineSegment(CornerPoint(5), CornerPoint(7)); case 11: return LineSegment(CornerPoint(6), CornerPoint(7)); */ // Force-optimize to avoid calling to CornerPoint for another switch-case statement. case 0: return LineSegment(POINT_VEC(minPoint.x, minPoint.y, minPoint.z), POINT_VEC(minPoint.x, minPoint.y, maxPoint.z)); case 1: return LineSegment(POINT_VEC(minPoint.x, minPoint.y, minPoint.z), POINT_VEC(minPoint.x, maxPoint.y, minPoint.z)); case 2: return LineSegment(POINT_VEC(minPoint.x, minPoint.y, minPoint.z), POINT_VEC(maxPoint.x, minPoint.y, minPoint.z)); case 3: return LineSegment(POINT_VEC(minPoint.x, minPoint.y, maxPoint.z), POINT_VEC(minPoint.x, maxPoint.y, maxPoint.z)); case 4: return LineSegment(POINT_VEC(minPoint.x, minPoint.y, maxPoint.z), POINT_VEC(maxPoint.x, minPoint.y, maxPoint.z)); case 5: return LineSegment(POINT_VEC(minPoint.x, maxPoint.y, minPoint.z), POINT_VEC(minPoint.x, maxPoint.y, maxPoint.z)); case 6: return LineSegment(POINT_VEC(minPoint.x, maxPoint.y, minPoint.z), POINT_VEC(maxPoint.x, maxPoint.y, minPoint.z)); case 7: return LineSegment(POINT_VEC(minPoint.x, maxPoint.y, maxPoint.z), POINT_VEC(maxPoint.x, maxPoint.y, maxPoint.z)); case 8: return LineSegment(POINT_VEC(maxPoint.x, minPoint.y, minPoint.z), POINT_VEC(maxPoint.x, minPoint.y, maxPoint.z)); case 9: return LineSegment(POINT_VEC(maxPoint.x, minPoint.y, minPoint.z), POINT_VEC(maxPoint.x, maxPoint.y, minPoint.z)); case 10: return LineSegment(POINT_VEC(maxPoint.x, minPoint.y, maxPoint.z), POINT_VEC(maxPoint.x, maxPoint.y, maxPoint.z)); case 11: return LineSegment(POINT_VEC(maxPoint.x, maxPoint.y, minPoint.z), POINT_VEC(maxPoint.x, maxPoint.y, maxPoint.z)); } }
LineSegment Ray::ToLineSegment(float d) const { return LineSegment(pos, GetPoint(d)); }
// Implements a basic rudimentary layout analysis based on Leptonica // works OK for Arabic. For other languages, the function TesseractPageAnalysis // should be called instead. bool CubeLineSegmenter::FindLines() { // convert the image to gray scale if necessary Pix *gray_scale_img = NULL; if (orig_img_->d != 2 && orig_img_->d != 8) { gray_scale_img = pixConvertTo8(orig_img_, false); if (gray_scale_img == NULL) { return false; } } else { gray_scale_img = orig_img_; } // threshold image Pix *thresholded_img; thresholded_img = pixThresholdToBinary(gray_scale_img, 128); // free the gray scale image if necessary if (gray_scale_img != orig_img_) { pixDestroy(&gray_scale_img); } // bail-out if thresholding failed if (thresholded_img == NULL) { return false; } // deskew Pix *deskew_img = pixDeskew(thresholded_img, 2); if (deskew_img == NULL) { return false; } pixDestroy(&thresholded_img); img_ = CleanUp(deskew_img); pixDestroy(&deskew_img); if (img_ == NULL) { return false; } pixDestroy(&deskew_img); // compute connected components Boxa *boxa = pixConnComp(img_, &con_comps_, 8); if (boxa == NULL) { return false; } boxaDestroy(&boxa); // estimate dot and alef hgts if (EstimateFontParams() == false) { return false; } // perform line segmentation if (LineSegment() == false) { return false; } // success init_ = true; return true; }
LineSegment RandomLineSegmentNearOrigin(float maxDistanceFromOrigin) { return LineSegment(RandomPointNearOrigin(maxDistanceFromOrigin), RandomPointNearOrigin(maxDistanceFromOrigin)); }
// // Calculate borders of this mask zone using MCS. // void ZoneBorder::Calculate( int nType, float2 fUpperLeft, float2 fLowerRight) { m_nType = nType; m_zoneBorder_ccs[MASK_ZONE_TOP] = LineSegment ( float2 { fUpperLeft.x, fUpperLeft.y }, float2 { fLowerRight.x, fUpperLeft.y } ); m_zoneBorder_ccs[MASK_ZONE_RIGHT] = LineSegment ( float2 { fLowerRight.x, fUpperLeft.y }, float2 { fLowerRight.x, fLowerRight.y } ); m_zoneBorder_ccs[MASK_ZONE_BOTTOM] = LineSegment ( float2 { fLowerRight.x, fLowerRight.y }, float2 { fUpperLeft.x, fLowerRight.y } ); m_zoneBorder_ccs[MASK_ZONE_LEFT] = LineSegment ( float2 { fUpperLeft.x, fLowerRight.y }, float2 { fUpperLeft.x, fUpperLeft.y } ); }
void process(const vector<string> &args, MyWindow *w) { int i; ArgData a = processArgs(args); Debugging::setOutStream(cout); Mesh m(a.filename); if(m.vertices.size() == 0) { cout << "Error reading file. Aborting." << endl; exit(0); return; } for(i = 0; i < (int)m.vertices.size(); ++i) m.vertices[i].pos = a.meshTransform * m.vertices[i].pos; m.normalizeBoundingBox(); m.computeVertexNormals(); Skeleton given = a.skeleton; given.scale(a.skelScale * 0.7); if(a.stopAtMesh) { //if early bailout w->addMesh(new StaticDisplayMesh(m)); return; } PinocchioOutput o; if(!a.noFit) { //do everything o = autorig(given, m); } else { //skip the fitting step--assume the skeleton is already correct for the mesh TreeType *distanceField = constructDistanceField(m); VisTester<TreeType> *tester = new VisTester<TreeType>(distanceField); o.embedding = a.skeleton.fGraph().verts; for(i = 0; i < (int)o.embedding.size(); ++i) o.embedding[i] = m.toAdd + o.embedding[i] * m.scale; o.attachment = new Attachment(m, a.skeleton, o.embedding, tester); delete tester; delete distanceField; } if(o.embedding.size() == 0) { cout << "Error embedding" << endl; exit(0); } if(a.motionname.size() > 0) { w->addMesh(new DefMesh(m, given, o.embedding, *(o.attachment), new Motion(a.motionname))); } else { w->addMesh(new StaticDisplayMesh(m)); for(i = 1; i < (int)o.embedding.size(); ++i) { w->addLine(LineSegment(o.embedding[i], o.embedding[given.fPrev()[i]], Vector3(.5, .5, 0), 4.)); } } //output skeleton embedding for(i = 0; i < (int)o.embedding.size(); ++i) o.embedding[i] = (o.embedding[i] - m.toAdd) / m.scale; ofstream os("skeleton.out"); for(i = 0; i < (int)o.embedding.size(); ++i) { os << i << " " << o.embedding[i][0] << " " << o.embedding[i][1] << " " << o.embedding[i][2] << " " << a.skeleton.fPrev()[i] << endl; } //output attachment std::ofstream astrm("attachment.out"); for(i = 0; i < (int)m.vertices.size(); ++i) { Vector<double, -1> v = o.attachment->getWeights(i); for(int j = 0; j < v.size(); ++j) { double d = floor(0.5 + v[j] * 10000.) / 10000.; astrm << d << " "; } astrm << endl; } delete o.attachment; }
// Compares the matches keypoints for parallel lines. Removes matches that are criss-crossing too much // We assume that license plates won't be upside-down or backwards. So expect lines to be closely parallel void FeatureMatcher::crisscrossFiltering(const vector<KeyPoint> queryKeypoints, const vector<DMatch> inputMatches, vector<DMatch> &outputMatches) { Rect crissCrossAreaVertical(0, 0, config->stateIdImageWidthPx, config->stateIdimageHeightPx * 2); Rect crissCrossAreaHorizontal(0, 0, config->stateIdImageWidthPx * 2, config->stateIdimageHeightPx); for (unsigned int i = 0; i < billMapping.size(); i++) { vector<DMatch> matchesForOnePlate; for (unsigned int j = 0; j < inputMatches.size(); j++) { if (inputMatches[j].imgIdx == (int) i) matchesForOnePlate.push_back(inputMatches[j]); } // For each plate, compare the lines for the keypoints (training image and query image) // go through each line between keypoints and filter out matches that are criss-crossing vector<LineSegment> vlines; vector<LineSegment> hlines; vector<int> matchIdx; for (unsigned int j = 0; j < matchesForOnePlate.size(); j++) { KeyPoint tkp = trainingImgKeypoints[i][matchesForOnePlate[j].trainIdx]; KeyPoint qkp = queryKeypoints[matchesForOnePlate[j].queryIdx]; vlines.push_back(LineSegment(tkp.pt.x, tkp.pt.y + config->stateIdimageHeightPx, qkp.pt.x, qkp.pt.y)); hlines.push_back(LineSegment(tkp.pt.x, tkp.pt.y, qkp.pt.x + config->stateIdImageWidthPx, qkp.pt.y)); matchIdx.push_back(j); } // Iterate through each line (n^2) removing the one with the most criss-crosses until there are none left. int mostIntersections = 1; while (mostIntersections > 0 && vlines.size() > 0) { int mostIntersectionsIndex = -1; mostIntersections = 0; for (unsigned int j = 0; j < vlines.size(); j++) { int intrCount = 0; for (unsigned int q = 0; q < vlines.size(); q++) { Point vintr = vlines[j].intersection(vlines[q]); Point hintr = hlines[j].intersection(hlines[q]); float vangleDiff = abs(vlines[j].angle - vlines[q].angle); float hangleDiff = abs(hlines[j].angle - hlines[q].angle); if (vintr.inside(crissCrossAreaVertical) && vangleDiff > 10) { intrCount++; } else if (hintr.inside(crissCrossAreaHorizontal) && hangleDiff > 10) { intrCount++; } } if (intrCount > mostIntersections) { mostIntersections = intrCount; mostIntersectionsIndex = j; } } if (mostIntersectionsIndex >= 0) { if (this->config->debugStateId) cout << "Filtered intersection! " << billMapping[i] << endl; vlines.erase(vlines.begin() + mostIntersectionsIndex); hlines.erase(hlines.begin() + mostIntersectionsIndex); matchIdx.erase(matchIdx.begin() + mostIntersectionsIndex); } } // Push the non-crisscrosses back on the list for (unsigned int j = 0; j < matchIdx.size(); j++) { outputMatches.push_back(matchesForOnePlate[matchIdx[j]]); } } }
CharacterSegmenter::CharacterSegmenter(Mat img, bool invertedColors, Config* config) { this->config = config; this->confidence = 0; if (this->config->debugCharSegmenter) cout << "Starting CharacterSegmenter" << endl; //CharacterRegion charRegion(img, debug); timespec startTime; getTime(&startTime); Mat img_gray(img.size(), CV_8U); cvtColor( img, img_gray, CV_BGR2GRAY ); //normalize(img_gray, img_gray, 0, 255, CV_MINMAX ); medianBlur(img_gray, img_gray, 3); if (invertedColors) bitwise_not(img_gray, img_gray); charAnalysis = new CharacterAnalysis(img_gray, config); charAnalysis->analyze(); if (this->config->debugCharSegmenter) { displayImage(config, "CharacterSegmenter Thresholds", drawImageDashboard(charAnalysis->thresholds, CV_8U, 3)); } if (this->config->debugCharSegmenter) { Mat img_contours(charAnalysis->bestThreshold.size(), CV_8U); charAnalysis->bestThreshold.copyTo(img_contours); cvtColor(img_contours, img_contours, CV_GRAY2RGB); vector<vector<Point> > allowedContours; for (int i = 0; i < charAnalysis->bestContours.size(); i++) { if (charAnalysis->bestCharSegments[i]) allowedContours.push_back(charAnalysis->bestContours[i]); } drawContours(img_contours, charAnalysis->bestContours, -1, // draw all contours cv::Scalar(255,0,0), // in blue 1); // with a thickness of 1 drawContours(img_contours, allowedContours, -1, // draw all contours cv::Scalar(0,255,0), // in green 1); // with a thickness of 1 if (charAnalysis->linePolygon.size() > 0) { line(img_contours, charAnalysis->linePolygon[0], charAnalysis->linePolygon[1], Scalar(255, 0, 255), 1); line(img_contours, charAnalysis->linePolygon[3], charAnalysis->linePolygon[2], Scalar(255, 0, 255), 1); } Mat bordered = addLabel(img_contours, "Best Contours"); imgDbgGeneral.push_back(bordered); } // Figure out the average character width float totalCharWidth = 0; float totalCharHeight = 0; if (charAnalysis->linePolygon.size() > 0) { this->top = LineSegment(charAnalysis->linePolygon[0].x, charAnalysis->linePolygon[0].y, charAnalysis->linePolygon[1].x, charAnalysis->linePolygon[1].y); this->bottom = LineSegment(charAnalysis->linePolygon[3].x, charAnalysis->linePolygon[3].y, charAnalysis->linePolygon[2].x, charAnalysis->linePolygon[2].y); for (int i = 0; i < charAnalysis->bestContours.size(); i++) { if (charAnalysis->bestCharSegments[i] == false) continue; Rect mr = boundingRect(charAnalysis->bestContours[i]); totalCharWidth += mr.width; totalCharHeight += mr.height; } int numSamples = charAnalysis->bestCharSegmentsCount; float avgCharWidth = totalCharWidth / numSamples; float avgCharHeight = totalCharHeight / numSamples; removeSmallContours(charAnalysis->thresholds, charAnalysis->allContours, avgCharWidth, avgCharHeight); // Do the histogram analysis to figure out char regions timespec startTime; getTime(&startTime); vector<Mat> allHistograms; vector<Rect> allBoxes; for (int i = 0; i < charAnalysis->allContours.size(); i++) { Mat histogramMask = Mat::zeros(charAnalysis->thresholds[i].size(), CV_8U); fillConvexPoly(histogramMask, charAnalysis->linePolygon.data(), charAnalysis->linePolygon.size(), Scalar(255,255,255)); VerticalHistogram vertHistogram(charAnalysis->thresholds[i], histogramMask); if (this->config->debugCharSegmenter) { Mat histoCopy(vertHistogram.debugImg.size(), vertHistogram.debugImg.type()); //vertHistogram.copyTo(histoCopy); cvtColor(vertHistogram.debugImg, histoCopy, CV_GRAY2RGB); allHistograms.push_back(histoCopy); } // float score = 0; vector<Rect> charBoxes = getHistogramBoxes(vertHistogram.debugImg, avgCharWidth, avgCharHeight, &score); if (this->config->debugCharSegmenter) { for (int cboxIdx = 0; cboxIdx < charBoxes.size(); cboxIdx++) { rectangle(allHistograms[i], charBoxes[cboxIdx], Scalar(0, 255, 0)); } Mat histDashboard = drawImageDashboard(allHistograms, allHistograms[0].type(), 3); displayImage(config, "Char seg histograms", histDashboard); } for (int z = 0; z < charBoxes.size(); z++) allBoxes.push_back(charBoxes[z]); //drawAndWait(&histogramMask); } float biggestCharWidth = avgCharWidth; // Compute largest char width for (int i = 0; i < allBoxes.size(); i++) { if (allBoxes[i].width > biggestCharWidth) biggestCharWidth = allBoxes[i].width; } if (config->debugTiming) { timespec endTime; getTime(&endTime); cout << " -- Character Segmentation Create and Score Histograms Time: " << diffclock(startTime, endTime) << "ms." << endl; } //ColorFilter colorFilter(img, charAnalysis->getCharacterMask()); vector<Rect> candidateBoxes = getBestCharBoxes(charAnalysis->thresholds[0], allBoxes, biggestCharWidth); if (this->config->debugCharSegmenter) { // Setup the dashboard images to show the cleaning filters for (int i = 0; i < charAnalysis->thresholds.size(); i++) { Mat cleanImg = Mat::zeros(charAnalysis->thresholds[i].size(), charAnalysis->thresholds[i].type()); Mat boxMask = getCharBoxMask(charAnalysis->thresholds[i], candidateBoxes); charAnalysis->thresholds[i].copyTo(cleanImg); bitwise_and(cleanImg, boxMask, cleanImg); cvtColor(cleanImg, cleanImg, CV_GRAY2BGR); for (int c = 0; c < candidateBoxes.size(); c++) rectangle(cleanImg, candidateBoxes[c], Scalar(0, 255, 0), 1); imgDbgCleanStages.push_back(cleanImg); } } getTime(&startTime); filterEdgeBoxes(charAnalysis->thresholds, candidateBoxes, biggestCharWidth, avgCharHeight); candidateBoxes = filterMostlyEmptyBoxes(charAnalysis->thresholds, candidateBoxes); candidateBoxes = combineCloseBoxes(candidateBoxes, biggestCharWidth); cleanCharRegions(charAnalysis->thresholds, candidateBoxes); cleanMostlyFullBoxes(charAnalysis->thresholds, candidateBoxes); //cleanBasedOnColor(thresholds, colorFilter.colorMask, candidateBoxes); candidateBoxes = filterMostlyEmptyBoxes(charAnalysis->thresholds, candidateBoxes); this->characters = candidateBoxes; if (config->debugTiming) { timespec endTime; getTime(&endTime); cout << " -- Character Segmentation Box cleaning/filtering Time: " << diffclock(startTime, endTime) << "ms." << endl; } if (this->config->debugCharSegmenter) { Mat imgDash = drawImageDashboard(charAnalysis->thresholds, CV_8U, 3); displayImage(config, "Segmentation after cleaning", imgDash); Mat generalDash = drawImageDashboard(this->imgDbgGeneral, this->imgDbgGeneral[0].type(), 2); displayImage(config, "Segmentation General", generalDash); Mat cleanImgDash = drawImageDashboard(this->imgDbgCleanStages, this->imgDbgCleanStages[0].type(), 3); displayImage(config, "Segmentation Clean Filters", cleanImgDash); } } if (config->debugTiming) { timespec endTime; getTime(&endTime); cout << "Character Segmenter Time: " << diffclock(startTime, endTime) << "ms." << endl; } }
/** * Creates a LineSegment from a template. */ LIBSBML_EXTERN LineSegment_t * LineSegment_createFrom (const LineSegment_t *temp) { return new(std::nothrow) LineSegment(temp ? *temp : LineSegment()); }
void CharacterAnalysis::analyze() { timespec startTime; getTimeMonotonic(&startTime); pipeline_data->clearThresholds(); pipeline_data->thresholds = produceThresholds(pipeline_data->crop_gray, config); timespec contoursStartTime; getTimeMonotonic(&contoursStartTime); pipeline_data->textLines.clear(); for (unsigned int i = 0; i < pipeline_data->thresholds.size(); i++) { TextContours tc(pipeline_data->thresholds[i]); allTextContours.push_back(tc); } if (config->debugTiming) { timespec contoursEndTime; getTimeMonotonic(&contoursEndTime); cout << " -- Character Analysis Find Contours Time: " << diffclock(contoursStartTime, contoursEndTime) << "ms." << endl; } //Mat img_equalized = equalizeBrightness(img_gray); timespec filterStartTime; getTimeMonotonic(&filterStartTime); for (unsigned int i = 0; i < pipeline_data->thresholds.size(); i++) { this->filter(pipeline_data->thresholds[i], allTextContours[i]); if (config->debugCharAnalysis) cout << "Threshold " << i << " had " << allTextContours[i].getGoodIndicesCount() << " good indices." << endl; } if (config->debugTiming) { timespec filterEndTime; getTimeMonotonic(&filterEndTime); cout << " -- Character Analysis Filter Time: " << diffclock(filterStartTime, filterEndTime) << "ms." << endl; } PlateMask plateMask(pipeline_data); plateMask.findOuterBoxMask(allTextContours); pipeline_data->hasPlateBorder = plateMask.hasPlateMask; pipeline_data->plateBorderMask = plateMask.getMask(); if (plateMask.hasPlateMask) { // Filter out bad contours now that we have an outer box mask... for (unsigned int i = 0; i < pipeline_data->thresholds.size(); i++) { filterByOuterMask(allTextContours[i]); } } int bestFitScore = -1; int bestFitIndex = -1; for (unsigned int i = 0; i < pipeline_data->thresholds.size(); i++) { int segmentCount = allTextContours[i].getGoodIndicesCount(); if (segmentCount > bestFitScore) { bestFitScore = segmentCount; bestFitIndex = i; bestThreshold = pipeline_data->thresholds[i]; bestContours = allTextContours[i]; } } if (this->config->debugCharAnalysis) cout << "Best fit score: " << bestFitScore << " Index: " << bestFitIndex << endl; if (bestFitScore <= 1) { pipeline_data->disqualified = true; pipeline_data->disqualify_reason = "Low best fit score in characteranalysis"; return; } //getColorMask(img, allContours, allHierarchy, charSegments); if (this->config->debugCharAnalysis) { Mat img_contours = bestContours.drawDebugImage(bestThreshold); displayImage(config, "Matching Contours", img_contours); } LineFinder lf(pipeline_data); vector<vector<Point> > linePolygons = lf.findLines(pipeline_data->crop_gray, bestContours); vector<TextLine> tempTextLines; for (unsigned int i = 0; i < linePolygons.size(); i++) { vector<Point> linePolygon = linePolygons[i]; LineSegment topLine = LineSegment(linePolygon[0].x, linePolygon[0].y, linePolygon[1].x, linePolygon[1].y); LineSegment bottomLine = LineSegment(linePolygon[3].x, linePolygon[3].y, linePolygon[2].x, linePolygon[2].y); vector<Point> textArea = getCharArea(topLine, bottomLine); TextLine textLine(textArea, linePolygon, pipeline_data->crop_gray.size()); tempTextLines.push_back(textLine); } filterBetweenLines(bestThreshold, bestContours, tempTextLines); // Sort the lines from top to bottom. std::sort(tempTextLines.begin(), tempTextLines.end(), sort_text_line); // Now that we've filtered a few more contours, re-do the text area. for (unsigned int i = 0; i < tempTextLines.size(); i++) { vector<Point> updatedTextArea = getCharArea(tempTextLines[i].topLine, tempTextLines[i].bottomLine); vector<Point> linePolygon = tempTextLines[i].linePolygon; if (updatedTextArea.size() > 0 && linePolygon.size() > 0) { pipeline_data->textLines.push_back(TextLine(updatedTextArea, linePolygon, pipeline_data->crop_gray.size())); } } pipeline_data->plate_inverted = isPlateInverted(); if (pipeline_data->textLines.size() > 0) { int confidenceDrainers = 0; int charSegmentCount = this->bestContours.getGoodIndicesCount(); if (charSegmentCount == 1) confidenceDrainers += 91; else if (charSegmentCount < 5) confidenceDrainers += (5 - charSegmentCount) * 10; // Use the angle for the first line -- assume they'll always be parallel for multi-line plates int absangle = abs(pipeline_data->textLines[0].topLine.angle); if (absangle > config->maxPlateAngleDegrees) confidenceDrainers += 91; else if (absangle > 1) confidenceDrainers += (config->maxPlateAngleDegrees - absangle) ; // If a multiline plate has only one line, disqualify if (pipeline_data->isMultiline && pipeline_data->textLines.size() < 2) { if (config->debugCharAnalysis) std::cout << "Did not detect multiple lines on multi-line plate" << std::endl; confidenceDrainers += 95; } if (confidenceDrainers >= 90) { pipeline_data->disqualified = true; pipeline_data->disqualify_reason = "Low confidence in characteranalysis"; } else { float confidence = 100 - confidenceDrainers; pipeline_data->confidence_weights.setScore("CHARACTER_ANALYSIS_SCORE", confidence, 1.0); } } else { pipeline_data->disqualified = true; pipeline_data->disqualify_reason = "No text lines found in characteranalysis"; } if (config->debugTiming) { timespec endTime; getTimeMonotonic(&endTime); cout << "Character Analysis Time: " << diffclock(startTime, endTime) << "ms." << endl; } // Draw debug dashboard if (this->pipeline_data->config->debugCharAnalysis && pipeline_data->textLines.size() > 0) { vector<Mat> tempDash; for (unsigned int z = 0; z < pipeline_data->thresholds.size(); z++) { Mat tmp(pipeline_data->thresholds[z].size(), pipeline_data->thresholds[z].type()); pipeline_data->thresholds[z].copyTo(tmp); cvtColor(tmp, tmp, CV_GRAY2BGR); tempDash.push_back(tmp); } Mat bestVal(this->bestThreshold.size(), this->bestThreshold.type()); this->bestThreshold.copyTo(bestVal); cvtColor(bestVal, bestVal, CV_GRAY2BGR); for (unsigned int z = 0; z < this->bestContours.size(); z++) { Scalar dcolor(255,0,0); if (this->bestContours.goodIndices[z]) dcolor = Scalar(0,255,0); drawContours(bestVal, this->bestContours.contours, z, dcolor, 1); } tempDash.push_back(bestVal); displayImage(config, "Character Region Step 1 Thresholds", drawImageDashboard(tempDash, bestVal.type(), 3)); } }
LineSegment Frustum::UnProjectLineSegment(float x, float y) const { vec nearPlanePos = NearPlanePos(x, y); vec farPlanePos = FarPlanePos(x, y); return LineSegment(nearPlanePos, farPlanePos); }
// Score a collection of lines as a possible license plate region. // If any segments are missing, extrapolate the missing pieces void PlateCorners::scoreHorizontals(int h1, int h2) { //if (this->debug) // cout << "PlateCorners::scorePlate" << endl; float score = 0; // Lower is better LineSegment top; LineSegment bottom; float charHeightToPlateHeightRatio = config->plateHeightMM / config->charHeightMM; float idealPixelHeight = this->charHeight * charHeightToPlateHeightRatio; if (h1 == NO_LINE && h2 == NO_LINE) { // return; Point centerLeft = charRegion->getCharBoxLeft().midpoint(); Point centerRight = charRegion->getCharBoxRight().midpoint(); LineSegment centerLine = LineSegment(centerLeft.x, centerLeft.y, centerRight.x, centerRight.y); top = centerLine.getParallelLine(idealPixelHeight / 2); bottom = centerLine.getParallelLine(-1 * idealPixelHeight / 2 ); score += SCORING_MISSING_SEGMENT_PENALTY_HORIZONTAL * 2; } else if (h1 != NO_LINE && h2 != NO_LINE) { top = this->plateLines->horizontalLines[h1]; bottom = this->plateLines->horizontalLines[h2]; } else if (h1 == NO_LINE && h2 != NO_LINE) { bottom = this->plateLines->horizontalLines[h2]; top = bottom.getParallelLine(idealPixelHeight); score += SCORING_MISSING_SEGMENT_PENALTY_HORIZONTAL; } else if (h1 != NO_LINE && h2 == NO_LINE) { top = this->plateLines->horizontalLines[h1]; bottom = top.getParallelLine(-1 * idealPixelHeight); score += SCORING_MISSING_SEGMENT_PENALTY_HORIZONTAL; } // Make sure this line is above our license plate letters if (top.isPointBelowLine(charRegion->getCharBoxTop().midpoint()) == false) return; // Make sure this line is below our license plate letters if (bottom.isPointBelowLine(charRegion->getCharBoxBottom().midpoint())) return; // We now have 4 possible lines. Let's put them to the test and score them... ///////////////////////////////////////////////////////////////////////// // Score "Boxiness" of the 4 lines. How close is it to a parallelogram? ///////////////////////////////////////////////////////////////////////// float horizontalAngleDiff = abs(top.angle - bottom.angle); score += (horizontalAngleDiff) * SCORING_BOXINESS_WEIGHT; // if (this->debug) // cout << "PlateCorners boxiness score: " << (horizontalAngleDiff + verticalAngleDiff) * SCORING_BOXINESS_WEIGHT << endl; ////////////////////////////////////////////////////////////////////////// // SCORE the shape wrt character position and height relative to position ////////////////////////////////////////////////////////////////////////// Point topPoint = top.midpoint(); Point botPoint = bottom.closestPointOnSegmentTo(topPoint); float plateHeightPx = distanceBetweenPoints(topPoint, botPoint); // Get the height difference float heightRatio = charHeight / plateHeightPx; float idealHeightRatio = (config->charHeightMM / config->plateHeightMM); //if (leftRatio < MIN_CHAR_HEIGHT_RATIO || leftRatio > MAX_CHAR_HEIGHT_RATIO || rightRatio < MIN_CHAR_HEIGHT_RATIO || rightRatio > MAX_CHAR_HEIGHT_RATIO) float heightRatioDiff = abs(heightRatio - idealHeightRatio); // Ideal ratio == ~.45 // Get the distance from the top and the distance from the bottom // Take the average distances from the corners of the character region to the top/bottom lines // float topDistance = distanceBetweenPoints(topMidLinePoint, charRegion->getCharBoxTop().midpoint()); // float bottomDistance = distanceBetweenPoints(bottomMidLinePoint, charRegion->getCharBoxBottom().midpoint()); // float idealTopDistance = charHeight * (TOP_WHITESPACE_HEIGHT_MM / CHARACTER_HEIGHT_MM); // float idealBottomDistance = charHeight * (BOTTOM_WHITESPACE_HEIGHT_MM / CHARACTER_HEIGHT_MM); // float distScore = abs(topDistance - idealTopDistance) + abs(bottomDistance - idealBottomDistance); score += heightRatioDiff * SCORING_PLATEHEIGHT_WEIGHT; ////////////////////////////////////////////////////////////////////////// // SCORE the middliness of the stuff. We want our top and bottom line to have the characters right towards the middle ////////////////////////////////////////////////////////////////////////// Point charAreaMidPoint = charRegion->getCharBoxLeft().midpoint(); Point topLineSpot = top.closestPointOnSegmentTo(charAreaMidPoint); Point botLineSpot = bottom.closestPointOnSegmentTo(charAreaMidPoint); float topDistanceFromMiddle = distanceBetweenPoints(topLineSpot, charAreaMidPoint); float bottomDistanceFromMiddle = distanceBetweenPoints(topLineSpot, charAreaMidPoint); float idealDistanceFromMiddle = idealPixelHeight / 2; float middleScore = abs(topDistanceFromMiddle - idealDistanceFromMiddle) + abs(bottomDistanceFromMiddle - idealDistanceFromMiddle); score += middleScore * SCORING_TOP_BOTTOM_SPACE_VS_CHARHEIGHT_WEIGHT; // if (this->debug) // { // cout << "PlateCorners boxiness score: " << avgRatio * SCORING_TOP_BOTTOM_SPACE_VS_CHARHEIGHT_WEIGHT << endl; // cout << "PlateCorners boxiness score: " << distScore * SCORING_PLATEHEIGHT_WEIGHT << endl; // } ////////////////////////////////////////////////////////////// // SCORE: the shape for angles matching the character region ////////////////////////////////////////////////////////////// float charanglediff = abs(charAngle - top.angle) + abs(charAngle - bottom.angle); score += charanglediff * SCORING_ANGLE_MATCHES_LPCHARS_WEIGHT; // if (this->debug) // cout << "PlateCorners boxiness score: " << charanglediff * SCORING_ANGLE_MATCHES_LPCHARS_WEIGHT << endl; if (score < this->bestHorizontalScore) { float scorecomponent; if (this->config->debugPlateCorners) { cout << "xx xx Score: charHeight " << this->charHeight << endl; cout << "xx xx Score: idealHeight " << idealPixelHeight << endl; cout << "xx xx Score: h1,h2= " << h1 << "," << h2 << endl; cout << "xx xx Score: Top= " << top.str() << endl; cout << "xx xx Score: Bottom= " << bottom.str() << endl; cout << "Horizontal breakdown Score:" << endl; cout << " -- Boxiness Score: " << horizontalAngleDiff << " -- Weight (" << SCORING_BOXINESS_WEIGHT << ")" << endl; scorecomponent = horizontalAngleDiff * SCORING_BOXINESS_WEIGHT; cout << " -- -- Score: " << scorecomponent << " = " << scorecomponent / score * 100 << "% of score" << endl; cout << " -- Height Ratio Diff Score: " << heightRatioDiff << " -- Weight (" << SCORING_PLATEHEIGHT_WEIGHT << ")" << endl; scorecomponent = heightRatioDiff * SCORING_PLATEHEIGHT_WEIGHT; cout << " -- -- " << scorecomponent << " = " << scorecomponent / score * 100 << "% of score" << endl; cout << " -- Distance Score: " << middleScore << " -- Weight (" << SCORING_TOP_BOTTOM_SPACE_VS_CHARHEIGHT_WEIGHT << ")" << endl; scorecomponent = middleScore * SCORING_TOP_BOTTOM_SPACE_VS_CHARHEIGHT_WEIGHT; cout << " -- -- Score: " << scorecomponent << " = " << scorecomponent / score * 100 << "% of score" << endl; cout << " -- Char angle Score: " << charanglediff << " -- Weight (" << SCORING_ANGLE_MATCHES_LPCHARS_WEIGHT << ")" << endl; scorecomponent = charanglediff * SCORING_ANGLE_MATCHES_LPCHARS_WEIGHT; cout << " -- -- Score: " << scorecomponent << " = " << scorecomponent / score * 100 << "% of score" << endl; cout << " -- Score: " << score << endl; } this->bestHorizontalScore = score; bestTop = LineSegment(top.p1.x, top.p1.y, top.p2.x, top.p2.y); bestBottom = LineSegment(bottom.p1.x, bottom.p1.y, bottom.p2.x, bottom.p2.y); } }
LineSegment Frustum::Edge(int edgeIndex) const { assume(0 <= edgeIndex && edgeIndex <= 11); switch(edgeIndex) { default: // For release builds where assume() is disabled, return always the first option if out-of-bounds. case 0: return LineSegment(CornerPoint(0), CornerPoint(1)); case 1: return LineSegment(CornerPoint(0), CornerPoint(2)); case 2: return LineSegment(CornerPoint(0), CornerPoint(4)); case 3: return LineSegment(CornerPoint(1), CornerPoint(3)); case 4: return LineSegment(CornerPoint(1), CornerPoint(5)); case 5: return LineSegment(CornerPoint(2), CornerPoint(3)); case 6: return LineSegment(CornerPoint(2), CornerPoint(6)); case 7: return LineSegment(CornerPoint(3), CornerPoint(7)); case 8: return LineSegment(CornerPoint(4), CornerPoint(5)); case 9: return LineSegment(CornerPoint(4), CornerPoint(6)); case 10: return LineSegment(CornerPoint(5), CornerPoint(7)); case 11: return LineSegment(CornerPoint(6), CornerPoint(7)); } }
bool PhysicalPolygon::intersectCircle(float radius, LineSegment displacement, sf::Vector2f& centerAfterCollision, sf::Vector2f& intersectionPoint, sf::Vector2f& intersectionNormal) const { float minDsq{MAX_FLOAT}; sf::Vector2f closestCenterAfterCollision; sf::Vector2f closestIntersectionPoint; sf::Vector2f closestIntersectionNormal; sf::Vector2f p1; sf::Vector2f p2; for (auto pVertex = vertices_.begin() + 1; pVertex != vertices_.end(); pVertex++) { sf::Vector2f normal(-(*(pVertex-1)-*pVertex).y, (*(pVertex-1)-*pVertex).x); normal = normalize( (dot(displacement.start - *pVertex, normal) > 0) ? normal : -normal ); Line shiftedLine(displacement.start - normal * radius, displacement.end - displacement.start); float t, u; if (lineIntersect(shiftedLine, LineSegment(*(pVertex - 1), *pVertex), t, u)) { if (t >= 0.0f && t <= 1.0f && u >= 0.0f && u <= 1.0f) { float dsq = lengthsq(t*shiftedLine.direction); if (dsq < minDsq) { minDsq = dsq; closestCenterAfterCollision = displacement.start + t*shiftedLine.direction; closestIntersectionPoint = shiftedLine.origin + t*shiftedLine.direction; closestIntersectionNormal = normal; p1 = *(pVertex-1); // end points to exclude p2 = *pVertex; } } } } for (auto pVertex = vertices_.begin(); pVertex != vertices_.end(); pVertex++) { if (*pVertex == p1 || *pVertex == p2) continue; sf::Vector2f v = displacement.end - displacement.start; sf::Vector2f p = *pVertex; sf::Vector2f w = p - displacement.start; float a = dot(w, v)/lengthsq(v); float b = radius * radius - lengthsq(w - a*v); if (b >= 0) { float d = a - sqrtf(b/dot(v, v)); if (d > 0.0f && d < 1.0f && d < sqrtf(minDsq)) { minDsq = d*d; closestCenterAfterCollision = displacement.start + d*v; closestIntersectionPoint = p; closestIntersectionNormal = closestCenterAfterCollision - p; } } } if (minDsq == MAX_FLOAT) return false; else { centerAfterCollision = closestCenterAfterCollision - 0.1f*normalize(displacement.end - displacement.start); intersectionPoint = closestIntersectionPoint; intersectionNormal = normalize(closestIntersectionNormal); } return true; }
// Returns a polygon "stripe" across the width of the character region. The lines are voted and the polygon starts at 0 and extends to image width vector<Point> CharacterAnalysis::getBestVotedLines(Mat img, vector<vector<Point> > contours, vector<bool> goodIndices) { //if (this->debug) // cout << "CharacterAnalysis::getBestVotedLines" << endl; vector<Point> bestStripe; vector<Rect> charRegions; for (int i = 0; i < contours.size(); i++) { if (goodIndices[i]) charRegions.push_back(boundingRect(contours[i])); } // Find the best fit line segment that is parallel with the most char segments if (charRegions.size() <= 1) { // Maybe do something about this later, for now let's just ignore } else { vector<LineSegment> topLines; vector<LineSegment> bottomLines; // Iterate through each possible char and find all possible lines for the top and bottom of each char segment for (int i = 0; i < charRegions.size() - 1; i++) { for (int k = i+1; k < charRegions.size(); k++) { //Mat tempImg; //result.copyTo(tempImg); Rect* leftRect; Rect* rightRect; if (charRegions[i].x < charRegions[k].x) { leftRect = &charRegions[i]; rightRect = &charRegions[k]; } else { leftRect = &charRegions[k]; rightRect = &charRegions[i]; } //rectangle(tempImg, *leftRect, Scalar(0, 255, 0), 2); //rectangle(tempImg, *rightRect, Scalar(255, 255, 255), 2); int x1, y1, x2, y2; if (leftRect->y > rightRect->y) // Rising line, use the top left corner of the rect { x1 = leftRect->x; x2 = rightRect->x; } else // falling line, use the top right corner of the rect { x1 = leftRect->x + leftRect->width; x2 = rightRect->x + rightRect->width; } y1 = leftRect->y; y2 = rightRect->y; //cv::line(tempImg, Point(x1, y1), Point(x2, y2), Scalar(0, 0, 255)); topLines.push_back(LineSegment(x1, y1, x2, y2)); if (leftRect->y > rightRect->y) // Rising line, use the bottom right corner of the rect { x1 = leftRect->x + leftRect->width; x2 = rightRect->x + rightRect->width; } else // falling line, use the bottom left corner of the rect { x1 = leftRect->x; x2 = rightRect->x; } y1 = leftRect->y + leftRect->height; y2 = rightRect->y + leftRect->height; //cv::line(tempImg, Point(x1, y1), Point(x2, y2), Scalar(0, 0, 255)); bottomLines.push_back(LineSegment(x1, y1, x2, y2)); //drawAndWait(&tempImg); } } int bestScoreIndex = 0; int bestScore = -1; int bestScoreDistance = -1; // Line segment distance is used as a tie breaker // Now, among all possible lines, find the one that is the best fit for (int i = 0; i < topLines.size(); i++) { float SCORING_MIN_THRESHOLD = 0.97; float SCORING_MAX_THRESHOLD = 1.03; int curScore = 0; for (int charidx = 0; charidx < charRegions.size(); charidx++) { float topYPos = topLines[i].getPointAt(charRegions[charidx].x); float botYPos = bottomLines[i].getPointAt(charRegions[charidx].x); float minTop = charRegions[charidx].y * SCORING_MIN_THRESHOLD; float maxTop = charRegions[charidx].y * SCORING_MAX_THRESHOLD; float minBot = (charRegions[charidx].y + charRegions[charidx].height) * SCORING_MIN_THRESHOLD; float maxBot = (charRegions[charidx].y + charRegions[charidx].height) * SCORING_MAX_THRESHOLD; if ( (topYPos >= minTop && topYPos <= maxTop) && (botYPos >= minBot && botYPos <= maxBot)) { curScore++; } //cout << "Slope: " << topslope << " yPos: " << topYPos << endl; //drawAndWait(&tempImg); } // Tie goes to the one with longer line segments if ((curScore > bestScore) || (curScore == bestScore && topLines[i].length > bestScoreDistance)) { bestScore = curScore; bestScoreIndex = i; // Just use x distance for now bestScoreDistance = topLines[i].length; } } if (this->config->debugCharAnalysis) { cout << "The winning score is: " << bestScore << endl; // Draw the winning line segment //Mat tempImg; //result.copyTo(tempImg); //cv::line(tempImg, topLines[bestScoreIndex].p1, topLines[bestScoreIndex].p2, Scalar(0, 0, 255), 2); //cv::line(tempImg, bottomLines[bestScoreIndex].p1, bottomLines[bestScoreIndex].p2, Scalar(0, 0, 255), 2); //displayImage(config, "Lines", tempImg); } //winningLines.push_back(topLines[bestScoreIndex]); //winningLines.push_back(bottomLines[bestScoreIndex]); Point topLeft = Point(0, topLines[bestScoreIndex].getPointAt(0) ); Point topRight = Point(img.cols, topLines[bestScoreIndex].getPointAt(img.cols)); Point bottomRight = Point(img.cols, bottomLines[bestScoreIndex].getPointAt(img.cols)); Point bottomLeft = Point(0, bottomLines[bestScoreIndex].getPointAt(0)); bestStripe.push_back(topLeft); bestStripe.push_back(topRight); bestStripe.push_back(bottomRight); bestStripe.push_back(bottomLeft); } return bestStripe; }