summaryrefslogtreecommitdiff
path: root/lib/igt_frame.c
diff options
context:
space:
mode:
authorPaul Kocialkowski <paul.kocialkowski@bootlin.com>2019-01-10 09:29:45 +0100
committerPaul Kocialkowski <paul.kocialkowski@bootlin.com>2019-02-26 09:45:54 +0100
commitc7f27710a63541b73fe72b579afc7250f87fdd89 (patch)
treef12e8e735e383ad7f2a008556645cdf1720272f0 /lib/igt_frame.c
parentcc28478682e35ced5cf45e9ef2ea8edd1d7cccb9 (diff)
lib/igt_frame: Add a checkerboard frame comparison method
This introduces a new frame comparison method that was designed for patterns that follow a checkerboard style. These patterns are made of consecutive rectangular shapes with alternating solid colors. They are currently used for some Chamelium-based tests. The method is particularly adapted for cases where the edges of the shapes might be blurred (e.g. due to scaling), which makes it impossible to use pixel-perfect or CRC-based comparisons to decide whether the captured frame matches the reference. Overall, this test will first detect the edges of the pattern and later exclude them from comparison. Colors are compared between the reference and capture with a low threshold for error. A percentage of the faulty pixels is calculated and the captured frame is considered invalid if more than one percent of the pixels are erroneous. Signed-off-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com> Reviewed-by: Lyude Paul <lyude@redhat.com> Reviewed-by: Maxime Ripard <maxime.ripard@bootlin.com>
Diffstat (limited to 'lib/igt_frame.c')
-rw-r--r--lib/igt_frame.c135
1 files changed, 135 insertions, 0 deletions
diff --git a/lib/igt_frame.c b/lib/igt_frame.c
index 6984c02e..45523a79 100644
--- a/lib/igt_frame.c
+++ b/lib/igt_frame.c
@@ -267,3 +267,138 @@ complete:
return match;
}
+
+#define XR24_COLOR_VALUE(data, stride, x, y, c) \
+ *((uint8_t *)(data) + (y) * (stride) + 4 * (x) + (c))
+
+/**
+ * igt_check_checkerboard_frame_match:
+ * @reference: The reference cairo surface
+ * @capture: The captured cairo surface
+ *
+ * Checks that the reference frame matches the captured frame using a
+ * method designed for checkerboard patterns. These patterns are made of
+ * consecutive rectangular shapes with alternating solid colors.
+ *
+ * The intent of this method is to cover cases where the captured result is
+ * not pixel-perfect due to features such as scaling or YUV conversion and
+ * subsampling. Such effects are mostly noticeable on the edges of the
+ * patterns, so they are detected and excluded from the comparison.
+ *
+ * The algorithm works with two major steps. First, the edges of the reference
+ * pattern are detected on the x and y axis independently. The detection is done
+ * by calculating an absolute difference with a span of a few pixels before and
+ * after the current position on the given axis, accumulated for each color
+ * component. If the sum is above a given threshold on one of the axis, the
+ * position is marked as an edge. In the second step, the pixel values are
+ * compared per-component, excluding positions that were marked as edges or
+ * that are at a transition between an edge and a non-edge. An error threshold
+ * (for each individual color component) is used to mark the position as
+ * erroneous or not. The ratio of erroneous pixels over compared pixels (that
+ * does not count excluded pixels) is then calculated and compared to the error
+ * rate threshold to determine whether the frames match or not.
+ *
+ * Returns: a boolean indicating whether the frames match
+ */
+bool igt_check_checkerboard_frame_match(cairo_surface_t *reference,
+ cairo_surface_t *capture)
+{
+ unsigned int width, height, ref_stride, cap_stride;
+ void *ref_data, *cap_data;
+ unsigned char *edges_map;
+ unsigned int x, y, c;
+ unsigned int errors = 0, pixels = 0;
+ unsigned int edge_threshold = 100;
+ unsigned int color_error_threshold = 24;
+ double error_rate_threshold = 0.01;
+ double error_rate;
+ unsigned int span = 2;
+ bool match = false;
+
+ width = cairo_image_surface_get_width(reference);
+ height = cairo_image_surface_get_height(reference);
+
+ ref_stride = cairo_image_surface_get_stride(reference);
+ ref_data = cairo_image_surface_get_data(reference);
+ igt_assert(ref_data);
+
+ cap_stride = cairo_image_surface_get_stride(capture);
+ cap_data = cairo_image_surface_get_data(capture);
+ igt_assert(cap_data);
+
+ edges_map = calloc(1, width * height);
+ igt_assert(edges_map);
+
+ /* First pass to detect the pattern edges. */
+ for (y = 0; y < height; y++) {
+ if (y < span || y > (height - span - 1))
+ continue;
+
+ for (x = 0; x < width; x++) {
+ unsigned int xdiff = 0, ydiff = 0;
+
+ if (x < span || x > (width - span - 1))
+ continue;
+
+ for (c = 0; c < 3; c++) {
+ xdiff += abs(XR24_COLOR_VALUE(ref_data, ref_stride, x + span, y, c) -
+ XR24_COLOR_VALUE(ref_data, ref_stride, x - span, y, c));
+ ydiff += abs(XR24_COLOR_VALUE(ref_data, ref_stride, x, y + span, c) -
+ XR24_COLOR_VALUE(ref_data, ref_stride, x, y - span, c));
+ }
+
+ edges_map[y * width + x] = (xdiff > edge_threshold ||
+ ydiff > edge_threshold);
+ }
+ }
+
+ /* Second pass to detect errors. */
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ bool error = false;
+
+ if (edges_map[y * width + x])
+ continue;
+
+ for (c = 0; c < 3; c++) {
+ unsigned int diff;
+
+ /* Compare the reference and capture values. */
+ diff = abs(XR24_COLOR_VALUE(ref_data, ref_stride, x, y, c) -
+ XR24_COLOR_VALUE(cap_data, cap_stride, x, y, c));
+
+ if (diff > color_error_threshold)
+ error = true;
+ }
+
+ /* Allow error if coming on or off an edge (on x). */
+ if (error && x >= span && x <= (width - span - 1) &&
+ edges_map[y * width + (x - span)] !=
+ edges_map[y * width + (x + span)])
+ continue;
+
+ /* Allow error if coming on or off an edge (on y). */
+ if (error && y >= span && y <= (height - span - 1) &&
+ edges_map[(y - span) * width + x] !=
+ edges_map[(y + span) * width + x] && error)
+ continue;
+
+ if (error)
+ errors++;
+
+ pixels++;
+ }
+ }
+
+ free(edges_map);
+
+ error_rate = (double) errors / pixels;
+
+ if (error_rate < error_rate_threshold)
+ match = true;
+
+ igt_debug("Checkerboard pattern %s with error rate %f %%\n",
+ match ? "matched" : "not matched", error_rate * 100);
+
+ return match;
+}