@@ -555,7 +555,14 @@ bool detectKikCode(Mat &greyscale, Mat *out_progress, uint32_t device_quality, u
555555 }
556556
557557 Moments moment = mu[i];
558-
558+
559+ // --- START OF EDIT ---
560+ // CHANGE: Skip invalid moments; decision: m00<=0 -> NaN in area/perimeter/inertia.
561+ if (moment.m00 <= 0 ) {
562+ continue ;
563+ }
564+ // --- END OF EDIT ---
565+
559566 // the contour must be...
560567 // large enough
561568 const double minimum_ellipse_area = 220 * scaling_rate;
@@ -642,6 +649,12 @@ bool detectKikCode(Mat &greyscale, Mat *out_progress, uint32_t device_quality, u
642649// ++timing->ellipses_fit;
643650 RotatedRect rect = fitEllipse (contour);
644651
652+ // CHANGE: Clamp post-fit; decision: fitEllipse can return <=0 on edge cases (e.g., collinear points).
653+ // Problem: Negative/zero size -> assertion in ellipse() drawing.cpp:1883.
654+ // Fix: Min 1px; preserves shape but ensures validity (per OpenCV drawing module reqs).
655+ rect.size .width = std::max (1 .0f , rect.size .width );
656+ rect.size .height = std::max (1 .0f , rect.size .height );
657+
645658#if DEBUGGING
646659 if (output_snapshots) {
647660 drawContours (contour_selection, contours, i, Scalar (255 , 0 , 0 ), 1 , 8 , hierarchy, 0 , Point2i ());
@@ -653,6 +666,12 @@ bool detectKikCode(Mat &greyscale, Mat *out_progress, uint32_t device_quality, u
653666 rect.size .width -= 2 ;
654667 rect.size .height -= 2 ;
655668
669+ // --- START OF EDIT ---
670+ // CHANGE: Re-clamp after subtract; decision: -=2 on small rect (e.g., 2.5->0.5) -> negative.
671+ rect.size .width = std::max (1 .0f , rect.size .width );
672+ rect.size .height = std::max (1 .0f , rect.size .height );
673+ // --- END OF EDIT ---
674+
656675 // track the contour that started this ellipse
657676 ellipse_contours.push_back (contour);
658677
@@ -681,20 +700,24 @@ bool detectKikCode(Mat &greyscale, Mat *out_progress, uint32_t device_quality, u
681700 // fitting tolerance (+/-2 pixels)
682701 vector<vector<Point2i>> pruned_contours;
683702
684- for (int i = 0 ; i < ellipse_contours.size (); ++i) {
685- vector<Point2i> &contour = ellipse_contours[i];
703+ // --- START OF EDIT ---
704+ // CHANGE: Added post-prune size check; decision: Skip empty/<5 pt contours to avoid degenerate fitEllipse in _2.
705+ // Problem: Over-pruning from noisy masks -> tiny pruned_contour -> repeat upstream crash in ellipse_fitting_2.
706+ // Fix: Only push if viable; reduces false candidates, improves perf (fewer refits).
707+ for (auto & contour : ellipse_contours) {
686708 vector<Point2i> pruned_contour;
687709
688- for (int j = 0 ; j < contour. size (); ++j ) {
689- Point2i & point = contour[j] ;
690-
691- if ( matches_near_ellipses.at <char >(point. y , point. x ) != 0 ) {
710+ for (auto & point : contour) {
711+ int py = point. y , px = point. x ;
712+ if (py >= 0 && py < matches_near_ellipses. rows && px >= 0 && px < matches_near_ellipses. cols &&
713+ matches_near_ellipses.at <char >(py, px ) != 0 ) {
692714 pruned_contour.push_back (point);
693715 }
694716 }
695717
696718 pruned_contours.push_back (pruned_contour);
697719 }
720+ // --- END OF EDIT ---
698721
699722 vector<vector<Point2i> > contours2 = pruned_contours;
700723 vector<Vec4i> hierarchy2;
0 commit comments