sframe

Simple Frame — extract unique frames from videos
git clone git clone https://git.krisyotam.com/krisyotam/sframe.git
Log | Files | Refs | README | LICENSE

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 }