Subversion Repositories SmartDukaan

Rev

Rev 37446 | Rev 37472 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 37446 Rev 37450
Line 2... Line 2...
2
 
2
 
3
import nu.pattern.OpenCV;
3
import nu.pattern.OpenCV;
4
import org.apache.commons.io.FileUtils;
4
import org.apache.commons.io.FileUtils;
5
import org.apache.logging.log4j.LogManager;
5
import org.apache.logging.log4j.LogManager;
6
import org.apache.logging.log4j.Logger;
6
import org.apache.logging.log4j.Logger;
-
 
7
import org.opencv.core.Core;
7
import org.opencv.core.Mat;
8
import org.opencv.core.Mat;
-
 
9
import org.opencv.core.Scalar;
8
import org.opencv.core.Size;
10
import org.opencv.core.Size;
9
import org.opencv.imgcodecs.Imgcodecs;
11
import org.opencv.imgcodecs.Imgcodecs;
10
import org.opencv.imgproc.Imgproc;
12
import org.opencv.imgproc.Imgproc;
11
import org.json.JSONObject;
13
import org.json.JSONObject;
12
import org.openqa.selenium.*;
14
import org.openqa.selenium.*;
Line 163... Line 165...
163
                                        + " - reloading. Cause: " + e.getMessage());
165
                                        + " - reloading. Cause: " + e.getMessage());
164
                                driver.get("https://support.oppo.com/in/warranty-check/");
166
                                driver.get("https://support.oppo.com/in/warranty-check/");
165
                                installResponseCapture(driver);
167
                                installResponseCapture(driver);
166
                                break;
168
                                break;
167
                            }
169
                            }
-
 
170
                            Double ringPageX = pieceCentreX(driver);
-
 
171
                            double[] circles = null;
-
 
172
                            if (ringPageX == null) {
-
 
173
                                LOGGER.warn("No ring element to anchor detection on");
-
 
174
                            } else {
-
 
175
                                int bgLeft = driver.findElement(
-
 
176
                                        By.xpath("//*[starts-with(@id, 'dx_captcha_basic_bg_')]")).getLocation().getX();
168
                            double[] circles = getMatCircles2(driver, fileName);
177
                                circles = getMatCircles2(driver, fileName, ringPageX - bgLeft);
-
 
178
                            }
169
                            if (circles != null) {
179
                            if (circles != null) {
170
                                double firstCircleX = circles[0];
180
                                double firstCircleX = circles[0];
171
                                double secondCircleX = circles[1];
181
                                double secondCircleX = circles[1];
172
                                double distance = Math.abs(firstCircleX - secondCircleX);
182
                                double distance = Math.abs(firstCircleX - secondCircleX);
173
                                LOGGER.info("Distance is " + distance);
183
                                LOGGER.info("Distance is " + distance);
Line 618... Line 628...
618
        Mat circles = new Mat();
628
        Mat circles = new Mat();
619
        Imgproc.HoughCircles(medianBlur, circles, Imgproc.HOUGH_GRADIENT, 1, 15, 50, 20, 17, 25);
629
        Imgproc.HoughCircles(medianBlur, circles, Imgproc.HOUGH_GRADIENT, 1, 15, 50, 20, 17, 25);
620
        return circles;
630
        return circles;
621
    }
631
    }
622
 
632
 
-
 
633
    /** Anything within this many px of the ring's known centre IS the ring, not the hole. */
-
 
634
    private static final double RING_EXCLUSION_PX = 30;
-
 
635
    /** A real hole sits at least this far below the frame's mean luminance. */
-
 
636
    private static final double MIN_HOLE_DARKNESS = 15;
-
 
637
 
-
 
638
    /** Mean luminance inside a detected circle, clamped to the image. */
-
 
639
    private double meanInside(Mat gray, double[] circle) {
-
 
640
        int r = (int) Math.max(3, circle[2] * 0.6);            // inner core, avoids the rim
-
 
641
        int x0 = (int) Math.max(0, circle[0] - r), y0 = (int) Math.max(0, circle[1] - r);
-
 
642
        int x1 = (int) Math.min(gray.cols(), circle[0] + r), y1 = (int) Math.min(gray.rows(), circle[1] + r);
-
 
643
        if (x1 - x0 < 2 || y1 - y0 < 2) return Double.MAX_VALUE;
-
 
644
        return Core.mean(gray.submat(y0, y1, x0, x1)).val[0];
-
 
645
    }
-
 
646
 
-
 
647
    /**
-
 
648
     * Locate the hole. The RING is not detected -- its exact position comes from the DOM
-
 
649
     * (dx_captcha_basic_sub-slider_), so hough only has to find one thing instead of two.
-
 
650
     *
-
 
651
     * The old version demanded TWO circles whose radii matched within 3px and returned
-
 
652
     * null otherwise, so a puzzle where the hole was found perfectly well still failed if
-
 
653
     * the ring was missed or the radii differed by 4px. It was also re-detecting something
-
 
654
     * already known exactly.
-
 
655
     *
-
 
656
     * param1 is the Canny HIGH threshold: edges weaker than it are discarded before circle
-
 
657
     * finding begins. At 100 a low-contrast hole produces no edge at all. Measured on four
-
 
658
     * live puzzles, hole-vs-background contrast was 98, 103, 28 and 117 -- the 28 (a pale
-
 
659
     * lilac background) cannot survive 100, which is the mechanism behind "60% of attempts
-
 
660
     * never find the circles". Dropped to 50.
-
 
661
     *
-
 
662
     * @param ringRelX ring centre in px relative to the bg element's left edge
-
 
663
     * @return {ringRelX, holeRelX} -- shape kept so callers are unchanged -- or null
-
 
664
     */
623
    private double[] getMatCircles2(WebDriver driver, String fileName) throws Exception {
665
    private double[] getMatCircles2(WebDriver driver, String fileName, double ringRelX) throws Exception {
624
        File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
666
        File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
625
        BufferedImage fullImg = ImageIO.read(screenshot);
667
        BufferedImage fullImg = ImageIO.read(screenshot);
626
 
668
 
627
        // Get the location of element on the page
669
        // Get the location of element on the page
628
        WebElement imageElement = driver.findElement(By.xpath("//*[starts-with(@id, 'dx_captcha_basic_bg_')]"));
670
        WebElement imageElement = driver.findElement(By.xpath("//*[starts-with(@id, 'dx_captcha_basic_bg_')]"));
Line 653... Line 695...
653
        Imgproc.GaussianBlur(gray, gray, new Size(9, 9), 2, 2);
695
        Imgproc.GaussianBlur(gray, gray, new Size(9, 9), 2, 2);
654
 
696
 
655
        Mat circles = new Mat();
697
        Mat circles = new Mat();
656
        Imgproc.HoughCircles(gray, circles, Imgproc.HOUGH_GRADIENT, 1.0,
698
        Imgproc.HoughCircles(gray, circles, Imgproc.HOUGH_GRADIENT, 1.0,
657
                20, // min distance between centers
699
                20, // min distance between centers
658
                100, 20, // param1 (Canny high threshold), param2 (accumulator threshold)
700
                50, 20, // param1 (Canny high threshold), param2 (accumulator threshold)
659
                10, 50); // minRadius, maxRadius (adjust as needed)
701
                10, 50); // minRadius, maxRadius
660
 
702
 
661
        if (circles.cols() < 2) {
703
        if (circles.cols() < 1) {
662
            new File(fileName).delete();
704
            new File(fileName).delete();
663
            return null;
705
            return null;
664
        }
706
        }
665
 
707
 
-
 
708
        // Dropping param1 to 50 lets weak edges through, which also lets textured
-
 
709
        // backgrounds (the sand images especially) produce spurious circles. Without the
-
 
710
        // old two-circle pairing there is nothing else to reject them, so verify the
-
 
711
        // candidate is actually a HOLE: it must be materially darker than the picture as
-
 
712
        // a whole. Measured on live puzzles the hole runs 28-117 luminance below the
-
 
713
        // median, so 15 rejects noise while keeping even the faintest real hole.
-
 
714
        Scalar frameMean = Core.mean(gray);
-
 
715
        // OpenCV returns circles strongest-accumulator first, so take the first candidate
-
 
716
        // that is not the ring and does look like a hole.
666
        for (int i = 0; i < circles.cols(); i++) {
717
        for (int i = 0; i < circles.cols(); i++) {
667
            double[] c1 = circles.get(0, i);
718
            double[] c = circles.get(0, i);
668
            for (int j = i + 1; j < circles.cols(); j++) {
719
            if (Math.abs(c[0] - ringRelX) < RING_EXCLUSION_PX) continue; // that is the ring
669
                double[] c2 = circles.get(0, j);
720
            double darkness = frameMean.val[0] - meanInside(gray, c);
670
                if (Math.abs(c1[2] - c2[2]) < 3) { // check similar radius
-
 
671
                    new File(fileName).delete();
721
            if (darkness < MIN_HOLE_DARKNESS) {
672
                    return new double[] {
722
                LOGGER.info("Rejecting circle at {}px: only {} darker than the frame",
673
                            c1[0], c2[0] // x-coordinates
723
                        Math.round(c[0]), Math.round(darkness));
674
                    };
724
                continue;
675
                }
-
 
676
            }
725
            }
-
 
726
            new File(fileName).delete();
-
 
727
            return new double[] { ringRelX, c[0] };
677
        }
728
        }
678
 
729
 
-
 
730
        LOGGER.info("Detected {} circle(s) but all sat on the ring at {}px", circles.cols(), Math.round(ringRelX));
679
        new File(fileName).delete();
731
        new File(fileName).delete();
680
        return null;
732
        return null;
681
    }
733
    }
682
 
734
 
683
 
735