3 * Copyright (C) 2015 Raspberry Pi Foundation
4 * Author: Thibault Saunier <thibault.saunier@collabora.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
21 #include <glib-object.h>
28 #include "gstvalidatessim.h"
34 #include <gst/video/video.h>
36 #define GST_CAT_DEFAULT gstvalidatessim_debug
37 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
39 #define SIMILARITY_ISSUE_WITH_PREVIOUS g_quark_from_static_string ("ssim::image-not-similar-enough-with-theoretical-reference")
40 #define SIMILARITY_ISSUE g_quark_from_static_string ("ssim::image-not-similar-enough")
41 #define GENERAL_INPUT_ERROR g_quark_from_static_string ("ssim::general-file-error")
42 #define WRONG_FORMAT g_quark_from_static_string ("ssim::wrong-format")
53 GstVideoConverter *converter;
55 GstVideoInfo out_info;
58 struct _GstValidateSsimPrivate
66 GstVideoInfo out_info;
68 SSimConverterInfo outconverter_info;
70 gfloat min_avg_similarity;
71 gfloat min_lowest_similarity;
73 GHashTable *ref_frames_cache;
77 G_DEFINE_TYPE_WITH_CODE (GstValidateSsim, gst_validate_ssim,
78 GST_TYPE_OBJECT, G_ADD_PRIVATE (GstValidateSsim)
79 G_IMPLEMENT_INTERFACE (GST_TYPE_VALIDATE_REPORTER, NULL));
82 ssim_convert_info_free (SSimConverterInfo * info)
85 gst_video_converter_free (info->converter);
87 g_slice_free (SSimConverterInfo, info);
91 gst_validate_ssim_convert (GstValidateSsim * self, SSimConverterInfo * info,
92 GstVideoFrame * frame, GstVideoFrame * converted_frame)
95 GstBuffer *outbuf = NULL;
97 g_return_val_if_fail (info != NULL, FALSE);
99 outbuf = gst_buffer_new_allocate (NULL, info->out_info.size, NULL);
100 if (!gst_video_frame_map (converted_frame, &info->out_info, outbuf,
102 GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR,
103 "Could not map output converted_frame");
107 gst_video_converter_frame (info->converter, frame, converted_frame);
111 gst_buffer_unref (outbuf);
121 gst_validate_ssim_save_out (GstValidateSsim * self, GstBuffer * buffer,
122 const gchar * ref_file, const gchar * file, const gchar * outfolder)
124 GstVideoFrame frame, converted;
126 if (!g_file_test (outfolder, G_FILE_TEST_IS_DIR)) {
127 if (g_mkdir_with_parents (outfolder, 0755) != 0) {
129 GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR,
130 "Could not create output directory %s", outfolder);
135 if (self->priv->outconverter_info.converter == NULL ||
136 self->priv->width != self->priv->outconverter_info.out_info.width ||
137 self->priv->height != self->priv->outconverter_info.out_info.height) {
139 if (self->priv->outconverter_info.converter)
140 gst_video_converter_free (self->priv->outconverter_info.converter);
142 gst_video_info_init (&self->priv->outconverter_info.in_info);
143 gst_video_info_set_format (&self->priv->outconverter_info.in_info,
144 GST_VIDEO_FORMAT_GRAY8, self->priv->width, self->priv->height);
146 gst_video_info_init (&self->priv->outconverter_info.out_info);
147 gst_video_info_set_format (&self->priv->outconverter_info.out_info,
148 GST_VIDEO_FORMAT_RGBx, self->priv->width, self->priv->height);
150 self->priv->outconverter_info.converter =
151 gst_video_converter_new (&self->priv->outconverter_info.in_info,
152 &self->priv->outconverter_info.out_info, NULL);
155 if (!gst_video_frame_map (&frame, &self->priv->outconverter_info.in_info,
156 buffer, GST_MAP_READ)) {
157 GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR,
158 "Could not map output frame");
163 if (gst_validate_ssim_convert (self, &self->priv->outconverter_info,
164 &frame, &converted)) {
165 cairo_status_t status;
166 cairo_surface_t *surface;
167 gchar *bn1 = g_path_get_basename (ref_file);
168 gchar *bn2 = g_path_get_basename (file);
169 gchar *fname = g_strdup_printf ("%s.VS.%s.result.png", bn1, bn2);
170 gchar *outfile = g_build_path (G_DIR_SEPARATOR_S, outfolder, fname, NULL);
173 cairo_image_surface_create_for_data (GST_VIDEO_FRAME_PLANE_DATA
174 (&converted, 0), CAIRO_FORMAT_RGB24, GST_VIDEO_FRAME_WIDTH (&converted),
175 GST_VIDEO_FRAME_HEIGHT (&converted),
176 GST_VIDEO_FRAME_PLANE_STRIDE (&converted, 0));
178 if ((status = cairo_surface_write_to_png (surface, outfile)) !=
179 CAIRO_STATUS_SUCCESS) {
180 GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR,
181 "Could not save '%s', cairo status is '%s'", outfile,
182 cairo_status_to_string (status));
185 cairo_surface_destroy (surface);
186 gst_video_frame_unmap (&frame);
187 gst_video_frame_unmap (&converted);
198 gst_validate_ssim_configure (GstValidateSsim * self, gint width, gint height)
200 if (width == self->priv->width && height == self->priv->height)
203 gssim_configure (self->priv->ssim, width, height);
205 self->priv->width = width;
206 self->priv->height = height;
208 gst_video_info_init (&self->priv->out_info);
209 gst_video_info_set_format (&self->priv->out_info, GST_VIDEO_FORMAT_I420,
210 self->priv->width, self->priv->height);
216 gst_validate_ssim_configure_converter (GstValidateSsim * self, gint index,
217 gboolean force, GstVideoFormat in_format, gint width, gint height)
219 SSimConverterInfo *info = g_list_nth_data (self->priv->converters, index);
222 info = g_slice_new0 (SSimConverterInfo);
224 self->priv->converters =
225 g_list_insert (self->priv->converters, info, index);
228 if (force || info->in_info.height != height || info->in_info.width != width ||
229 info->in_info.finfo->format != in_format) {
230 gst_video_info_init (&info->in_info);
231 gst_video_info_set_format (&info->in_info, in_format, width, height);
234 gst_video_converter_free (info->converter);
236 info->out_info = self->priv->out_info;
238 if (gst_video_info_is_equal (&info->in_info, &info->out_info))
239 info->converter = NULL;
242 gst_video_converter_new (&info->in_info, &info->out_info, NULL);
246 static GstVideoFormat
247 _get_format_from_surface (cairo_surface_t * surface)
249 #if G_BYTE_ORDER == G_BIG_ENDIAN
250 if (cairo_surface_get_content (surface) == CAIRO_CONTENT_COLOR_ALPHA)
251 return GST_VIDEO_FORMAT_BGRA;
253 return GST_VIDEO_FORMAT_BGRx;
255 if (cairo_surface_get_content (surface) == CAIRO_CONTENT_COLOR_ALPHA)
256 return GST_VIDEO_FORMAT_ARGB;
258 return GST_VIDEO_FORMAT_RGBx;
263 gst_validate_ssim_compare_frames (GstValidateSsim * self,
264 GstVideoFrame * ref_frame, GstVideoFrame * frame, GstBuffer ** outbuf,
265 gfloat * mean, gfloat * lowest, gfloat * highest)
268 guint8 *outdata = NULL;
269 GstMapInfo map1, map2, outmap;
271 GstVideoFrame converted_frame1, converted_frame2;
272 SSimConverterInfo *convinfo1, *convinfo2;
275 gst_validate_ssim_configure (self, ref_frame->info.width,
276 ref_frame->info.height);
278 gst_validate_ssim_configure_converter (self, 0, reconf,
279 ref_frame->info.finfo->format, ref_frame->info.width,
280 ref_frame->info.height);
282 gst_validate_ssim_configure_converter (self, 1, reconf,
283 frame->info.finfo->format, frame->info.width, frame->info.height);
285 convinfo1 = (SSimConverterInfo *) g_list_nth_data (self->priv->converters, 0);
286 if (convinfo1->converter)
287 gst_validate_ssim_convert (self, convinfo1, ref_frame, &converted_frame1);
289 converted_frame1 = *ref_frame;
291 convinfo2 = (SSimConverterInfo *) g_list_nth_data (self->priv->converters, 1);
292 if (convinfo2->converter)
293 gst_validate_ssim_convert (self, convinfo2, frame, &converted_frame2);
295 converted_frame2 = *frame;
297 if (!gst_buffer_map (converted_frame1.buffer, &map1, GST_MAP_READ)) {
298 GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR,
299 "Could not map reference frame");
304 if (!gst_buffer_map (converted_frame2.buffer, &map2, GST_MAP_READ)) {
305 gst_buffer_unmap (converted_frame1.buffer, &map1);
306 GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR,
307 "Could not map compared frame");
313 *outbuf = gst_buffer_new_and_alloc (GST_ROUND_UP_4 (self->priv->width) *
315 if (!gst_buffer_map (*outbuf, &outmap, GST_MAP_WRITE)) {
316 GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR,
317 "Could not map output frame");
319 gst_buffer_unref (*outbuf);
320 gst_buffer_unmap (converted_frame1.buffer, &map1);
321 gst_buffer_unmap (converted_frame2.buffer, &map2);
327 outdata = outmap.data;
330 gssim_compare (self->priv->ssim, map1.data, map2.data, outdata, mean,
333 gst_buffer_unmap (ref_frame->buffer, &map1);
334 gst_buffer_unmap (frame->buffer, &map2);
336 if (convinfo1->converter)
337 gst_video_frame_unmap (&converted_frame1);
338 if (convinfo2->converter)
339 gst_video_frame_unmap (&converted_frame2);
342 gst_buffer_unmap (*outbuf, &outmap);
346 gst_validate_ssim_get_frame_from_png (GstValidateSsim * self, const char *file,
347 GstVideoFrame * frame)
352 cairo_surface_t *surface = NULL;
354 surface = cairo_image_surface_create_from_png (file);
356 || (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS)) {
357 GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR, "Could not open %s: %s",
358 file, cairo_status_to_string (cairo_surface_status (surface)));
363 gst_video_info_init (&info);
364 gst_video_info_set_format (&info,
365 _get_format_from_surface (surface),
366 cairo_image_surface_get_width (surface),
367 cairo_image_surface_get_height (surface));
369 cairo_surface_flush (surface);
370 data = cairo_image_surface_get_data (surface);
371 buf = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY,
372 data, info.size, 0, info.size, surface,
373 (GDestroyNotify) cairo_surface_destroy);
374 if (!gst_video_frame_map (frame, &info, buf, GST_MAP_READ)) {
375 gst_buffer_unref (buf);
376 GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR,
377 "Could not map input frame");
382 gst_buffer_unref (buf);
388 gst_validate_ssim_get_frame_from_file (GstValidateSsim * self, const char *file,
389 GstVideoFrame * frame)
395 GstVideoFormat format;
396 gint strv_length, width, height;
399 gchar **splited_name = NULL, **splited_size = NULL, *strformat;
401 GError *error = NULL;
403 if (g_str_has_suffix (file, ".png")) {
404 return gst_validate_ssim_get_frame_from_png (self, file, frame);
407 splited_name = g_strsplit (file, ".", -1);
408 strv_length = g_strv_length (splited_name);
410 strformat = splited_name[strv_length - 1];
411 format = gst_video_format_from_string (strformat);
412 if (format == GST_VIDEO_FORMAT_UNKNOWN) {
413 GST_VALIDATE_REPORT (self, WRONG_FORMAT, "Unknown format: %s", strformat);
418 splited_size = g_strsplit (splited_name[strv_length - 2], "x", -1);
419 if (g_strv_length (splited_size) != 2) {
420 GST_VALIDATE_REPORT (self, WRONG_FORMAT,
421 "Can not determine video size from filename: %s ", file);
427 width = g_ascii_strtoull (splited_size[0], NULL, 10);
429 GST_VALIDATE_REPORT (self, WRONG_FORMAT,
430 "Can not determine video size from filename: %s ", file);
436 height = g_ascii_strtoull (splited_size[1], NULL, 10);
438 GST_VALIDATE_REPORT (self, WRONG_FORMAT,
439 "Can not determine video size from filename: %s ", file);
444 gst_video_info_init (&info);
445 gst_video_info_set_format (&info, format, width, height);
447 if (!g_file_get_contents (file, &data, &length, &error)) {
448 GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR, "Could not open %s: %s",
449 file, error->message);
450 g_error_free (error);
455 buf = gst_buffer_new_wrapped (data, length);
456 if (!gst_video_frame_map (frame, &info, buf, GST_MAP_READ)) {
457 gst_buffer_unref (buf);
458 GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR,
459 "Could not map input frame");
463 gst_buffer_unref (buf);
466 g_strfreev (splited_name);
467 g_strfreev (splited_size);
478 _filename_get_timestamp (GstValidateSsim * self, const gchar * filename,
482 gchar *bname = g_path_get_basename (filename);
483 gchar *other = g_strdup (bname);
486 if (sscanf (bname, "%" GST_VALIDATE_SSIM_TIME_FORMAT "%s", &h, &m, &s, &ns,
488 GST_INFO_OBJECT (self, "Can not sscanf %s", bname);
493 *ts = (h * 3600 + m * 60 + s) * GST_SECOND + ns;
512 _free_frame (Frame * frame)
514 g_free (frame->path);
518 _sort_frames (Frame * a, Frame * b)
530 _find_frame (GstValidateSsim * self, GArray * frames, GstClockTime ts,
534 Frame *lframe = &g_array_index (frames, Frame, 0);
536 if (self->priv->fps_n) {
537 gint64 frame_number = gst_util_uint64_scale (ts, self->priv->fps_n,
538 self->priv->fps_d * GST_SECOND);
540 if (frames->len < frame_number)
543 return &g_array_index (frames, Frame, frame_number);
546 if (frames->len == 1) {
547 Frame *iframe = &g_array_index (frames, Frame, 0);
549 if (iframe->ts == ts)
555 for (i = 1; i < frames->len; i++) {
556 Frame *iframe = &g_array_index (frames, Frame, i);
558 if (ts >= lframe->ts && iframe->ts > ts) {
563 } else if (i + 1 == frames->len) {
574 _get_ref_frame_cache (GstValidateSsim * self, const gchar * ref_file)
576 GFile *ref_dir_file = NULL;
578 GFileEnumerator *fenum;
579 GArray *frames = NULL;
580 gchar *ref_dir = NULL;
582 ref_dir = g_path_get_dirname (ref_file);
584 frames = g_hash_table_lookup (self->priv->ref_frames_cache, ref_file);
588 ref_dir_file = g_file_new_for_path (ref_dir);
589 if (!(fenum = g_file_enumerate_children (ref_dir_file,
590 "standard::*", G_FILE_QUERY_INFO_NONE, NULL, NULL))) {
591 GST_INFO ("%s is not a folder", ref_dir);
596 for (info = g_file_enumerator_next_file (fenum, NULL, NULL);
597 info; info = g_file_enumerator_next_file (fenum, NULL, NULL)) {
599 const gchar *display_name = g_file_info_get_display_name (info);
601 if (!_filename_get_timestamp (self, display_name, &iframe.ts)) {
602 g_object_unref (info);
606 iframe.path = g_build_path (G_DIR_SEPARATOR_S,
607 ref_dir, g_file_info_get_name (info), NULL);
609 g_object_unref (info);
612 frames = g_array_new (TRUE, TRUE, sizeof (Frame));
614 g_array_set_clear_func (frames, (GDestroyNotify) _free_frame);
616 g_array_append_val (frames, iframe);
618 g_object_unref (fenum);
621 g_array_sort (frames, (GCompareFunc) _sort_frames);
623 g_hash_table_insert (self->priv->ref_frames_cache, g_strdup (ref_dir),
628 g_clear_object (&ref_dir_file);
635 _get_ref_file_path (GstValidateSsim * self, const gchar * ref_file,
636 const gchar * file, gboolean get_next)
640 gchar *real_ref_file = NULL;
641 GstClockTime file_ts;
643 if (!g_strrstr (ref_file, "*"))
644 return g_strdup (ref_file);
646 if (!_filename_get_timestamp (self, file, &file_ts)) {
650 frames = _get_ref_frame_cache (self, ref_file);
652 frame = _find_frame (self, frames, file_ts, get_next);
655 real_ref_file = g_strdup (frame->path);
660 return real_ref_file;
664 gst_validate_ssim_compare_image_file (GstValidateSsim * self,
665 const gchar * ref_file, const gchar * file, gfloat * mean, gfloat * lowest,
666 gfloat * highest, const gchar * outfolder)
668 GstBuffer *outbuf = NULL, **poutbuf = NULL;
670 GstVideoFrame ref_frame, frame;
671 gchar *real_ref_file = NULL;
672 gchar *output_failure_image = NULL, *failure_info = NULL;
674 real_ref_file = _get_ref_file_path (self, ref_file, file, FALSE);
676 if (!real_ref_file) {
677 GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR,
678 "Could find ref file for %s", ref_file);
682 if (!gst_validate_ssim_get_frame_from_file (self, real_ref_file, &ref_frame))
686 if (!gst_validate_ssim_get_frame_from_file (self, file, &frame)) {
687 gst_video_frame_unmap (&ref_frame);
696 gst_validate_ssim_compare_frames (self, &ref_frame, &frame,
697 poutbuf, mean, lowest, highest);
699 if (*mean < self->priv->min_avg_similarity) {
700 GstClockTime ref_ts, f_ts;
702 gst_video_frame_unmap (&ref_frame);
703 gst_video_frame_unmap (&frame);
705 _filename_get_timestamp (self, real_ref_file, &ref_ts);
706 _filename_get_timestamp (self, file, &f_ts);
708 if (g_strcmp0 (ref_file, real_ref_file) && ref_ts != f_ts) {
709 gchar *tmpref = real_ref_file;
711 real_ref_file = _get_ref_file_path (self, ref_file, file, TRUE);
713 GST_VALIDATE_REPORT (self, SIMILARITY_ISSUE_WITH_PREVIOUS,
714 "\nComparing %s with %s failed, (mean %f "
715 " min %f), checking next %s\n",
716 tmpref, file, *mean, *lowest, real_ref_file);
720 res = gst_validate_ssim_compare_image_file (self,
721 real_ref_file, file, mean, lowest, highest, outfolder);
726 output_failure_image =
727 gst_validate_ssim_save_out (self, outbuf, real_ref_file, file,
730 if (output_failure_image)
732 g_strdup_printf (" (See %s to check differences in images)",
733 output_failure_image);
735 GST_VALIDATE_REPORT (self, SIMILARITY_ISSUE,
736 "Average similarity '%f' between %s and %s inferior"
737 " than the minimum average: %f%s", *mean,
738 real_ref_file, file, self->priv->min_avg_similarity, failure_info);
743 if (*lowest < self->priv->min_lowest_similarity) {
745 output_failure_image =
746 gst_validate_ssim_save_out (self, outbuf, real_ref_file, file,
749 if (output_failure_image)
751 g_strdup_printf (" (See %s to check differences in images)",
752 output_failure_image);
754 GST_VALIDATE_REPORT (self, SIMILARITY_ISSUE,
755 "Lowest similarity '%f' between %s and %s inferior"
756 " than the minimum lowest similarity: %f%s", *lowest,
757 real_ref_file, file, self->priv->min_lowest_similarity, failure_info);
759 gst_video_frame_unmap (&ref_frame);
760 gst_video_frame_unmap (&frame);
765 gst_video_frame_unmap (&ref_frame);
766 gst_video_frame_unmap (&frame);
770 g_free (failure_info);
771 g_free (output_failure_image);
772 g_free (real_ref_file);
774 gst_buffer_unref (outbuf);
785 _check_directory (GstValidateSsim * self, const gchar * ref_dir,
786 const gchar * compared_dir, gfloat * mean, gfloat * lowest,
787 gfloat * highest, const gchar * outfolder)
789 gint nfiles = 0, nnotfound = 0, nfailures = 0;
792 GFileEnumerator *fenum;
793 gfloat min_avg = 1.0, min_min = 1.0, total_avg = 0;
794 GFile *file = g_file_new_for_path (ref_dir);
796 if (!(fenum = g_file_enumerate_children (file,
797 "standard::*", G_FILE_QUERY_INFO_NONE, NULL, NULL))) {
798 GST_INFO ("%s is not a folder", ref_dir);
804 for (info = g_file_enumerator_next_file (fenum, NULL, NULL);
805 info; info = g_file_enumerator_next_file (fenum, NULL, NULL)) {
807 if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR ||
808 g_file_info_get_file_type (info) == G_FILE_TYPE_SYMBOLIC_LINK) {
809 gchar *compared_file = g_build_path (G_DIR_SEPARATOR_S,
810 compared_dir, g_file_info_get_name (info), NULL);
811 gchar *ref_file = NULL;
813 if (!g_file_test (compared_file, G_FILE_TEST_IS_REGULAR)) {
814 GST_INFO_OBJECT (self, "Could not find file %s", compared_file);
820 g_build_path (G_DIR_SEPARATOR_S, ref_dir,
821 g_file_info_get_name (info), NULL);
822 if (!gst_validate_ssim_compare_image_files (self, ref_file,
823 compared_file, mean, lowest, highest, outfolder)) {
831 min_avg = MIN (min_avg, *mean);
832 min_min = MIN (min_min, *lowest);
834 gst_validate_printf (NULL,
835 "<position: %s duration: %" GST_TIME_FORMAT
836 " avg: %f min: %f (Passed: %d failed: %d, %d not found)/>\r",
837 g_file_info_get_display_name (info),
838 GST_TIME_ARGS (GST_CLOCK_TIME_NONE),
839 *mean, *lowest, nfiles, nfailures, nnotfound);
841 g_free (compared_file);
845 g_object_unref (info);
849 gst_validate_printf (NULL, "\nNo files to verify.\n");
851 gst_validate_printf (NULL,
852 "\nAverage similarity: %f, min_avg: %f, min_min: %f\n",
853 total_avg / nfiles, min_avg, min_min);
857 gst_object_unref (file);
859 gst_object_unref (fenum);
865 gst_validate_ssim_compare_image_files (GstValidateSsim * self,
866 const gchar * ref_file, const gchar * file, gfloat * mean, gfloat * lowest,
867 gfloat * highest, const gchar * outfolder)
869 if (g_file_test (ref_file, G_FILE_TEST_IS_DIR)) {
870 if (!g_file_test (file, G_FILE_TEST_IS_DIR)) {
871 GST_VALIDATE_REPORT (self, GENERAL_INPUT_ERROR,
872 "%s is a directory but %s is not", ref_file, file);
877 return _check_directory (self, ref_file, file, mean, lowest, highest,
880 return gst_validate_ssim_compare_image_file (self, ref_file, file, mean,
881 lowest, highest, outfolder);
886 gst_validate_ssim_get_property (GObject * object,
887 guint property_id, GValue * value, GParamSpec * pspec)
889 switch (property_id) {
891 /* we assume the runner is valid as long as this scenario is,
893 g_value_set_object (value,
894 gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (object)));
902 gst_validate_ssim_set_property (GObject * object,
903 guint property_id, const GValue * value, GParamSpec * pspec)
905 switch (property_id) {
907 /* we assume the runner is valid as long as this scenario is,
909 gst_validate_reporter_set_runner (GST_VALIDATE_REPORTER (object),
910 g_value_get_object (value));
918 gst_validate_ssim_dispose (GObject * object)
920 GstValidateSsim *self = GST_VALIDATE_SSIM (object);
921 void (*chain_up) (GObject *) =
922 ((GObjectClass *) gst_validate_ssim_parent_class)->dispose;
924 gst_object_unref (self->priv->ssim);
930 gst_validate_ssim_finalize (GObject * object)
932 GstValidateSsim *self = GST_VALIDATE_SSIM (object);
933 void (*chain_up) (GObject *) =
934 ((GObjectClass *) gst_validate_ssim_parent_class)->finalize;
936 g_list_free_full (self->priv->converters,
937 (GDestroyNotify) ssim_convert_info_free);
939 if (self->priv->outconverter_info.converter)
940 gst_video_converter_free (self->priv->outconverter_info.converter);
941 g_hash_table_unref (self->priv->ref_frames_cache);
947 _register_issues (gpointer data)
949 gst_validate_issue_register (gst_validate_issue_new_full (SIMILARITY_ISSUE,
950 "Compared images were not similar enough",
951 "The images checker detected that the images"
952 " it is comparing do not have the similarity"
953 " level defined with min-avg-similarity or"
954 " min-lowest-similarity", GST_VALIDATE_REPORT_LEVEL_CRITICAL,
955 GST_VALIDATE_ISSUE_FLAGS_FULL_DETAILS |
956 GST_VALIDATE_ISSUE_FLAGS_NO_BACKTRACE));
958 gst_validate_issue_register (gst_validate_issue_new
959 (SIMILARITY_ISSUE_WITH_PREVIOUS,
960 "Comparison with theoretical reference image failed",
961 " In a case where we have reference frames with the following"
962 " timestamps: [0.00, 0.10, 0.20, 0.30], comparing a frame with"
963 " 0.05 as a timestamp will be done with the first frame."
964 " If this fails, a ssim::image-not-similar-enough-with-theoretical-reference"
965 " warning is issued and the system then tries with the second reference frame.",
966 GST_VALIDATE_REPORT_LEVEL_WARNING));
968 gst_validate_issue_register (gst_validate_issue_new (GENERAL_INPUT_ERROR,
969 "Something went wrong handling image files for ssim comparison",
970 "An error occurred when working with input files",
971 GST_VALIDATE_REPORT_LEVEL_CRITICAL));
973 gst_validate_issue_register (gst_validate_issue_new (WRONG_FORMAT,
974 "The format or dimensions of the compared images do not match",
975 "The format or dimensions of the compared images do not match",
976 GST_VALIDATE_REPORT_LEVEL_CRITICAL));
982 gst_validate_ssim_class_init (GstValidateSsimClass * klass)
984 GObjectClass *oclass = G_OBJECT_CLASS (klass);
985 static GOnce _once = G_ONCE_INIT;
987 GST_DEBUG_CATEGORY_INIT (gstvalidatessim_debug, "validatessim", 0,
988 "Validate ssim plugin");
990 oclass->get_property = gst_validate_ssim_get_property;
991 oclass->set_property = gst_validate_ssim_set_property;
992 oclass->dispose = gst_validate_ssim_dispose;
993 oclass->finalize = gst_validate_ssim_finalize;
995 g_once (&_once, _register_issues, NULL);
997 g_object_class_install_property (oclass, PROP_RUNNER,
998 g_param_spec_object ("validate-runner", "VALIDATE Runner",
999 "The Validate runner to report errors to",
1000 GST_TYPE_VALIDATE_RUNNER,
1001 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
1005 gst_validate_ssim_init (GstValidateSsim * self)
1007 self->priv = gst_validate_ssim_get_instance_private (self);
1009 self->priv->ssim = gssim_new ();
1010 self->priv->ref_frames_cache = g_hash_table_new_full (g_str_hash,
1011 g_str_equal, g_free, (GDestroyNotify) g_array_unref);
1015 gst_validate_ssim_new (GstValidateRunner * runner,
1016 gfloat min_avg_similarity, gfloat min_lowest_similarity,
1017 gint fps_n, gint fps_d)
1019 GstValidateSsim *self =
1020 g_object_new (GST_VALIDATE_SSIM_TYPE, "validate-runner", runner, NULL);
1022 self->priv->min_avg_similarity = min_avg_similarity;
1023 self->priv->min_lowest_similarity = min_lowest_similarity;
1024 self->priv->fps_n = fps_n;
1025 self->priv->fps_d = fps_d;
1027 gst_validate_reporter_set_name (GST_VALIDATE_REPORTER (self),
1028 g_strdup ("gst-validate-images-checker"));