00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #include <stdlib.h>
00039 #include <fcntl.h>
00040 #include <unistd.h>
00041 #include <stdarg.h>
00042 #include <string.h>
00043 #include <time.h>
00044 #include <stdio.h>
00045 #include <dirent.h>
00046
00047 #include "libavformat/avformat.h"
00048 #include "libavformat/framehook.h"
00049 #include "libavcodec/dsputil.h"
00050 #include "libswscale/swscale.h"
00051 #undef fprintf
00052
00053 static int sws_flags = SWS_BICUBIC;
00054
00055 #define SCALEBITS 10
00056 #define ONE_HALF (1 << (SCALEBITS - 1))
00057 #define FIX(x) ((int) ((x) * (1<<SCALEBITS) + 0.5))
00058
00059 #define YUV_TO_RGB1_CCIR(cb1, cr1)\
00060 {\
00061 cb = (cb1) - 128;\
00062 cr = (cr1) - 128;\
00063 r_add = FIX(1.40200*255.0/224.0) * cr + ONE_HALF;\
00064 g_add = - FIX(0.34414*255.0/224.0) * cb - FIX(0.71414*255.0/224.0) * cr + \
00065 ONE_HALF;\
00066 b_add = FIX(1.77200*255.0/224.0) * cb + ONE_HALF;\
00067 }
00068
00069 #define YUV_TO_RGB2_CCIR(r, g, b, y1)\
00070 {\
00071 yt = ((y1) - 16) * FIX(255.0/219.0);\
00072 r = cm[(yt + r_add) >> SCALEBITS];\
00073 g = cm[(yt + g_add) >> SCALEBITS];\
00074 b = cm[(yt + b_add) >> SCALEBITS];\
00075 }
00076
00077
00078
00079
00080 typedef struct {
00081 int h;
00082 int s;
00083 int v;
00084 } HSV;
00085
00086 typedef struct {
00087 int zapping;
00088 int threshold;
00089 HSV dark, bright;
00090 char *dir;
00091 int file_limit;
00092 int debug;
00093 int min_interval;
00094 int64_t next_pts;
00095 int inset;
00096 int min_width;
00097 struct SwsContext *toRGB_convert_ctx;
00098 } ContextInfo;
00099
00100 static void dorange(const char *s, int *first, int *second, int maxval)
00101 {
00102 sscanf(s, "%d-%d", first, second);
00103 if (*first > maxval)
00104 *first = maxval;
00105 if (*second > maxval)
00106 *second = maxval;
00107 }
00108
00109 void Release(void *ctx)
00110 {
00111 ContextInfo *ci;
00112 ci = (ContextInfo *) ctx;
00113
00114 if (ctx) {
00115 sws_freeContext(ci->toRGB_convert_ctx);
00116 av_free(ctx);
00117 }
00118 }
00119
00120 int Configure(void **ctxp, int argc, char *argv[])
00121 {
00122 ContextInfo *ci;
00123 int c;
00124
00125 *ctxp = av_mallocz(sizeof(ContextInfo));
00126 ci = (ContextInfo *) *ctxp;
00127
00128 optind = 1;
00129
00130 ci->dir = av_strdup("/tmp");
00131 ci->threshold = 100;
00132 ci->file_limit = 100;
00133 ci->min_interval = 1000000;
00134 ci->inset = 10;
00135
00136 while ((c = getopt(argc, argv, "w:i:dh:s:v:zl:t:D:")) > 0) {
00137 switch (c) {
00138 case 'h':
00139 dorange(optarg, &ci->dark.h, &ci->bright.h, 360);
00140 break;
00141 case 's':
00142 dorange(optarg, &ci->dark.s, &ci->bright.s, 255);
00143 break;
00144 case 'v':
00145 dorange(optarg, &ci->dark.v, &ci->bright.v, 255);
00146 break;
00147 case 'z':
00148 ci->zapping = 1;
00149 break;
00150 case 'l':
00151 ci->file_limit = atoi(optarg);
00152 break;
00153 case 'i':
00154 ci->min_interval = 1000000 * atof(optarg);
00155 break;
00156 case 't':
00157 ci->threshold = atof(optarg) * 1000;
00158 if (ci->threshold > 1000 || ci->threshold < 0) {
00159 av_log(NULL, AV_LOG_ERROR, "Invalid threshold value '%s' (range is 0-1)\n", optarg);
00160 return -1;
00161 }
00162 break;
00163 case 'w':
00164 ci->min_width = atoi(optarg);
00165 break;
00166 case 'd':
00167 ci->debug++;
00168 break;
00169 case 'D':
00170 ci->dir = av_strdup(optarg);
00171 break;
00172 default:
00173 av_log(NULL, AV_LOG_ERROR, "Unrecognized argument '%s'\n", argv[optind]);
00174 return -1;
00175 }
00176 }
00177
00178 av_log(NULL, AV_LOG_INFO, "Fish detector configured:\n");
00179 av_log(NULL, AV_LOG_INFO, " HSV range: %d,%d,%d - %d,%d,%d\n",
00180 ci->dark.h,
00181 ci->dark.s,
00182 ci->dark.v,
00183 ci->bright.h,
00184 ci->bright.s,
00185 ci->bright.v);
00186 av_log(NULL, AV_LOG_INFO, " Threshold is %d%% pixels\n", ci->threshold / 10);
00187
00188
00189 return 0;
00190 }
00191
00192 static void get_hsv(HSV *hsv, int r, int g, int b)
00193 {
00194 int i, v, x, f;
00195
00196 x = (r < g) ? r : g;
00197 if (b < x)
00198 x = b;
00199 v = (r > g) ? r : g;
00200 if (b > v)
00201 v = b;
00202
00203 if (v == x) {
00204 hsv->h = 0;
00205 hsv->s = 0;
00206 hsv->v = v;
00207 return;
00208 }
00209
00210 if (r == v) {
00211 f = g - b;
00212 i = 0;
00213 } else if (g == v) {
00214 f = b - r;
00215 i = 2 * 60;
00216 } else {
00217 f = r - g;
00218 i = 4 * 60;
00219 }
00220
00221 hsv->h = i + (60 * f) / (v - x);
00222 if (hsv->h < 0)
00223 hsv->h += 360;
00224
00225 hsv->s = (255 * (v - x)) / v;
00226 hsv->v = v;
00227
00228 return;
00229 }
00230
00231 void Process(void *ctx, AVPicture *picture, enum PixelFormat pix_fmt, int width, int height, int64_t pts)
00232 {
00233 ContextInfo *ci = (ContextInfo *) ctx;
00234 uint8_t *cm = ff_cropTbl + MAX_NEG_CROP;
00235 int rowsize = picture->linesize[0];
00236
00237 #if 0
00238 av_log(NULL, AV_LOG_DEBUG, "pix_fmt = %d, width = %d, pts = %lld, ci->next_pts = %lld\n",
00239 pix_fmt, width, pts, ci->next_pts);
00240 #endif
00241
00242 if (pts < ci->next_pts)
00243 return;
00244
00245 if (width < ci->min_width)
00246 return;
00247
00248 ci->next_pts = pts + 1000000;
00249
00250 if (pix_fmt == PIX_FMT_YUV420P) {
00251 uint8_t *y, *u, *v;
00252 int width2 = width >> 1;
00253 int inrange = 0;
00254 int pixcnt;
00255 int h;
00256 int h_start, h_end;
00257 int w_start, w_end;
00258
00259 h_end = 2 * ((ci->inset * height) / 200);
00260 h_start = height - h_end;
00261
00262 w_end = (ci->inset * width2) / 100;
00263 w_start = width2 - w_end;
00264
00265 pixcnt = ((h_start - h_end) >> 1) * (w_start - w_end);
00266
00267 y = picture->data[0] + h_end * picture->linesize[0] + w_end * 2;
00268 u = picture->data[1] + h_end * picture->linesize[1] / 2 + w_end;
00269 v = picture->data[2] + h_end * picture->linesize[2] / 2 + w_end;
00270
00271 for (h = h_start; h > h_end; h -= 2) {
00272 int w;
00273
00274 for (w = w_start; w > w_end; w--) {
00275 unsigned int r,g,b;
00276 HSV hsv;
00277 int cb, cr, yt, r_add, g_add, b_add;
00278
00279 YUV_TO_RGB1_CCIR(u[0], v[0]);
00280 YUV_TO_RGB2_CCIR(r, g, b, y[0]);
00281
00282 get_hsv(&hsv, r, g, b);
00283
00284 if (ci->debug > 1)
00285 av_log(NULL, AV_LOG_DEBUG, "(%d,%d,%d) -> (%d,%d,%d)\n",
00286 r,g,b,hsv.h,hsv.s,hsv.v);
00287
00288
00289 if (hsv.h >= ci->dark.h && hsv.h <= ci->bright.h &&
00290 hsv.s >= ci->dark.s && hsv.s <= ci->bright.s &&
00291 hsv.v >= ci->dark.v && hsv.v <= ci->bright.v) {
00292 inrange++;
00293 } else if (ci->zapping) {
00294 y[0] = y[1] = y[rowsize] = y[rowsize + 1] = 16;
00295 u[0] = 128;
00296 v[0] = 128;
00297 }
00298
00299 y+= 2;
00300 u++;
00301 v++;
00302 }
00303
00304 y += picture->linesize[0] * 2 - (w_start - w_end) * 2;
00305 u += picture->linesize[1] - (w_start - w_end);
00306 v += picture->linesize[2] - (w_start - w_end);
00307 }
00308
00309 if (ci->debug)
00310 av_log(NULL, AV_LOG_INFO, "Fish: Inrange=%d of %d = %d threshold\n", inrange, pixcnt, 1000 * inrange / pixcnt);
00311
00312 if (inrange * 1000 / pixcnt >= ci->threshold) {
00313
00314 int size;
00315 char *buf;
00316 AVPicture picture1;
00317 static int frame_counter;
00318 static int foundfile;
00319
00320 if ((frame_counter++ % 20) == 0) {
00321
00322 DIR *d;
00323
00324 foundfile = 0;
00325
00326 d = opendir(ci->dir);
00327 if (d) {
00328 struct dirent *dent;
00329
00330 while ((dent = readdir(d))) {
00331 if (strncmp("fishimg", dent->d_name, 7) == 0) {
00332 if (strcmp(".ppm", dent->d_name + strlen(dent->d_name) - 4) == 0) {
00333 foundfile++;
00334 }
00335 }
00336 }
00337 closedir(d);
00338 }
00339 }
00340
00341 if (foundfile < ci->file_limit) {
00342 FILE *f;
00343 char fname[256];
00344
00345 size = avpicture_get_size(PIX_FMT_RGB24, width, height);
00346 buf = av_malloc(size);
00347
00348 avpicture_fill(&picture1, buf, PIX_FMT_RGB24, width, height);
00349
00350
00351 ci->toRGB_convert_ctx = sws_getCachedContext(ci->toRGB_convert_ctx,
00352 width, height, pix_fmt,
00353 width, height, PIX_FMT_RGB24,
00354 sws_flags, NULL, NULL, NULL);
00355 if (ci->toRGB_convert_ctx == NULL) {
00356 av_log(NULL, AV_LOG_ERROR,
00357 "Cannot initialize the toRGB conversion context\n");
00358 return;
00359 }
00360
00361
00362 sws_scale(ci->toRGB_convert_ctx,
00363 picture->data, picture->linesize, 0, height,
00364 picture1.data, picture1.linesize);
00365
00366
00367 snprintf(fname, sizeof(fname), "%s/fishimg%ld_%"PRId64".ppm", ci->dir, (long)(av_gettime() / 1000000), pts);
00368 f = fopen(fname, "w");
00369 if (f) {
00370 fprintf(f, "P6 %d %d 255\n", width, height);
00371 if (!fwrite(buf, width * height * 3, 1, f))
00372 av_log(ctx, AV_LOG_ERROR, "Couldn't write to PPM file %s\n", fname);
00373 fclose(f);
00374 }
00375
00376 av_free(buf);
00377 ci->next_pts = pts + ci->min_interval;
00378 }
00379 }
00380 }
00381 }
00382