Exemplo n.º 1
0
    def test_compute_diff_with_cropping_on_reference_image_with_exclusions_outside_reference_width(
            self):
        """
        issue #81: check that if exclusion zones are partially ouside of reference image (in width), computing is still done
        test_Image1Mod2.png is 1174x759
        test_Image1Crop.png is 1170x701
        
        We put an exclusion zone at Point(1169x700) of 2 pixel width, so that it goes outside of test_Image1Crop.png range of 1 pixel
        
        This checks that an exclusion zone can be set outside of reference image
        """

        comparator = PictureComparator()
        pixels_diff_map, diff_percentage, diff_image = comparator.get_changed_pixels(
            self.dataDir + 'test_Image1Crop.png',
            self.dataDir + 'test_Image1Mod2.png', [Rectangle(1169, 700, 2, 1)])
        pixels_diff_map = self.convert_points_to_pixels(pixels_diff_map)

        self.assertEqual(pixels_diff_map[0], Pixel(0, 0))
        self.assertFalse(
            Pixel(1169, 700) in pixels_diff_map
        )  # exclusion zone inside reference area is excluded from comparison
        self.assertFalse(
            Pixel(1170, 700) in pixels_diff_map
        )  # exclusion zone outside reference area is excluded from comparison
        self.assertTrue(
            Pixel(1171, 700) in pixels_diff_map
        )  # pixel outside of reference area and outside of exclusion zone is marked as a diff
        self.assertTrue(
            Pixel(1170, 699) in pixels_diff_map
        )  # pixel outside of reference area and outside of exclusion zone is marked as a diff
Exemplo n.º 2
0
 def test_compare_same_image(self):
     comparator = PictureComparator()
     rect = comparator.compare(self.dataDir + 'Ibis_Mulhouse.png',
                               self.dataDir + 'Ibis_Mulhouse.png')
     self.assertNotEqual(rect, None, "A matching should have been found")
     self.assertEqual(rect.x, 0)
     self.assertEqual(rect.y, 0)
Exemplo n.º 3
0
    def test_compute_diff_with_cropping_on_reference_image(self):
        """
        Check no error is raised when comparing 2 images with different sizes
        Here reference image is smaller than step, so diff will be shown on missing pixels
        """

        comparator = PictureComparator()
        pixels_diff_map, diff_percentage, diff_image = comparator.get_changed_pixels(
            self.dataDir + 'test_Image1Crop.png',
            self.dataDir + 'test_Image1.png', [])
        pixels_diff_map = self.convert_points_to_pixels(pixels_diff_map)

        # cropping on 58 lines. Check we have all points
        self.assertFalse(
            Pixel(0, 700) in pixels_diff_map
        )  # last line where comparison is possible => same line
        self.assertTrue(
            Pixel(0, 701) in pixels_diff_map
        )  # first line where step image is taller that reference on
        self.assertTrue(Pixel(0, 702) in pixels_diff_map)
        self.assertTrue(Pixel(1173, 758)
                        in pixels_diff_map)  # top right corner

        # cropping on 4 columns
        self.assertTrue(Pixel(1170, 0) in pixels_diff_map)
        self.assertTrue(Pixel(1173, 700) in pixels_diff_map)
        self.assertEqual(len(pixels_diff_map), 70896)
Exemplo n.º 4
0
 def test_no_diff(self):
     comparator = PictureComparator()
     diff_pixels, diff_percentage, diff_image = comparator.get_changed_pixels(
         self.dataDir + 'Ibis_Mulhouse.png',
         self.dataDir + 'Ibis_Mulhouse.png')
     self.assertEqual(0, len(diff_pixels),
                      "No difference pixels should be found")
     self.assertEqual(int(diff_percentage), 0)
Exemplo n.º 5
0
    def test_compute_diff_with_cropping_on_step_image(self):
        """
        Check no error is raised when comparing 2 images with different sizes
        Here step image is smaller than reference, so no diff will be shown
        """
        comparator = PictureComparator()
        pixels_diff_map, too_many_diffs, diff_image = comparator.get_changed_pixels(
            self.dataDir + 'test_Image1.png',
            self.dataDir + 'test_Image1Crop.png', [])

        self.assertEqual(len(pixels_diff_map), 0, "no diff should be found")
Exemplo n.º 6
0
    def test_real_too_many_diff(self):
        comparator = PictureComparator()
        diff_pixels, diff_percentage, diff_image = comparator.get_changed_pixels(
            self.dataDir + 'Ibis_Mulhouse.png',
            self.dataDir + 'Ibis_Mulhouse_tooManyDiffs.png')

        diff_pixels = self.convert_points_to_pixels(diff_pixels)
        self.assertEqual(817176, len(diff_pixels),
                         "207360 pixels should be found")
        self.assertEqual(diff_pixels[0], Pixel(0, 132),
                         "First diff pixel should be (0, 132)")
        self.assertTrue(int(diff_percentage), 39)
Exemplo n.º 7
0
    def test_real_diff(self):
        comparator = PictureComparator()
        diff_pixels, diff_percentage, diff_image = comparator.get_changed_pixels(
            self.dataDir + 'Ibis_Mulhouse.png',
            self.dataDir + 'Ibis_Mulhouse_diff.png')

        diff_pixels = self.convert_points_to_pixels(diff_pixels)

        self.assertEqual(3, len(diff_pixels), "3 pixels should be found")
        self.assertEqual(Pixel(554, 256), diff_pixels[0],
                         "detected position is wrong")
        self.assertTrue(diff_percentage > 0)
Exemplo n.º 8
0
 def test_real_diff_with_exclusion(self):
     """
     Test that if some exclude zones are defined, and covers the diff pixels, these pixels are not
     marked as diff
     """
     comparator = PictureComparator()
     diff_pixels, diff_percentage, diff_image = comparator.get_changed_pixels(
         self.dataDir + 'Ibis_Mulhouse.png',
         self.dataDir + 'Ibis_Mulhouse_diff.png',
         [Rectangle(550, 255, 5, 3)])
     diff_pixels = self.convert_points_to_pixels(diff_pixels)
     self.assertEqual(2, len(diff_pixels), "2 pixels should be found")
     self.assertEqual(Pixel(555, 256), diff_pixels[0],
                      "detected position is wrong")
Exemplo n.º 9
0
    def test_compute_diff_with_multiple_exclusions_on_datatable(self):
        """
        Check multiple exclusions are correctly computed
        """

        # image without differences where we add a square of different pixels
        image = numpy.zeros((6, 7), dtype=uint8)
        image[0][0] = 1
        image[1][0] = 1
        image[0][1] = 1
        image[1][1] = 1

        comparator = PictureComparator()
        diff_pixels, diff_image = comparator._build_list_of_changed_pixels(
            image, 7, 6, 7, 6, [Rectangle(1, 1, 3, 3),
                                Rectangle(0, 0, 5, 1)])
        diff_pixels = self.convert_points_to_pixels(diff_pixels)

        self.assertEqual([Pixel(x=0, y=1)], diff_pixels)
Exemplo n.º 10
0
    def test_compute_diff_with_exclusions_outside_step_image(self):
        """
        issue #81: check that if exclusion zones are ouside of step image, computing is still done
        test_Image1Mod2.png is 1174x759
        test_Image1Crop.png is 1170x701
        
        We put an exclusion zone at Point(0x758) of 2 pixel height, so that it goes outside of test_Image1Mod2.png range of 1 pixel
        """

        comparator = PictureComparator()
        pixels_diff_map, diff_percentage, diff_image = comparator.get_changed_pixels(
            self.dataDir + 'test_Image1Crop.png',
            self.dataDir + 'test_Image1Mod2.png', [Rectangle(0, 758, 1, 2)])
        pixels_diff_map = self.convert_points_to_pixels(pixels_diff_map)

        self.assertEqual(pixels_diff_map[0], Pixel(0, 0))
        self.assertFalse(
            Pixel(0, 758) in pixels_diff_map
        )  # exclusion zone outside reference area is excluded from comparison
        self.assertTrue(
            Pixel(0, 757) in pixels_diff_map
        )  # this pixel is outside reference, so, there is difference on it
Exemplo n.º 11
0
    def test_compute_diff_with_exclusions_outside_datatable(self):
        """
        Check exclusions are correctly computed
        Exclusion zone is outside the shape of datatable
        """

        # image without differences where we add a square of different pixels
        image = numpy.zeros((6, 7), dtype=uint8)
        image[0][0] = 1
        image[1][0] = 1
        image[0][1] = 1
        image[1][1] = 1

        comparator = PictureComparator()
        diff_pixels, diff_image = comparator._build_list_of_changed_pixels(
            image, 7, 6, 7, 6, [Rectangle(10, 10, 3, 3)])
        diff_pixels = self.convert_points_to_pixels(diff_pixels)
        self.assertEqual([
            Pixel(x=0, y=0),
            Pixel(x=1, y=0),
            Pixel(x=0, y=1),
            Pixel(x=1, y=1)
        ], diff_pixels)
Exemplo n.º 12
0
 def test_compare_with_unavailable_image(self):
     comparator = PictureComparator()
     self.assertRaisesRegex(PictureComparatorError, "^Image file",
                            comparator.compare,
                            self.dataDir + 'template_Ibis_Mulhouse.png',
                            self.dataDir + 'Ibis_Mulhou.png')
Exemplo n.º 13
0
 def test_compare_with_greater_image(self):
     comparator = PictureComparator()
     self.assertRaisesRegex(PictureComparatorError, "must be greater",
                            comparator.compare,
                            self.dataDir + 'template_Ibis_Mulhouse.png',
                            self.dataDir + 'Ibis_Mulhouse.png')
Exemplo n.º 14
0
 def test_compare_image_not_found(self):
     comparator = PictureComparator()
     rect = comparator.compare(self.dataDir + 'Ibis_Mulhouse.png',
                               self.dataDir + 'engie.png')
     self.assertEqual(rect, None, "A matching should not have been found")
Exemplo n.º 15
0
class DiffComputer(threading.Thread):
    """
    Class for processing differences asynchronously
    """

    _instance = None
    _jobLock = threading.Lock()
    _instanceLock = threading.Lock()
    picture_comparator = PictureComparator()

    @classmethod
    def get_instance(cls):
        with cls._instanceLock:
            if not cls._instance:
                cls._instance = DiffComputer()
                cls._instance.start()

                if not cls._instance.running:
                    time.sleep(0.1)

            return cls._instance

    def add_jobs(self, ref_snapshot, step_snapshot, check_test_mode=True):
        """
        Add a job to handle
        @param ref_snapshot: reference snapshot
        @param step_snapshot: snapshot to compare with reference
        @param check_test_mode: default is True. If True and we are in unit tests, computation is not done through thread
        """
        # as we will (re)compute, consider that current diff are not valid anymore
        step_snapshot.computed = False
        step_snapshot.save()

        if Tools.isTestMode() and check_test_mode:
            self.compute_now(ref_snapshot, step_snapshot)

        else:
            with DiffComputer._jobLock:
                self.jobs.append((ref_snapshot, step_snapshot))

    def compute_now(self, ref_snapshot, step_snapshot, save_snapshot=True):
        """
        Compute difference now
        @param ref_snapshot: the reference snapshot
        @param step_snapshot: the snapshot to compare to step_snapshot
        @param save_snapshot: (default True). When computing, snapshot is saved into database. By setting it to False, we prevent this
        """
        self._compute_diff(ref_snapshot, step_snapshot, save_snapshot)

    @classmethod
    def stopThread(cls):
        with cls._instanceLock:
            if cls._instance:
                cls._instance.running = False
                cls._instance.join()
            cls._instance = None

    def __init__(self):
        self.jobs = []
        self.running = False
        super(DiffComputer, self).__init__()

    def run(self):
        logger.info('starting compute thread')
        self.running = True

        # be sure we can restart a new thread if something goes wrong
        try:
            while self.running or self.jobs:
                with DiffComputer._jobLock:
                    tmp_jobs = self.jobs[:]
                    self.jobs = []

                for ref_snapshot, step_snapshot in tmp_jobs:
                    try:
                        self._compute_diff(ref_snapshot, step_snapshot)
                    except Exception as e:
                        logger.exception('Error computing snapshot: %s',
                                         str(e))
                time.sleep(0.5)

        except Exception as e:
            logger.exception('Exception during computing: %s', str(e))

        DiffComputer._instance = None

    def _compute_diff(self, ref_snapshot, step_snapshot, save_snapshot=True):
        """
        Compare all pixels from reference snapshto and step snapshot, and store difference to database
        """

        logger.info('computing')
        start = time.perf_counter()
        try:
            if ref_snapshot and step_snapshot and ref_snapshot.image and step_snapshot.image:

                # get the list of exclude zones
                exclude_zones = [
                    e.toRectangle() for e in ExcludeZone.objects.filter(
                        Q(snapshot=ref_snapshot) | Q(snapshot=step_snapshot))
                ]

                pixel_diffs, diff_percentage, diff_image = DiffComputer.picture_comparator.get_changed_pixels(
                    ref_snapshot.image.path, step_snapshot.image.path,
                    exclude_zones)

                # store diff picture mask into database instead of pixels, to reduce size of stored object
                #                 step_snapshot.pixelsDiff = self.mark_diff(step_snapshot.image.width, step_snapshot.image.height, pixel_diffs)
                step_snapshot.pixelsDiff = self.mark_diff(diff_image)

                # too many pixel differences if we go over tolerance
                step_snapshot.tooManyDiffs = step_snapshot.diffTolerance < diff_percentage
            else:
                step_snapshot.pixelsDiff = None
                step_snapshot.tooManyDiffs = False

            step_snapshot.refSnapshot = ref_snapshot
            step_snapshot.computingError = ''

        except PictureComparatorError:
            pass
        except Exception as e:
            step_snapshot.computingError = str(e)
        finally:
            # issue #81: be sure step_snapshot is marked as computed so that it never remain in "computing" state
            if step_snapshot:
                step_snapshot.computed = True

                if save_snapshot:
                    step_snapshot.save()
            logger.info('finished computing in %.2fs' %
                        (time.perf_counter() - start))

    def mark_diff(self, diff_image):
        """
        Save 'difference' pixels to a picture
        """
        is_success, buffer = cv2.imencode(".png", diff_image)
        return io.BytesIO(buffer).getvalue()