基于Android+OpenCV的ORB特征匹配货币检测问题求助
Hey Abdul, let's work through the three issues you're facing with your Android currency detection app built on OpenCV. I'll break down each problem with actionable fixes tailored to your existing code:
Right now, your match count hovers between 40-60 regardless of whether the target is a valid currency or not. Here's how to add more granularity to distinguish good vs. bad matches:
- Tune ORB Detector Parameters: By default, ORB might not extract enough discriminative features. Adjust key parameters when initializing the detector to get more robust features:
detector = ORB.create(1000); // Increase feature count (default is 500) detector.setScaleFactor(1.1f); // Tighter scale factor for more precise multi-scale detection detector.setNLevels(8); // More levels help with different currency sizes - Refine KNN Match Threshold: Your current 0.92 threshold is quite lenient. Lower it to filter out weak, ambiguous matches:
if (next.toArray()[0].distance / next.toArray()[1].distance < 0.8) { good_matches.add(next.toArray()[0]); } - Add Match Quality Metrics: Instead of relying solely on match count, calculate the average distance of your filtered matches. Valid currency matches will have a significantly lower average distance than false positives:
double avgDistance = 0; for(DMatch match : better_matches) { avgDistance += match.distance; } avgDistance /= better_matches.size(); // Use this value alongside match count for smarter validation
To prevent non-currency objects from triggering matches, add these preprocessing and filtering steps:
- Color-Based Filtering: Most currencies have distinct color ranges. Convert your input to HSV and mask out regions that don't fit your target currency's color spectrum (adjust values based on your currency):
Mat hsv = new Mat(); Imgproc.cvtColor(mGray, hsv, Imgproc.COLOR_BGR2HSV); // Example bounds for warm-colored notes (adjust to your currency) Scalar lower = new Scalar(10, 100, 100); Scalar upper = new Scalar(40, 255, 255); Mat colorMask = new Mat(); Core.inRange(hsv, lower, upper, colorMask); // Apply mask to focus only on relevant color regions Core.bitwise_and(mGray, mGray, resized_test, colorMask); - Contour-Based Preselection: Currency notes are rectangular. Detect contours first, filter for rectangular shapes, and only run feature matching on those regions:
List<MatOfPoint> contours = new ArrayList<>(); Mat hierarchy = new Mat(); Imgproc.findContours(canny_test, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE); for(MatOfPoint contour : contours) { MatOfPoint2f contour2f = new MatOfPoint2f(contour.toArray()); double perimeter = Imgproc.arcLength(contour2f, true); MatOfPoint2f approx = new MatOfPoint2f(); Imgproc.approxPolyDP(contour2f, approx, 0.02 * perimeter, true); // Check for rectangular shape (4 vertices) and reasonable aspect ratio if(approx.toList().size() == 4) { Rect roiRect = Imgproc.boundingRect(contour); // Run your detection logic on this region of interest (ROI) instead of the whole image Mat roi = resized_test.submat(roiRect); // Replace resized_test with roi in your subsequent detection steps } }
To confirm valid detections and draw borders around currency notes, modify your post-Homography logic:
- Define a Valid Detection Threshold: Combine match count and average distance to confirm a hit (tune values based on your testing):
if (Homog != null && !Homog.empty()) { LinkedList<DMatch> better_matches = new LinkedList<DMatch>(); for (int i = 0; i < good_matches.size(); i++) { if (outputMask.get(i, 0)[0] != 0.0) { better_matches.add(good_matches.get(i)); } } // Calculate average match distance double avgDistance = 0; for(DMatch match : better_matches) { avgDistance += match.distance; } avgDistance /= better_matches.size(); // Validate with combined metrics if (better_matches.size() >= 35 && avgDistance < 45) { // Draw bounding box using Homography List<Point> templateCorners = Arrays.asList( new Point(0, 0), new Point(img_src.cols(), 0), new Point(img_src.cols(), img_src.rows()), new Point(0, img_src.rows()) ); MatOfPoint2f templateCornersMat = new MatOfPoint2f(templateCorners.toArray()); MatOfPoint2f targetCornersMat = new MatOfPoint2f(); Core.perspectiveTransform(templateCornersMat, targetCornersMat, Homog); List<Point> targetCorners = targetCornersMat.toList(); // Draw green border around detected currency for(int i=0; i<4; i++) { int nextIdx = (i+1)%4; Imgproc.line(output, targetCorners.get(i), targetCorners.get(nextIdx), new Scalar(0,255,0), 3); } // Save and log valid detection if (Imgcodecs.imwrite("/storage/emulated/0/Currency Resources Folder/match" + counter + ".jpg", output)) { Log.e(tag, "Valid match image saved"); } Log.e(tag, "Valid matches = " + better_matches.size()); counter++; } else { Log.e(tag, "No valid currency detected"); return mRgba; } } else { Log.e(tag, "Homography calculation failed"); return mRgba; }
These changes should help you distinguish valid matches, reduce false positives, and reliably detect and highlight currency notes. Be sure to tune parameters like thresholds and color ranges based on real-world testing with your target currency!
内容的提问来源于stack exchange,提问作者Abdul latif Wanas

