Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4349 | Serge | 1 | /* |
2 | * ColorMatrix v2.2 for Avisynth 2.5.x |
||
3 | * |
||
4 | * Copyright (C) 2006-2007 Kevin Stone |
||
5 | * |
||
6 | * ColorMatrix 1.x is Copyright (C) Wilbert Dijkhof |
||
7 | * |
||
8 | * This program is free software; you can redistribute it and/or modify it |
||
9 | * under the terms of the GNU General Public License as published by the |
||
10 | * Free Software Foundation; either version 2 of the License, or (at your |
||
11 | * option) any later version. |
||
12 | * |
||
13 | * This program is distributed in the hope that it will be useful, but |
||
14 | * OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
||
15 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public |
||
16 | * License for more details. |
||
17 | * |
||
18 | * You should have received a copy of the GNU General Public License |
||
19 | * along with this program; if not, write to the Free Software Foundation, |
||
20 | * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||
21 | */ |
||
22 | |||
23 | /** |
||
24 | * @file |
||
25 | * ColorMatrix 2.0 is based on the original ColorMatrix filter by Wilbert |
||
26 | * Dijkhof. It adds the ability to convert between any of: Rec.709, FCC, |
||
27 | * Rec.601, and SMPTE 240M. It also makes pre and post clipping optional, |
||
28 | * adds an option to use scaled or non-scaled coefficients, and more... |
||
29 | */ |
||
30 | |||
31 | #include |
||
32 | #include "avfilter.h" |
||
33 | #include "formats.h" |
||
34 | #include "internal.h" |
||
35 | #include "video.h" |
||
36 | #include "libavutil/opt.h" |
||
37 | #include "libavutil/pixdesc.h" |
||
38 | #include "libavutil/avstring.h" |
||
39 | |||
40 | #define NS(n) n < 0 ? (int)(n*65536.0-0.5+DBL_EPSILON) : (int)(n*65536.0+0.5) |
||
41 | #define CB(n) av_clip_uint8(n) |
||
42 | |||
43 | static const double yuv_coeff[4][3][3] = { |
||
44 | { { +0.7152, +0.0722, +0.2126 }, // Rec.709 (0) |
||
45 | { -0.3850, +0.5000, -0.1150 }, |
||
46 | { -0.4540, -0.0460, +0.5000 } }, |
||
47 | { { +0.5900, +0.1100, +0.3000 }, // FCC (1) |
||
48 | { -0.3310, +0.5000, -0.1690 }, |
||
49 | { -0.4210, -0.0790, +0.5000 } }, |
||
50 | { { +0.5870, +0.1140, +0.2990 }, // Rec.601 (ITU-R BT.470-2/SMPTE 170M) (2) |
||
51 | { -0.3313, +0.5000, -0.1687 }, |
||
52 | { -0.4187, -0.0813, +0.5000 } }, |
||
53 | { { +0.7010, +0.0870, +0.2120 }, // SMPTE 240M (3) |
||
54 | { -0.3840, +0.5000, -0.1160 }, |
||
55 | { -0.4450, -0.0550, +0.5000 } }, |
||
56 | }; |
||
57 | |||
58 | enum ColorMode { |
||
59 | COLOR_MODE_NONE = -1, |
||
60 | COLOR_MODE_BT709, |
||
61 | COLOR_MODE_FCC, |
||
62 | COLOR_MODE_BT601, |
||
63 | COLOR_MODE_SMPTE240M, |
||
64 | COLOR_MODE_COUNT |
||
65 | }; |
||
66 | |||
67 | typedef struct { |
||
68 | const AVClass *class; |
||
69 | int yuv_convert[16][3][3]; |
||
70 | int interlaced; |
||
71 | enum ColorMode source, dest; |
||
72 | int mode; |
||
73 | int hsub, vsub; |
||
74 | } ColorMatrixContext; |
||
75 | |||
76 | #define OFFSET(x) offsetof(ColorMatrixContext, x) |
||
77 | #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM |
||
78 | |||
79 | static const AVOption colormatrix_options[] = { |
||
80 | { "src", "set source color matrix", OFFSET(source), AV_OPT_TYPE_INT, {.i64=COLOR_MODE_NONE}, COLOR_MODE_NONE, COLOR_MODE_COUNT-1, .flags=FLAGS, .unit="color_mode" }, |
||
81 | { "dst", "set destination color matrix", OFFSET(dest), AV_OPT_TYPE_INT, {.i64=COLOR_MODE_NONE}, COLOR_MODE_NONE, COLOR_MODE_COUNT-1, .flags=FLAGS, .unit="color_mode" }, |
||
82 | { "bt709", "set BT.709 colorspace", 0, AV_OPT_TYPE_CONST, {.i64=COLOR_MODE_BT709}, .flags=FLAGS, .unit="color_mode" }, |
||
83 | { "fcc", "set FCC colorspace ", 0, AV_OPT_TYPE_CONST, {.i64=COLOR_MODE_FCC}, .flags=FLAGS, .unit="color_mode" }, |
||
84 | { "bt601", "set BT.601 colorspace", 0, AV_OPT_TYPE_CONST, {.i64=COLOR_MODE_BT601}, .flags=FLAGS, .unit="color_mode" }, |
||
85 | { "smpte240m", "set SMPTE-240M colorspace", 0, AV_OPT_TYPE_CONST, {.i64=COLOR_MODE_SMPTE240M}, .flags=FLAGS, .unit="color_mode" }, |
||
86 | { NULL } |
||
87 | }; |
||
88 | |||
89 | AVFILTER_DEFINE_CLASS(colormatrix); |
||
90 | |||
91 | #define ma m[0][0] |
||
92 | #define mb m[0][1] |
||
93 | #define mc m[0][2] |
||
94 | #define md m[1][0] |
||
95 | #define me m[1][1] |
||
96 | #define mf m[1][2] |
||
97 | #define mg m[2][0] |
||
98 | #define mh m[2][1] |
||
99 | #define mi m[2][2] |
||
100 | |||
101 | #define ima im[0][0] |
||
102 | #define imb im[0][1] |
||
103 | #define imc im[0][2] |
||
104 | #define imd im[1][0] |
||
105 | #define ime im[1][1] |
||
106 | #define imf im[1][2] |
||
107 | #define img im[2][0] |
||
108 | #define imh im[2][1] |
||
109 | #define imi im[2][2] |
||
110 | |||
111 | static void inverse3x3(double im[3][3], const double m[3][3]) |
||
112 | { |
||
113 | double det = ma * (me * mi - mf * mh) - mb * (md * mi - mf * mg) + mc * (md * mh - me * mg); |
||
114 | det = 1.0 / det; |
||
115 | ima = det * (me * mi - mf * mh); |
||
116 | imb = det * (mc * mh - mb * mi); |
||
117 | imc = det * (mb * mf - mc * me); |
||
118 | imd = det * (mf * mg - md * mi); |
||
119 | ime = det * (ma * mi - mc * mg); |
||
120 | imf = det * (mc * md - ma * mf); |
||
121 | img = det * (md * mh - me * mg); |
||
122 | imh = det * (mb * mg - ma * mh); |
||
123 | imi = det * (ma * me - mb * md); |
||
124 | } |
||
125 | |||
126 | static void solve_coefficients(double cm[3][3], double rgb[3][3], const double yuv[3][3]) |
||
127 | { |
||
128 | int i, j; |
||
129 | for (i = 0; i < 3; i++) |
||
130 | for (j = 0; j < 3; j++) |
||
131 | cm[i][j] = yuv[i][0] * rgb[0][j] + yuv[i][1] * rgb[1][j] + yuv[i][2] * rgb[2][j]; |
||
132 | } |
||
133 | |||
134 | static void calc_coefficients(AVFilterContext *ctx) |
||
135 | { |
||
136 | ColorMatrixContext *color = ctx->priv; |
||
137 | double rgb_coeffd[4][3][3]; |
||
138 | double yuv_convertd[16][3][3]; |
||
139 | int v = 0; |
||
140 | int i, j, k; |
||
141 | |||
142 | for (i = 0; i < 4; i++) |
||
143 | inverse3x3(rgb_coeffd[i], yuv_coeff[i]); |
||
144 | for (i = 0; i < 4; i++) { |
||
145 | for (j = 0; j < 4; j++) { |
||
146 | solve_coefficients(yuv_convertd[v], rgb_coeffd[i], yuv_coeff[j]); |
||
147 | for (k = 0; k < 3; k++) { |
||
148 | color->yuv_convert[v][k][0] = NS(yuv_convertd[v][k][0]); |
||
149 | color->yuv_convert[v][k][1] = NS(yuv_convertd[v][k][1]); |
||
150 | color->yuv_convert[v][k][2] = NS(yuv_convertd[v][k][2]); |
||
151 | } |
||
152 | if (color->yuv_convert[v][0][0] != 65536 || color->yuv_convert[v][1][0] != 0 || |
||
153 | color->yuv_convert[v][2][0] != 0) { |
||
154 | av_log(ctx, AV_LOG_ERROR, "error calculating conversion coefficients\n"); |
||
155 | } |
||
156 | v++; |
||
157 | } |
||
158 | } |
||
159 | } |
||
160 | |||
161 | static const char *color_modes[] = {"bt709", "fcc", "bt601", "smpte240m"}; |
||
162 | |||
163 | static av_cold int init(AVFilterContext *ctx) |
||
164 | { |
||
165 | ColorMatrixContext *color = ctx->priv; |
||
166 | |||
167 | if (color->source == COLOR_MODE_NONE || color->dest == COLOR_MODE_NONE) { |
||
168 | av_log(ctx, AV_LOG_ERROR, "Unspecified source or destination color space\n"); |
||
169 | return AVERROR(EINVAL); |
||
170 | } |
||
171 | |||
172 | if (color->source == color->dest) { |
||
173 | av_log(ctx, AV_LOG_ERROR, "Source and destination color space must not be identical\n"); |
||
174 | return AVERROR(EINVAL); |
||
175 | } |
||
176 | |||
177 | color->mode = color->source * 4 + color->dest; |
||
178 | |||
179 | calc_coefficients(ctx); |
||
180 | |||
181 | return 0; |
||
182 | } |
||
183 | |||
184 | static void process_frame_uyvy422(ColorMatrixContext *color, |
||
185 | AVFrame *dst, AVFrame *src) |
||
186 | { |
||
187 | const unsigned char *srcp = src->data[0]; |
||
188 | const int src_pitch = src->linesize[0]; |
||
189 | const int height = src->height; |
||
190 | const int width = src->width*2; |
||
191 | unsigned char *dstp = dst->data[0]; |
||
192 | const int dst_pitch = dst->linesize[0]; |
||
193 | const int c2 = color->yuv_convert[color->mode][0][1]; |
||
194 | const int c3 = color->yuv_convert[color->mode][0][2]; |
||
195 | const int c4 = color->yuv_convert[color->mode][1][1]; |
||
196 | const int c5 = color->yuv_convert[color->mode][1][2]; |
||
197 | const int c6 = color->yuv_convert[color->mode][2][1]; |
||
198 | const int c7 = color->yuv_convert[color->mode][2][2]; |
||
199 | int x, y; |
||
200 | |||
201 | for (y = 0; y < height; y++) { |
||
202 | for (x = 0; x < width; x += 4) { |
||
203 | const int u = srcp[x + 0] - 128; |
||
204 | const int v = srcp[x + 2] - 128; |
||
205 | const int uvval = c2 * u + c3 * v + 1081344; |
||
206 | dstp[x + 0] = CB((c4 * u + c5 * v + 8421376) >> 16); |
||
207 | dstp[x + 1] = CB((65536 * (srcp[x + 1] - 16) + uvval) >> 16); |
||
208 | dstp[x + 2] = CB((c6 * u + c7 * v + 8421376) >> 16); |
||
209 | dstp[x + 3] = CB((65536 * (srcp[x + 3] - 16) + uvval) >> 16); |
||
210 | } |
||
211 | srcp += src_pitch; |
||
212 | dstp += dst_pitch; |
||
213 | } |
||
214 | } |
||
215 | |||
216 | static void process_frame_yuv422p(ColorMatrixContext *color, |
||
217 | AVFrame *dst, AVFrame *src) |
||
218 | { |
||
219 | const unsigned char *srcpU = src->data[1]; |
||
220 | const unsigned char *srcpV = src->data[2]; |
||
221 | const unsigned char *srcpY = src->data[0]; |
||
222 | const int src_pitchY = src->linesize[0]; |
||
223 | const int src_pitchUV = src->linesize[1]; |
||
224 | const int height = src->height; |
||
225 | const int width = src->width; |
||
226 | unsigned char *dstpU = dst->data[1]; |
||
227 | unsigned char *dstpV = dst->data[2]; |
||
228 | unsigned char *dstpY = dst->data[0]; |
||
229 | const int dst_pitchY = dst->linesize[0]; |
||
230 | const int dst_pitchUV = dst->linesize[1]; |
||
231 | const int c2 = color->yuv_convert[color->mode][0][1]; |
||
232 | const int c3 = color->yuv_convert[color->mode][0][2]; |
||
233 | const int c4 = color->yuv_convert[color->mode][1][1]; |
||
234 | const int c5 = color->yuv_convert[color->mode][1][2]; |
||
235 | const int c6 = color->yuv_convert[color->mode][2][1]; |
||
236 | const int c7 = color->yuv_convert[color->mode][2][2]; |
||
237 | int x, y; |
||
238 | |||
239 | for (y = 0; y < height; y++) { |
||
240 | for (x = 0; x < width; x += 2) { |
||
241 | const int u = srcpU[x >> 1] - 128; |
||
242 | const int v = srcpV[x >> 1] - 128; |
||
243 | const int uvval = c2 * u + c3 * v + 1081344; |
||
244 | dstpY[x + 0] = CB((65536 * (srcpY[x + 0] - 16) + uvval) >> 16); |
||
245 | dstpY[x + 1] = CB((65536 * (srcpY[x + 1] - 16) + uvval) >> 16); |
||
246 | dstpU[x >> 1] = CB((c4 * u + c5 * v + 8421376) >> 16); |
||
247 | dstpV[x >> 1] = CB((c6 * u + c7 * v + 8421376) >> 16); |
||
248 | } |
||
249 | srcpY += src_pitchY; |
||
250 | dstpY += dst_pitchY; |
||
251 | srcpU += src_pitchUV; |
||
252 | srcpV += src_pitchUV; |
||
253 | dstpU += dst_pitchUV; |
||
254 | dstpV += dst_pitchUV; |
||
255 | } |
||
256 | } |
||
257 | |||
258 | static void process_frame_yuv420p(ColorMatrixContext *color, |
||
259 | AVFrame *dst, AVFrame *src) |
||
260 | { |
||
261 | const unsigned char *srcpU = src->data[1]; |
||
262 | const unsigned char *srcpV = src->data[2]; |
||
263 | const unsigned char *srcpY = src->data[0]; |
||
264 | const unsigned char *srcpN = src->data[0] + src->linesize[0]; |
||
265 | const int src_pitchY = src->linesize[0]; |
||
266 | const int src_pitchUV = src->linesize[1]; |
||
267 | const int height = src->height; |
||
268 | const int width = src->width; |
||
269 | unsigned char *dstpU = dst->data[1]; |
||
270 | unsigned char *dstpV = dst->data[2]; |
||
271 | unsigned char *dstpY = dst->data[0]; |
||
272 | unsigned char *dstpN = dst->data[0] + dst->linesize[0]; |
||
273 | const int dst_pitchY = dst->linesize[0]; |
||
274 | const int dst_pitchUV = dst->linesize[1]; |
||
275 | const int c2 = color->yuv_convert[color->mode][0][1]; |
||
276 | const int c3 = color->yuv_convert[color->mode][0][2]; |
||
277 | const int c4 = color->yuv_convert[color->mode][1][1]; |
||
278 | const int c5 = color->yuv_convert[color->mode][1][2]; |
||
279 | const int c6 = color->yuv_convert[color->mode][2][1]; |
||
280 | const int c7 = color->yuv_convert[color->mode][2][2]; |
||
281 | int x, y; |
||
282 | |||
283 | for (y = 0; y < height; y += 2) { |
||
284 | for (x = 0; x < width; x += 2) { |
||
285 | const int u = srcpU[x >> 1] - 128; |
||
286 | const int v = srcpV[x >> 1] - 128; |
||
287 | const int uvval = c2 * u + c3 * v + 1081344; |
||
288 | dstpY[x + 0] = CB((65536 * (srcpY[x + 0] - 16) + uvval) >> 16); |
||
289 | dstpY[x + 1] = CB((65536 * (srcpY[x + 1] - 16) + uvval) >> 16); |
||
290 | dstpN[x + 0] = CB((65536 * (srcpN[x + 0] - 16) + uvval) >> 16); |
||
291 | dstpN[x + 1] = CB((65536 * (srcpN[x + 1] - 16) + uvval) >> 16); |
||
292 | dstpU[x >> 1] = CB((c4 * u + c5 * v + 8421376) >> 16); |
||
293 | dstpV[x >> 1] = CB((c6 * u + c7 * v + 8421376) >> 16); |
||
294 | } |
||
295 | srcpY += src_pitchY << 1; |
||
296 | dstpY += dst_pitchY << 1; |
||
297 | srcpN += src_pitchY << 1; |
||
298 | dstpN += dst_pitchY << 1; |
||
299 | srcpU += src_pitchUV; |
||
300 | srcpV += src_pitchUV; |
||
301 | dstpU += dst_pitchUV; |
||
302 | dstpV += dst_pitchUV; |
||
303 | } |
||
304 | } |
||
305 | |||
306 | static int config_input(AVFilterLink *inlink) |
||
307 | { |
||
308 | AVFilterContext *ctx = inlink->dst; |
||
309 | ColorMatrixContext *color = ctx->priv; |
||
310 | const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink->format); |
||
311 | |||
312 | color->hsub = pix_desc->log2_chroma_w; |
||
313 | color->vsub = pix_desc->log2_chroma_h; |
||
314 | |||
315 | av_log(ctx, AV_LOG_VERBOSE, "%s -> %s\n", |
||
316 | color_modes[color->source], color_modes[color->dest]); |
||
317 | |||
318 | return 0; |
||
319 | } |
||
320 | |||
321 | static int query_formats(AVFilterContext *ctx) |
||
322 | { |
||
323 | static const enum AVPixelFormat pix_fmts[] = { |
||
324 | AV_PIX_FMT_YUV422P, |
||
325 | AV_PIX_FMT_YUV420P, |
||
326 | AV_PIX_FMT_UYVY422, |
||
327 | AV_PIX_FMT_NONE |
||
328 | }; |
||
329 | |||
330 | ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); |
||
331 | |||
332 | return 0; |
||
333 | } |
||
334 | |||
335 | static int filter_frame(AVFilterLink *link, AVFrame *in) |
||
336 | { |
||
337 | AVFilterContext *ctx = link->dst; |
||
338 | ColorMatrixContext *color = ctx->priv; |
||
339 | AVFilterLink *outlink = ctx->outputs[0]; |
||
340 | AVFrame *out; |
||
341 | |||
342 | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); |
||
343 | if (!out) { |
||
344 | av_frame_free(&in); |
||
345 | return AVERROR(ENOMEM); |
||
346 | } |
||
347 | av_frame_copy_props(out, in); |
||
348 | |||
349 | if (in->format == AV_PIX_FMT_YUV422P) |
||
350 | process_frame_yuv422p(color, out, in); |
||
351 | else if (in->format == AV_PIX_FMT_YUV420P) |
||
352 | process_frame_yuv420p(color, out, in); |
||
353 | else |
||
354 | process_frame_uyvy422(color, out, in); |
||
355 | |||
356 | av_frame_free(&in); |
||
357 | return ff_filter_frame(outlink, out); |
||
358 | } |
||
359 | |||
360 | static const AVFilterPad colormatrix_inputs[] = { |
||
361 | { |
||
362 | .name = "default", |
||
363 | .type = AVMEDIA_TYPE_VIDEO, |
||
364 | .config_props = config_input, |
||
365 | .filter_frame = filter_frame, |
||
366 | }, |
||
367 | { NULL } |
||
368 | }; |
||
369 | |||
370 | static const AVFilterPad colormatrix_outputs[] = { |
||
371 | { |
||
372 | .name = "default", |
||
373 | .type = AVMEDIA_TYPE_VIDEO, |
||
374 | }, |
||
375 | { NULL } |
||
376 | }; |
||
377 | |||
378 | AVFilter avfilter_vf_colormatrix = { |
||
379 | .name = "colormatrix", |
||
380 | .description = NULL_IF_CONFIG_SMALL("Convert color matrix."), |
||
381 | .priv_size = sizeof(ColorMatrixContext), |
||
382 | .init = init, |
||
383 | .query_formats = query_formats, |
||
384 | .inputs = colormatrix_inputs, |
||
385 | .outputs = colormatrix_outputs, |
||
386 | .priv_class = &colormatrix_class, |
||
387 | .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, |
||
388 | };><>><>><>><>>>>>>>>>>>>>> |