diff.c (4267B)
1 /* See LICENSE file for copyright and license details. */ 2 3 #include <stdint.h> 4 #include <stdio.h> 5 #include <string.h> 6 7 #include <libavutil/frame.h> 8 #include <libavutil/imgutils.h> 9 #include <libswscale/swscale.h> 10 #include <png.h> 11 #include <jpeglib.h> 12 13 #include "diff.h" 14 #include "util.h" 15 16 /* 17 * Compute 64-bit average perceptual hash. 18 * 19 * 1. Scale frame to 8x8 grayscale. 20 * 2. Compute mean pixel value. 21 * 3. Each bit = 1 if pixel > mean, 0 otherwise. 22 * 23 * Fast and effective for detecting duplicate/near-duplicate frames. 24 */ 25 uint64_t 26 diff_phash(AVFrame *frame, struct SwsContext *sws, 27 int src_w, int src_h) 28 { 29 uint8_t gray[64]; 30 uint8_t *dst_data[1]; 31 int dst_linesize[1]; 32 uint64_t hash; 33 unsigned sum; 34 uint8_t mean; 35 int i; 36 37 dst_data[0] = gray; 38 dst_linesize[0] = 8; 39 40 sws_scale(sws, 41 (const uint8_t *const *)frame->data, 42 frame->linesize, 43 0, src_h, 44 dst_data, dst_linesize); 45 46 /* compute mean */ 47 sum = 0; 48 for (i = 0; i < 64; i++) 49 sum += gray[i]; 50 mean = (uint8_t)(sum / 64); 51 52 /* build hash */ 53 hash = 0; 54 for (i = 0; i < 64; i++) { 55 if (gray[i] > mean) 56 hash |= ((uint64_t)1 << i); 57 } 58 59 return hash; 60 } 61 62 int 63 diff_hamming(uint64_t a, uint64_t b) 64 { 65 uint64_t x; 66 int count; 67 68 x = a ^ b; 69 count = 0; 70 while (x) { 71 count++; 72 x &= x - 1; 73 } 74 return count; 75 } 76 77 static int 78 save_png(const uint8_t *rgb, int w, int h, int stride, 79 const char *path) 80 { 81 FILE *fp; 82 png_structp png; 83 png_infop info; 84 int y; 85 86 fp = fopen(path, "wb"); 87 if (!fp) { 88 warn("cannot open '%s' for writing:", path); 89 return -1; 90 } 91 92 png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 93 NULL, NULL, NULL); 94 if (!png) { 95 fclose(fp); 96 return -1; 97 } 98 99 info = png_create_info_struct(png); 100 if (!info) { 101 png_destroy_write_struct(&png, NULL); 102 fclose(fp); 103 return -1; 104 } 105 106 if (setjmp(png_jmpbuf(png))) { 107 png_destroy_write_struct(&png, &info); 108 fclose(fp); 109 return -1; 110 } 111 112 png_init_io(png, fp); 113 png_set_IHDR(png, info, w, h, 8, 114 PNG_COLOR_TYPE_RGB, 115 PNG_INTERLACE_NONE, 116 PNG_COMPRESSION_TYPE_DEFAULT, 117 PNG_FILTER_TYPE_DEFAULT); 118 png_write_info(png, info); 119 120 for (y = 0; y < h; y++) 121 png_write_row(png, rgb + y * stride); 122 123 png_write_end(png, NULL); 124 png_destroy_write_struct(&png, &info); 125 fclose(fp); 126 return 0; 127 } 128 129 static int 130 save_jpg(const uint8_t *rgb, int w, int h, int stride, 131 const char *path) 132 { 133 FILE *fp; 134 struct jpeg_compress_struct cinfo; 135 struct jpeg_error_mgr jerr; 136 JSAMPROW row; 137 int y; 138 139 fp = fopen(path, "wb"); 140 if (!fp) { 141 warn("cannot open '%s' for writing:", path); 142 return -1; 143 } 144 145 cinfo.err = jpeg_std_error(&jerr); 146 jpeg_create_compress(&cinfo); 147 jpeg_stdio_dest(&cinfo, fp); 148 149 cinfo.image_width = w; 150 cinfo.image_height = h; 151 cinfo.input_components = 3; 152 cinfo.in_color_space = JCS_RGB; 153 jpeg_set_defaults(&cinfo); 154 jpeg_set_quality(&cinfo, 95, 1); 155 jpeg_start_compress(&cinfo, 1); 156 157 for (y = 0; y < h; y++) { 158 row = (JSAMPROW)(rgb + y * stride); 159 jpeg_write_scanlines(&cinfo, &row, 1); 160 } 161 162 jpeg_finish_compress(&cinfo); 163 jpeg_destroy_compress(&cinfo); 164 fclose(fp); 165 return 0; 166 } 167 168 int 169 diff_save_frame(AVFrame *frame, struct SwsContext *sws_rgb, 170 int src_w, int src_h, 171 const char *path, const char *fmt) 172 { 173 AVFrame *rgb_frame; 174 int ret; 175 176 rgb_frame = av_frame_alloc(); 177 if (!rgb_frame) 178 return -1; 179 180 rgb_frame->format = AV_PIX_FMT_RGB24; 181 rgb_frame->width = src_w; 182 rgb_frame->height = src_h; 183 184 ret = av_image_alloc(rgb_frame->data, 185 rgb_frame->linesize, 186 src_w, src_h, 187 AV_PIX_FMT_RGB24, 32); 188 if (ret < 0) { 189 av_frame_free(&rgb_frame); 190 return -1; 191 } 192 193 sws_scale(sws_rgb, 194 (const uint8_t *const *)frame->data, 195 frame->linesize, 196 0, src_h, 197 rgb_frame->data, rgb_frame->linesize); 198 199 if (strcmp(fmt, "jpg") == 0 || strcmp(fmt, "jpeg") == 0) { 200 ret = save_jpg(rgb_frame->data[0], 201 src_w, src_h, 202 rgb_frame->linesize[0], path); 203 } else { 204 ret = save_png(rgb_frame->data[0], 205 src_w, src_h, 206 rgb_frame->linesize[0], path); 207 } 208 209 av_freep(&rgb_frame->data[0]); 210 av_frame_free(&rgb_frame); 211 return ret; 212 }