a tool to compare timings between LUT methods
[sparrow.git] / floodfill.c
blob005d44b8775c7f0d3fdf25c45d57e6217d909964
1 /* Copyright (C) <2010> Douglas Bagnall <douglas@halo.gen.nz>
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Library General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) any later version.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Library General Public License for more details.
13 * You should have received a copy of the GNU Library General Public
14 * License along with this library; if not, write to the
15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 * Boston, MA 02111-1307, USA.
19 #include "sparrow.h"
20 #include "gstsparrow.h"
22 #include <string.h>
23 #include <math.h>
25 #define STUPID_DEBUG_TRICK 1
27 typedef struct sparrow_find_screen_s {
28 IplImage *green;
29 IplImage *working;
30 IplImage *mask;
31 IplImage *im;
32 } sparrow_find_screen_t;
35 /* Floodfill for find screen */
36 static inline void
37 expand_one_mono(int x, int y, int c,
38 CvPoint *nexts, int *n_nexts, guint8 *im, guint8 *mask, int w, int h){
39 guint8 p = im[y * w + x];
40 guint8 *m = &mask[y * w + x];
41 if (*m && (p == c)){
42 *m = 0;
43 nexts[*n_nexts].x = x;
44 nexts[*n_nexts].y = y;
45 (*n_nexts)++;
50 im: the image to be analysed
51 mim: the mask image to be written
52 start: a point of the right colour.
55 static IplImage*
56 floodfill_mono_superfast(IplImage *im, IplImage *mim, CvPoint start)
58 guint8 * data = (guint8 *)im->imageData;
59 guint8 * mdata = (guint8 *)mim->imageData;
60 int w = im->width;
61 int h = im->height;
62 int n_starts;
63 int n_nexts = 0;
64 CvPoint *starts;
65 CvPoint *nexts;
67 //malloc 2 lists of points. These *could* be as large as the image (but never should be)
68 void * mem = malloc_or_die(w * h * 2 * sizeof(CvPoint));
69 starts = mem;
70 nexts = starts + w * h;
71 n_starts = 1;
72 starts[0] = start;
74 while(n_starts){
75 n_nexts = 0;
76 int i;
77 for (i = 0; i < n_starts; i++){
78 int x = starts[i].x;
79 int y = starts[i].y;
80 int c = data[y * w + x];
81 if (x > 0){
82 expand_one_mono(x - 1, y, c, nexts, &n_nexts, data, mdata, w, h);
84 if (x < w - 1){
85 expand_one_mono(x + 1, y, c, nexts, &n_nexts, data, mdata, w, h);
87 if (y > 0){
88 expand_one_mono(x, y - 1, c, nexts, &n_nexts, data, mdata, w, h);
90 if (y < h - 1){
91 expand_one_mono(x, y + 1, c, nexts, &n_nexts, data, mdata, w, h);
94 CvPoint *tmp = starts;
95 starts = nexts;
96 nexts = tmp;
97 n_starts = n_nexts;
99 free(mem);
100 return im;
108 /* find a suitable threshold level by looking at the histogram of a monochrome
109 image */
110 static int
111 find_edges_threshold(IplImage *im)
113 int w = im->width;
114 int h = im->height;
115 CvSize small_size = {w / 4, h / 4};
116 IplImage *small = cvCreateImage(small_size, IPL_DEPTH_8U, 1); /*for quicker histogram (stupid, perhaps?)*/
117 cvResize(im, small, CV_INTER_NN);
118 int hist_size[] = {255};
119 float range[] = {0, 255};
120 float *ranges[] = {range};
121 CvHistogram* hist = cvCreateHist(1, hist_size, CV_HIST_ARRAY, ranges, 1);
122 cvCalcHist(&small, hist, 0, NULL);
124 int pixels = small->width * small->height;
125 int min_black = pixels / 16;
126 int max_black = pixels * 3 / 4;
127 int totals[256] = {0};
129 int best_d = pixels + 1;
130 int best_t = 0;
132 /* look for a low region in the histogram between the two peaks.
133 (big assumption: two peaks, with most in whiter peak) */
134 int total = 0;
135 for (int i = 0; i < 255; i++){
136 int v = (int)cvQueryHistValue_1D(hist, i);
137 total += v;
138 totals[i] = total;
139 if (total >= min_black){
140 if (i >= 5){
141 int diff = total - totals[i - 5];
142 if (diff < best_d){
143 best_d = diff;
144 best_t = i - 2;
146 if (total >= max_black){
147 break;
152 GST_DEBUG("found best threshold %d -- %d pixel change at %d/%d pixels\n",
153 best_t, best_d, totals[best_t], pixels);
154 //MAYBE_DEBUG_IPL(small);
155 cvReleaseImage(&small);
156 cvReleaseHist(&hist);
158 return best_t;
162 /* a minature state progression within this one, in case the processing is too
163 much for one frame.*/
164 INVISIBLE sparrow_state
165 mode_find_screen(GstSparrow *sparrow, guint8 *in, guint8 *out){
166 sparrow->countdown--;
167 GST_DEBUG("in find_screen with countdown %d\n", sparrow->countdown);
168 sparrow_find_screen_t *finder = (sparrow_find_screen_t *)sparrow->helper_struct;
169 IplImage *im = finder->im;
170 IplImage *green = finder->green;
171 IplImage *working = finder->working;
172 IplImage *mask = finder->mask;
173 /* size is 1 byte per pixel, not 4! */
174 size_t size = sparrow->in.pixcount;
175 CvPoint middle, corner;
176 switch (sparrow->countdown){
177 case 2:
178 /* time to look and see if the screen is there.
179 Look at the histogram of a single channel. */
180 im->imageData = (char*)in;
181 guint32 gshift = sparrow->in.gshift;
182 cvSplit(im,
183 (gshift == 24) ? green : NULL,
184 (gshift == 16) ? green : NULL,
185 (gshift == 8) ? green : NULL,
186 (gshift == 0) ? green : NULL);
187 //int best_t = find_edges_threshold(green);
188 /*XXX if best_t is wrong, add to sparrow->countdown: probably the light is
189 not really on. But what counts as wrong? */
191 //cvCmpS(green, best_t, mask, CV_CMP_GT);
192 cvCanny(green, mask, 100, 170, 3);
193 MAYBE_DEBUG_IPL(mask);
194 goto black;
195 case 1:
196 /* floodfill where the screen is, removing outlying bright spots*/
197 middle = (CvPoint){sparrow->in.width / 2, sparrow->in.height / 2};
198 memset(working->imageData, 255, size);
199 floodfill_mono_superfast(mask, working, middle);
200 MAYBE_DEBUG_IPL(working);
201 goto black;
202 case 0:
203 /* floodfill the border, removing onscreen dirt.*/
204 corner = (CvPoint){0, 0};
205 memset(mask->imageData, 255, size);
206 floodfill_mono_superfast(working, mask, corner);
207 #if STUPID_DEBUG_TRICK
208 cvErode(mask, mask, NULL, 9);
209 #endif
210 MAYBE_DEBUG_IPL(mask);
211 goto finish;
212 default:
213 /*send white and wait for the picture to arrive back. */
214 memset(out, 255, sparrow->out.size);
215 return SPARROW_STATUS_QUO;
217 black:
218 memset(out, 0, sparrow->out.size);
219 return SPARROW_STATUS_QUO;
220 finish:
221 memset(out, 0, sparrow->out.size);
222 return SPARROW_NEXT_STATE;
225 INVISIBLE void
226 finalise_find_screen(GstSparrow *sparrow){
227 sparrow_find_screen_t *finder = (sparrow_find_screen_t *)sparrow->helper_struct;
228 GST_DEBUG("finalise_find_screen: green %p, working %p, mask %p, im %p finder %p\n",
229 finder->green, finder->working, finder->mask, finder->im, finder);
230 cvReleaseImage(&finder->green);
231 cvReleaseImage(&finder->working);
232 cvReleaseImageHeader(&finder->mask);
233 cvReleaseImageHeader(&finder->im);
234 free(finder);
237 INVISIBLE void
238 init_find_screen(GstSparrow *sparrow){
239 sparrow_find_screen_t *finder = zalloc_aligned_or_die(sizeof(sparrow_find_screen_t));
240 sparrow->helper_struct = (void *)finder;
241 sparrow->countdown = sparrow->lag + 5;
242 CvSize size = {sparrow->in.width, sparrow->in.height};
243 finder->green = cvCreateImage(size, IPL_DEPTH_8U, 1);
244 finder->working = cvCreateImage(size, IPL_DEPTH_8U, 1);
246 finder->im = init_ipl_image(&sparrow->in, PIXSIZE);
247 finder->mask = init_ipl_image(&sparrow->in, 1);
249 finder->mask->imageData = (char *)sparrow->screenmask;
250 GST_DEBUG("init_find_screen: green %p, working %p, mask %p, im %p finder %p\n",
251 finder->green, finder->working, finder->mask, finder->im, finder);