remove unused modes/code and formalise finalise_* methods
[sparrow.git] / floodfill.c
blob97c974c3f9ea2cb83ad8f71f8461a3be24b43a1e
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>
26 typedef struct sparrow_find_screen_s {
27 IplImage *green;
28 IplImage *working;
29 IplImage *mask;
30 IplImage *im;
31 } sparrow_find_screen_t;
34 /* Floodfill for find screen */
35 static inline void
36 expand_one_mono(int x, int y, int c,
37 CvPoint *nexts, int *n_nexts, guint8 *im, guint8 *mask, int w, int h){
38 guint8 p = im[y * w + x];
39 guint8 *m = &mask[y * w + x];
40 if (*m && (p == c)){
41 *m = 0;
42 nexts[*n_nexts].x = x;
43 nexts[*n_nexts].y = y;
44 (*n_nexts)++;
49 im: the image to be analysed
50 mim: the mask image to be written
51 start: a point of the right colour.
54 static IplImage*
55 floodfill_mono_superfast(IplImage *im, IplImage *mim, CvPoint start)
57 guint8 * data = (guint8 *)im->imageData;
58 guint8 * mdata = (guint8 *)mim->imageData;
59 int w = im->width;
60 int h = im->height;
61 int n_starts;
62 int n_nexts = 0;
63 CvPoint *starts;
64 CvPoint *nexts;
66 //malloc 2 lists of points. These *could* be as large as the image (but never should be)
67 void * mem = malloc_or_die(w * h * 2 * sizeof(CvPoint));
68 starts = mem;
69 nexts = starts + w * h;
70 n_starts = 1;
71 starts[0] = start;
73 while(n_starts){
74 n_nexts = 0;
75 int i;
76 for (i = 0; i < n_starts; i++){
77 int x = starts[i].x;
78 int y = starts[i].y;
79 int c = data[y * w + x];
80 if (x > 0){
81 expand_one_mono(x - 1, y, c, nexts, &n_nexts, data, mdata, w, h);
83 if (x < w - 1){
84 expand_one_mono(x + 1, y, c, nexts, &n_nexts, data, mdata, w, h);
86 if (y > 0){
87 expand_one_mono(x, y - 1, c, nexts, &n_nexts, data, mdata, w, h);
89 if (y < h - 1){
90 expand_one_mono(x, y + 1, c, nexts, &n_nexts, data, mdata, w, h);
93 CvPoint *tmp = starts;
94 starts = nexts;
95 nexts = tmp;
96 n_starts = n_nexts;
98 free(mem);
99 return im;
107 /* find a suitable threshold level by looking at the histogram of a monochrome
108 image */
109 static int
110 find_edges_threshold(IplImage *im)
112 int w = im->width;
113 int h = im->height;
114 CvSize small_size = {w / 8, h / 8};
115 IplImage *small = cvCreateImage(small_size, IPL_DEPTH_8U, 1); /*for quicker histogram (stupid, perhaps?)*/
116 cvResize(im, small, CV_INTER_NN);
117 int hist_size[] = {255};
118 float range[] = {0, 255};
119 float *ranges[] = {range};
120 CvHistogram* hist = cvCreateHist(1, hist_size, CV_HIST_ARRAY, ranges, 1);
121 cvCalcHist(&small, hist, 0, NULL);
123 int pixels = small->width * small->height;
124 int min_black = pixels / 16;
125 int max_black = pixels / 2;
126 int totals[256] = {0};
128 int best_d = pixels + 1;
129 int best_t = 0;
131 /* look for a low region in the histogram between the two peaks.
132 (big assumption: two peaks, with most in whiter peak) */
133 int total = 0;
134 for (int i = 0; i < 255; i++){
135 int v = (int)cvQueryHistValue_1D(hist, i);
136 total += v;
137 totals[i] = total;
138 if (total >= min_black){
139 if (i >= 5){
140 int diff = total - totals[i - 5];
141 if (diff < best_d){
142 best_d = diff;
143 best_t = i - 2;
145 if (total >= max_black){
146 break;
151 GST_DEBUG("found best threshold %d -- %d pixel change at %d/%d pixels\n",
152 best_t, best_d, totals[best_t], pixels);
154 cvReleaseImage(&small);
155 cvReleaseHist(&hist);
157 return best_t;
161 /* a minature state progression within this one, in case the processing is too
162 much for one frame.*/
163 INVISIBLE sparrow_state
164 mode_find_screen(GstSparrow *sparrow, guint8 *in, guint8 *out){
165 sparrow->countdown--;
166 GST_DEBUG("in find_screen with countdown %d\n", sparrow->countdown);
167 sparrow_find_screen_t *finder = (sparrow_find_screen_t *)sparrow->helper_struct;
168 IplImage *im = finder->im;
169 IplImage *green = finder->green;
170 IplImage *working = finder->working;
171 IplImage *mask = finder->mask;
172 /* size is 1 byte per pixel, not 4! */
173 size_t size = sparrow->in.pixcount;
174 CvPoint middle, corner;
175 switch (sparrow->countdown){
176 case 2:
177 /* time to look and see if the screen is there.
178 Look at the histogram of a single channel. */
179 im->imageData = (char*)in;
180 guint32 gshift = sparrow->in.gshift;
181 cvSplit(im,
182 (gshift == 24) ? green : NULL,
183 (gshift == 16) ? green : NULL,
184 (gshift == 8) ? green : NULL,
185 (gshift == 0) ? green : NULL);
186 int best_t = find_edges_threshold(green);
187 /*XXX if best_t is wrong, add to sparrow->countdown: probably the light is
188 not really on. But what counts as wrong? */
189 cvCmpS(green, best_t, mask, CV_CMP_GT);
190 goto black;
191 case 1:
192 /* floodfill where the screen is, removing outlying bright spots*/
193 middle = (CvPoint){sparrow->in.width / 2, sparrow->in.height / 2};
194 memset(working->imageData, 255, size);
195 floodfill_mono_superfast(mask, working, middle);
196 goto black;
197 case 0:
198 /* floodfill the border, removing onscreen dirt.*/
199 corner = (CvPoint){0, 0};
200 memset(mask->imageData, 255, size);
201 floodfill_mono_superfast(working, mask, corner);
202 sparrow->screenmask = (guint8*)mask->imageData;
203 cvReleaseImage(&(finder->green));
204 cvReleaseImage(&(finder->working));
205 goto finish;
206 default:
207 /*send white and wait for the picture to arrive back. */
208 memset(out, 255, sparrow->out.size);
209 return SPARROW_STATUS_QUO;
211 black:
212 memset(out, 0, sparrow->out.size);
213 return SPARROW_STATUS_QUO;
214 finish:
215 memset(out, 0, sparrow->out.size);
216 finalise_find_screen(sparrow);
217 return SPARROW_NEXT_STATE;
220 INVISIBLE void
221 finalise_find_screen(GstSparrow *sparrow){
222 sparrow_find_screen_t *finder = (sparrow_find_screen_t *)sparrow->helper_struct;
223 cvReleaseImage(&finder->green);
224 cvReleaseImage(&finder->working);
225 cvReleaseImageHeader(&finder->mask);
226 cvReleaseImageHeader(&finder->im);
227 free(finder);
230 INVISIBLE void
231 init_find_screen(GstSparrow *sparrow){
232 sparrow_find_screen_t *finder = zalloc_aligned_or_die(sizeof(sparrow_find_screen_t));
233 sparrow->helper_struct = (void *)finder;
234 sparrow->countdown = sparrow->lag + 4;
235 CvSize size = {sparrow->in.width, sparrow->in.height};
236 finder->green = cvCreateImage(size, IPL_DEPTH_8U, 1);
237 finder->working = cvCreateImage(size, IPL_DEPTH_8U, 1);
239 finder->im = cvCreateImageHeader(size, IPL_DEPTH_8U, PIXSIZE);
240 cvInitImageHeader(finder->im, size, IPL_DEPTH_8U, PIXSIZE, 0, 8);
242 finder->mask = cvCreateImageHeader(size, IPL_DEPTH_8U, 1);
243 cvInitImageHeader(finder->mask, size, IPL_DEPTH_8U, 1, 0, 8);
244 finder->mask->imageData = (char *)sparrow->screenmask;