FFmpeg
vf_colordetect.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2025 Niklas Haas
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg 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.
10  *
11  * FFmpeg 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.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FFmpeg; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 /**
22  * @file
23  * Video color space detector, tries to auto-detect YUV range and alpha mode.
24  */
25 
26 #include <stdbool.h>
27 #include <stdatomic.h>
28 
29 #include "config.h"
30 
31 #include "libavutil/mem.h"
32 #include "libavutil/opt.h"
33 #include "libavutil/pixdesc.h"
34 
35 #include "avfilter.h"
36 #include "filters.h"
37 #include "formats.h"
38 #include "video.h"
39 
40 #include "vf_colordetect.h"
41 
42 enum AlphaMode {
43  ALPHA_NONE = -1,
46  /* No way to positively identify premultiplied alpha */
47 };
48 
52 };
53 
54 typedef struct ColorDetectContext {
55  const AVClass *class;
57  unsigned mode;
58 
61  int depth;
62  int idx_a;
63  int mpeg_min;
64  int mpeg_max;
65 
66  atomic_int detected_range; // enum AVColorRange
67  atomic_int detected_alpha; // enum AlphaMode
69 
70 #define OFFSET(x) offsetof(ColorDetectContext, x)
71 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
72 
73 static const AVOption colordetect_options[] = {
74  { "mode", "Image properties to detect", OFFSET(mode), AV_OPT_TYPE_FLAGS, {.i64 = -1}, 0, UINT_MAX, FLAGS, .unit = "mode" },
75  { "color_range", "Detect (YUV) color range", 0, AV_OPT_TYPE_CONST, {.i64 = COLOR_DETECT_COLOR_RANGE}, 0, 0, FLAGS, .unit = "mode" },
76  { "alpha_mode", "Detect alpha mode", 0, AV_OPT_TYPE_CONST, {.i64 = COLOR_DETECT_ALPHA_MODE }, 0, 0, FLAGS, .unit = "mode" },
77  { "all", "Detect all supported properties", 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, FLAGS, .unit = "mode" },
78  { NULL }
79 };
80 
81 AVFILTER_DEFINE_CLASS(colordetect);
82 
83 static int query_format(const AVFilterContext *ctx,
84  AVFilterFormatsConfig **cfg_in,
85  AVFilterFormatsConfig **cfg_out)
86 {
87  int want_flags = AV_PIX_FMT_FLAG_PLANAR;
88  int reject_flags = AV_PIX_FMT_FLAG_PAL | AV_PIX_FMT_FLAG_HWACCEL |
91 
92  if (HAVE_BIGENDIAN) {
93  want_flags |= AV_PIX_FMT_FLAG_BE;
94  } else {
95  reject_flags |= AV_PIX_FMT_FLAG_BE;
96  }
97 
98  AVFilterFormats *formats = ff_formats_pixdesc_filter(want_flags, reject_flags);
99  return ff_set_common_formats2(ctx, cfg_in, cfg_out, formats);
100 }
101 
103 {
104  AVFilterContext *ctx = inlink->dst;
105  ColorDetectContext *s = ctx->priv;
107  const int depth = desc->comp[0].depth;
108  const int mpeg_min = 16 << (depth - 8);
109  const int mpeg_max = 235 << (depth - 8);
110  if (depth > 16) /* not currently possible; prevent future bugs */
111  return AVERROR(ENOTSUP);
112 
113  s->desc = desc;
114  s->depth = depth;
115  s->mpeg_min = mpeg_min;
116  s->mpeg_max = mpeg_max;
117  s->nb_threads = ff_filter_get_nb_threads(ctx);
118 
119  if (desc->flags & AV_PIX_FMT_FLAG_RGB) {
120  atomic_init(&s->detected_range, AVCOL_RANGE_JPEG);
121  } else {
122  atomic_init(&s->detected_range, AVCOL_RANGE_UNSPECIFIED);
123  }
124 
125  if (desc->flags & AV_PIX_FMT_FLAG_ALPHA) {
126  s->idx_a = desc->comp[desc->nb_components - 1].plane;
127  atomic_init(&s->detected_alpha, ALPHA_UNDETERMINED);
128  } else {
129  atomic_init(&s->detected_alpha, ALPHA_NONE);
130  }
131 
132  ff_color_detect_dsp_init(&s->dsp, depth, inlink->color_range);
133  return 0;
134 }
135 
136 static int detect_range(AVFilterContext *ctx, void *arg,
137  int jobnr, int nb_jobs)
138 {
139  ColorDetectContext *s = ctx->priv;
140  const AVFrame *in = arg;
141  const ptrdiff_t stride = in->linesize[0];
142  const int y_start = (in->height * jobnr) / nb_jobs;
143  const int y_end = (in->height * (jobnr + 1)) / nb_jobs;
144  const int h_slice = y_end - y_start;
145 
146  if (s->dsp.detect_range(in->data[0] + y_start * stride, stride,
147  in->width, h_slice, s->mpeg_min, s->mpeg_max))
148  atomic_store(&s->detected_range, AVCOL_RANGE_JPEG);
149 
150  return 0;
151 }
152 
153 static int detect_alpha(AVFilterContext *ctx, void *arg,
154  int jobnr, int nb_jobs)
155 {
156  ColorDetectContext *s = ctx->priv;
157  const AVFrame *in = arg;
158  const int w = in->width;
159  const int h = in->height;
160  const int y_start = (h * jobnr) / nb_jobs;
161  const int y_end = (h * (jobnr + 1)) / nb_jobs;
162  const int h_slice = y_end - y_start;
163 
164  const int nb_planes = (s->desc->flags & AV_PIX_FMT_FLAG_RGB) ? 3 : 1;
165  const ptrdiff_t alpha_stride = in->linesize[s->idx_a];
166  const uint8_t *alpha = in->data[s->idx_a] + y_start * alpha_stride;
167 
168  /**
169  * To check if a value is out of range, we need to compare the color value
170  * against the maximum possible color for a given alpha value.
171  * x > ((mpeg_max - mpeg_min) / pixel_max) * a + mpeg_min
172  *
173  * This simplifies to:
174  * (x - mpeg_min) * pixel_max > (mpeg_max - mpeg_min) * a
175  * = P * x - K > Q * a in the below formula.
176  *
177  * We subtract an additional offset of (1 << (depth - 1)) to account for
178  * rounding errors in the value of `x`, and an extra safety margin of
179  * Q because vf_premultiply.c et al. add an offset of (a >> 1) & 1.
180  */
181  const int p = (1 << s->depth) - 1;
182  const int q = s->mpeg_max - s->mpeg_min;
183  const int k = p * s->mpeg_min + q + (1 << (s->depth - 1));
184 
185  for (int i = 0; i < nb_planes; i++) {
186  const ptrdiff_t stride = in->linesize[i];
187  if (s->dsp.detect_alpha(in->data[i] + y_start * stride, stride,
188  alpha, alpha_stride, w, h_slice, p, q, k)) {
189  atomic_store(&s->detected_alpha, ALPHA_STRAIGHT);
190  return 0;
191  }
192  }
193 
194  return 0;
195 }
196 
198 {
199  AVFilterContext *ctx = inlink->dst;
200  ColorDetectContext *s = ctx->priv;
201  const int nb_threads = FFMIN(inlink->h, s->nb_threads);
202 
203  if (s->mode & COLOR_DETECT_COLOR_RANGE && s->detected_range == AVCOL_RANGE_UNSPECIFIED)
204  ff_filter_execute(ctx, detect_range, in, NULL, nb_threads);
205  if (s->mode & COLOR_DETECT_ALPHA_MODE && s->detected_alpha == ALPHA_UNDETERMINED)
206  ff_filter_execute(ctx, detect_alpha, in, NULL, nb_threads);
207 
208  return ff_filter_frame(inlink->dst->outputs[0], in);
209 }
210 
212 {
213  ColorDetectContext *s = ctx->priv;
214  if (!s->mode)
215  return;
216 
217  av_log(ctx, AV_LOG_INFO, "Detected color properties:\n");
218  if (s->mode & COLOR_DETECT_COLOR_RANGE) {
219  av_log(ctx, AV_LOG_INFO, " Color range: %s\n",
220  s->detected_range == AVCOL_RANGE_JPEG ? "JPEG / full range"
221  : "undetermined");
222  }
223 
224  if (s->mode & COLOR_DETECT_ALPHA_MODE) {
225  av_log(ctx, AV_LOG_INFO, " Alpha mode: %s\n",
226  s->detected_alpha == ALPHA_NONE ? "none" :
227  s->detected_alpha == ALPHA_STRAIGHT ? "straight / independent"
228  : "undetermined");
229  }
230 }
231 
234 {
235 #if ARCH_X86
237 #endif
238 
239  if (!dsp->detect_range)
241  if (!dsp->detect_alpha) {
242  if (color_range == AVCOL_RANGE_JPEG) {
244  } else {
246  }
247  }
248 }
249 
250 static const AVFilterPad colordetect_inputs[] = {
251  {
252  .name = "default",
253  .type = AVMEDIA_TYPE_VIDEO,
254  .config_props = config_input,
255  .filter_frame = filter_frame,
256  },
257 };
258 
260  .p.name = "colordetect",
261  .p.description = NULL_IF_CONFIG_SMALL("Detect video color properties."),
262  .p.priv_class = &colordetect_class,
264  .priv_size = sizeof(ColorDetectContext),
268  .uninit = uninit,
269 };
formats
formats
Definition: signature.h:47
ColorDetectContext::mode
unsigned mode
Definition: vf_colordetect.c:57
atomic_store
#define atomic_store(object, desired)
Definition: stdatomic.h:85
ColorDetectContext::desc
const AVPixFmtDescriptor * desc
Definition: vf_colordetect.c:59
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
opt.h
vf_colordetect.h
ff_filter_frame
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition: avfilter.c:1062
uninit
static av_cold void uninit(AVFilterContext *ctx)
Definition: vf_colordetect.c:211
av_pix_fmt_desc_get
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition: pixdesc.c:3441
ff_set_common_formats2
int ff_set_common_formats2(const AVFilterContext *ctx, AVFilterFormatsConfig **cfg_in, AVFilterFormatsConfig **cfg_out, AVFilterFormats *formats)
Definition: formats.c:1007
inlink
The exact code depends on how similar the blocks are and how related they are to the and needs to apply these operations to the correct inlink or outlink if there are several Macros are available to factor that when no extra processing is inlink
Definition: filter_design.txt:212
ColorDetectContext::idx_a
int idx_a
Definition: vf_colordetect.c:62
AV_PIX_FMT_FLAG_FLOAT
#define AV_PIX_FMT_FLAG_FLOAT
The pixel format contains IEEE-754 floating point values.
Definition: pixdesc.h:158
detect_range
static int detect_range(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_colordetect.c:136
FILTER_INPUTS
#define FILTER_INPUTS(array)
Definition: filters.h:263
mode
Definition: swscale.c:56
AVFrame
This structure describes decoded (raw) audio or video data.
Definition: frame.h:421
pixdesc.h
AVFrame::width
int width
Definition: frame.h:493
w
uint8_t w
Definition: llviddspenc.c:38
AVCOL_RANGE_JPEG
@ AVCOL_RANGE_JPEG
Full range content.
Definition: pixfmt.h:767
AVOption
AVOption.
Definition: opt.h:429
atomic_int
intptr_t atomic_int
Definition: stdatomic.h:55
AVFilter::name
const char * name
Filter name.
Definition: avfilter.h:215
video.h
ff_vf_colordetect
const FFFilter ff_vf_colordetect
Definition: vf_colordetect.c:259
AVFrame::data
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition: frame.h:442
AVFilterFormats
A list of supported formats for one end of a filter link.
Definition: formats.h:64
formats.h
config_input
static int config_input(AVFilterLink *inlink)
Definition: vf_colordetect.c:102
AV_PIX_FMT_FLAG_HWACCEL
#define AV_PIX_FMT_FLAG_HWACCEL
Pixel format is an HW accelerated format.
Definition: pixdesc.h:128
AVFILTER_DEFINE_CLASS
AVFILTER_DEFINE_CLASS(colordetect)
AVFilterPad
A filter pad used for either input or output.
Definition: filters.h:39
colordetect_inputs
static const AVFilterPad colordetect_inputs[]
Definition: vf_colordetect.c:250
av_cold
#define av_cold
Definition: attributes.h:90
ff_video_default_filterpad
const AVFilterPad ff_video_default_filterpad[1]
An AVFilterPad array whose only entry has name "default" and is of type AVMEDIA_TYPE_VIDEO.
Definition: video.c:37
FFFilter
Definition: filters.h:266
s
#define s(width, name)
Definition: cbs_vp9.c:198
filters.h
COLOR_DETECT_COLOR_RANGE
@ COLOR_DETECT_COLOR_RANGE
Definition: vf_colordetect.c:50
AV_PIX_FMT_FLAG_ALPHA
#define AV_PIX_FMT_FLAG_ALPHA
The pixel format has an alpha channel.
Definition: pixdesc.h:147
ctx
AVFormatContext * ctx
Definition: movenc.c:49
ff_color_detect_dsp_init
av_cold void ff_color_detect_dsp_init(FFColorDetectDSPContext *dsp, int depth, enum AVColorRange color_range)
Definition: vf_colordetect.c:232
color_range
color_range
Definition: vf_selectivecolor.c:43
FILTER_OUTPUTS
#define FILTER_OUTPUTS(array)
Definition: filters.h:264
arg
const char * arg
Definition: jacosubdec.c:67
AVClass
Describe the class of an AVClass context structure.
Definition: log.h:76
NULL
#define NULL
Definition: coverity.c:32
ColorDetectContext::detected_alpha
atomic_int detected_alpha
Definition: vf_colordetect.c:67
ColorDetectMode
ColorDetectMode
Definition: vf_colordetect.c:49
AlphaMode
AlphaMode
Definition: vf_colordetect.c:42
detect_alpha
static int detect_alpha(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
Definition: vf_colordetect.c:153
ColorDetectContext
Definition: vf_colordetect.c:54
ff_color_detect_dsp_init_x86
void ff_color_detect_dsp_init_x86(FFColorDetectDSPContext *dsp, int depth, enum AVColorRange color_range)
Definition: vf_colordetect_init.c:79
AVCOL_RANGE_UNSPECIFIED
@ AVCOL_RANGE_UNSPECIFIED
Definition: pixfmt.h:733
AVFilterFormatsConfig
Lists of formats / etc.
Definition: avfilter.h:121
ff_detect_range16_c
static int ff_detect_range16_c(const uint8_t *data, ptrdiff_t stride, ptrdiff_t width, ptrdiff_t height, int mpeg_min, int mpeg_max)
Definition: vf_colordetect.h:63
ff_detect_alpha_limited_c
static int ff_detect_alpha_limited_c(const uint8_t *color, ptrdiff_t color_stride, const uint8_t *alpha, ptrdiff_t alpha_stride, ptrdiff_t width, ptrdiff_t height, int p, int q, int k)
Definition: vf_colordetect.h:98
OFFSET
#define OFFSET(x)
Definition: vf_colordetect.c:70
ColorDetectContext::nb_threads
int nb_threads
Definition: vf_colordetect.c:60
ff_detect_alpha_full_c
static int ff_detect_alpha_full_c(const uint8_t *color, ptrdiff_t color_stride, const uint8_t *alpha, ptrdiff_t alpha_stride, ptrdiff_t width, ptrdiff_t height, int p, int q, int k)
Definition: vf_colordetect.h:81
NULL_IF_CONFIG_SMALL
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:94
AV_PIX_FMT_FLAG_RGB
#define AV_PIX_FMT_FLAG_RGB
The pixel format contains RGB-like data (as opposed to YUV/grayscale).
Definition: pixdesc.h:136
AV_PIX_FMT_FLAG_BITSTREAM
#define AV_PIX_FMT_FLAG_BITSTREAM
All values of a component are bit-wise packed end to end.
Definition: pixdesc.h:124
ff_detect_alpha16_full_c
static int ff_detect_alpha16_full_c(const uint8_t *color, ptrdiff_t color_stride, const uint8_t *alpha, ptrdiff_t alpha_stride, ptrdiff_t width, ptrdiff_t height, int p, int q, int k)
Definition: vf_colordetect.h:115
query_format
static int query_format(const AVFilterContext *ctx, AVFilterFormatsConfig **cfg_in, AVFilterFormatsConfig **cfg_out)
Definition: vf_colordetect.c:83
AV_LOG_INFO
#define AV_LOG_INFO
Standard information.
Definition: log.h:221
AV_PIX_FMT_FLAG_BAYER
#define AV_PIX_FMT_FLAG_BAYER
The pixel format is following a Bayer pattern.
Definition: pixdesc.h:152
FFColorDetectDSPContext::detect_range
int(* detect_range)(const uint8_t *data, ptrdiff_t stride, ptrdiff_t width, ptrdiff_t height, int mpeg_min, int mpeg_max)
Definition: vf_colordetect.h:30
ff_detect_alpha16_limited_c
static int ff_detect_alpha16_limited_c(const uint8_t *color, ptrdiff_t color_stride, const uint8_t *alpha, ptrdiff_t alpha_stride, ptrdiff_t width, ptrdiff_t height, int p, int q, int k)
Definition: vf_colordetect.h:134
ff_formats_pixdesc_filter
AVFilterFormats * ff_formats_pixdesc_filter(unsigned want, unsigned rej)
Construct a formats list containing all pixel formats with certain properties.
Definition: formats.c:553
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:256
ColorDetectContext::depth
int depth
Definition: vf_colordetect.c:61
ff_filter_get_nb_threads
int ff_filter_get_nb_threads(AVFilterContext *ctx)
Get number of threads for current filter instance.
Definition: avfilter.c:840
FILTER_QUERY_FUNC2
#define FILTER_QUERY_FUNC2(func)
Definition: filters.h:240
colordetect_options
static const AVOption colordetect_options[]
Definition: vf_colordetect.c:73
FFMIN
#define FFMIN(a, b)
Definition: macros.h:49
AV_PIX_FMT_FLAG_BE
#define AV_PIX_FMT_FLAG_BE
Pixel format is big-endian.
Definition: pixdesc.h:116
AVFilterPad::name
const char * name
Pad name.
Definition: filters.h:45
stride
#define stride
Definition: h264pred_template.c:536
ColorDetectContext::dsp
FFColorDetectDSPContext dsp
Definition: vf_colordetect.c:56
ALPHA_STRAIGHT
@ ALPHA_STRAIGHT
Definition: vf_colordetect.c:45
AVFrame::height
int height
Definition: frame.h:493
filter_frame
static int filter_frame(AVFilterLink *inlink, AVFrame *in)
Definition: vf_colordetect.c:197
AV_PIX_FMT_FLAG_XYZ
#define AV_PIX_FMT_FLAG_XYZ
The pixel format contains XYZ-like data (as opposed to YUV/RGB/grayscale).
Definition: pixdesc.h:163
ff_filter_execute
int ff_filter_execute(AVFilterContext *ctx, avfilter_action_func *func, void *arg, int *ret, int nb_jobs)
Definition: avfilter.c:1686
avfilter.h
AVFILTER_FLAG_METADATA_ONLY
#define AVFILTER_FLAG_METADATA_ONLY
The filter is a "metadata" filter - it does not modify the frame data in any way.
Definition: avfilter.h:178
AV_PIX_FMT_FLAG_PLANAR
#define AV_PIX_FMT_FLAG_PLANAR
At least one pixel component is not in the first data plane.
Definition: pixdesc.h:132
ALPHA_UNDETERMINED
@ ALPHA_UNDETERMINED
Definition: vf_colordetect.c:44
ColorDetectContext::mpeg_max
int mpeg_max
Definition: vf_colordetect.c:64
AVFilterContext
An instance of a filter.
Definition: avfilter.h:269
AVFILTER_FLAG_SLICE_THREADS
#define AVFILTER_FLAG_SLICE_THREADS
The filter supports multithreading by splitting frames into multiple parts and processing them concur...
Definition: avfilter.h:162
desc
const char * desc
Definition: libsvtav1.c:79
AVMEDIA_TYPE_VIDEO
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:200
FFFilter::p
AVFilter p
The public AVFilter.
Definition: filters.h:270
mem.h
AVPixFmtDescriptor
Descriptor that unambiguously describes how the bits of a pixel are stored in the up to 4 data planes...
Definition: pixdesc.h:69
COLOR_DETECT_ALPHA_MODE
@ COLOR_DETECT_ALPHA_MODE
Definition: vf_colordetect.c:51
ColorDetectContext::detected_range
atomic_int detected_range
Definition: vf_colordetect.c:66
alpha
static const int16_t alpha[]
Definition: ilbcdata.h:55
FFColorDetectDSPContext
Definition: vf_colordetect.h:28
ALPHA_NONE
@ ALPHA_NONE
Definition: vf_colordetect.c:43
AV_OPT_TYPE_FLAGS
@ AV_OPT_TYPE_FLAGS
Underlying C type is unsigned int.
Definition: opt.h:255
AVFrame::linesize
int linesize[AV_NUM_DATA_POINTERS]
For video, a positive or negative value, which is typically indicating the size in bytes of each pict...
Definition: frame.h:466
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
FFColorDetectDSPContext::detect_alpha
int(* detect_alpha)(const uint8_t *color, ptrdiff_t color_stride, const uint8_t *alpha, ptrdiff_t alpha_stride, ptrdiff_t width, ptrdiff_t height, int p, int q, int k)
Definition: vf_colordetect.h:35
h
h
Definition: vp9dsp_template.c:2070
atomic_init
#define atomic_init(obj, value)
Definition: stdatomic.h:33
AVColorRange
AVColorRange
Visual content value range.
Definition: pixfmt.h:732
AV_PIX_FMT_FLAG_PAL
#define AV_PIX_FMT_FLAG_PAL
Pixel format has a palette in data[1], values are indexes in this palette.
Definition: pixdesc.h:120
AV_OPT_TYPE_CONST
@ AV_OPT_TYPE_CONST
Special option type for declaring named constants.
Definition: opt.h:299
ColorDetectContext::mpeg_min
int mpeg_min
Definition: vf_colordetect.c:63
ff_detect_range_c
static int ff_detect_range_c(const uint8_t *data, ptrdiff_t stride, ptrdiff_t width, ptrdiff_t height, int mpeg_min, int mpeg_max)
Definition: vf_colordetect.h:47
FLAGS
#define FLAGS
Definition: vf_colordetect.c:71