Merge evaluate() functionnality into find_edges(), get delta for missed edges.
[goir.git] / ImageInfo.cc
blob843e56f294899fbf60631f09adf2a3b1fc7bd081
1 #include "sImLib/img_path.hh"
3 #include "ImageInfo.hh"
4 #include "log.hh"
5 #include "util.hh"
7 #include <cerrno>
8 #include <climits>
9 #include <cmath>
10 #include <cstdlib>
12 namespace goir {
14 // number of previous pixels kept in memory while scanning
15 static const unsigned NKEPTPIXES = 2;
16 struct prevpixes { // cannot be a STL container since pixel has to be a C array
17 sImLib::pixel data[NKEPTPIXES];
18 unsigned used;
21 void ImageInfo::AlgoResults::update_watermarks(bool is_known_edge,
22 unsigned value) {
23 if (is_known_edge) { log::trace(log::wm_trace, "edge, ");
24 if (value < min_positive) {
25 log::trace(log::wm_trace, "smaller than %u - new record\n", min_positive);
26 min_positive = value;
27 } else
28 log::trace(log::wm_trace, "larger than %u\n", min_positive);
29 } else { log::trace(log::wm_trace, "no, ");
30 if (value > max_negative) {
31 log::trace(log::wm_trace, "larger than %u - new record\n", max_negative);
32 max_negative = value;
33 } else
34 log::trace(log::wm_trace, "smaller than %u\n", max_negative);
37 void ImageInfo::AlgoResults::fprint(FILE* outf) {
38 std::vector<MatchData>::const_iterator m = missed.begin();
40 // First precompute y drift direction to know how to order 2
41 // points.
42 // FIXME: be more clever about null drifts ?
43 bool positive_y_drift =
44 (found_edges[0].y <= found_edges[1].y);
45 log::trace(log::debug_eval, "y drift is %s\n",
46 positive_y_drift ? "positive" : "negative");
47 // Assume x drift is never negative
48 assert(found_edges[0].x <= found_edges[1].x);
50 const_foreach(f, found_edges) {
51 // print all missed edges up to now
52 while (m != missed.end() && ((m->x < f->x) ||
53 (positive_y_drift ? (m->y < f->y)
54 : (m->y > f->y)))) {
55 fprintf(outf, "(%u,%u)(%u) ", m->x, m->y, m->delta);
56 m++;
59 if (f->false_match)
60 fprintf(outf, "*%u,%u(%u) ", f->x, f->y, f->delta);
61 else
62 fprintf(outf, "%u,%u(%u) ", f->x, f->y, f->delta);
65 // remaining missed if any
66 for (; m != missed.end(); m++)
67 fprintf(outf, "(%u,%u)(%u) ", m->x, m->y, m->delta);
69 fprintf(outf, "\n");
72 void ImageInfo::AlgoResults::draw_on(sImLib::Image & img) {
73 // light drawing of the scanline
74 img.draw_line(linedata->x0, linedata->y0,
75 linedata->x1, linedata->y1,
76 sImLib::black, 0.2);
78 // direction of drawing, orthogonal to line
79 int vec_x = -(linedata->y1 - linedata->y0);
80 int vec_y = (linedata->x1 - linedata->x0);
81 int vec_divisor = hypot(vec_x, vec_y);
83 std::vector<MatchData>::const_iterator m = missed.begin();
85 const_foreach(f, found_edges) {
86 // all missed edges up to now
87 while (m != missed.end() && ((m->x < f->x) || (m->y < f->y))) {
88 // FIXME: use missed delta when available
89 img.draw_line(m->x, m->y,
90 m->x - (int)m->delta * vec_x / vec_divisor,
91 m->y - (int)m->delta * vec_y / vec_divisor,
92 sImLib::magenta, 0.5);
93 m++;
96 sImLib::pixel const *color;
97 if (f->false_match)
98 color = &sImLib::cyan;
99 else
100 color = &sImLib::red;
101 img.draw_line(f->x, f->y,
102 f->x + (int)f->delta * vec_x / vec_divisor,
103 f->y + (int)f->delta * vec_y / vec_divisor,
104 *color, 0.5);
107 // remaining missed if any
108 for (; m != missed.end(); m++)
109 // FIXME: use missed delta when available
110 img.draw_line(m->x, m->y,
111 m->x - (int)m->delta * vec_x / vec_divisor,
112 m->y - (int)m->delta * vec_y / vec_divisor,
113 sImLib::magenta, 0.5);
116 // Read a list of unsigned values as x of edges along the line
117 // FIXME: other code relies on increasing order, should assert that ?
118 void ImageInfo::LineData::read_edges(FILE* f) {
119 std::pair<unsigned,unsigned> values;
120 while (fscanf(f, " %u,%u", &values.first, &values.second) == 2) {
121 known_edges.push_back(values);
122 // skip any parenthesized delta leftover from copypaste
123 int c = getc(f);
124 if (c == EOF)
125 break;
126 else if (c == '(') {
127 // skip until matching ')'
128 do {
129 c = getc(f);
130 } while ((c != EOF) && (c != ')'));
131 } else
132 if (EOF == ungetc(c, f)) {
133 perror("ungetc"); exit(1);
136 if (ferror(f)) {
137 perror("fscanf"); exit(1);
139 _edges_are_known = true;
142 void ImageInfo::LineData::maybe_update_watermarks(int algo_id,
143 unsigned x,
144 bool is_known_edge,
145 unsigned value) {
146 if (! edges_are_known())
147 return;
149 log::trace(log::wm_trace, "(%u) [%u] %u ? ", x, algo_id, value);
151 results[algo_id].update_watermarks(is_known_edge, value);
154 void ImageInfo::LineData::find_edges() {
155 // FIXME: these are dummy values - goal is deriving them from ImageInfo
156 const unsigned percomp_threshold_1pix = 25;
157 const unsigned summed_threshold_1pix = 50;
158 const unsigned percomp_threshold_2pix = 35;
159 const unsigned summed_threshold_2pix = 85;
161 unsigned next_known_edge = 0;
162 bool near_edge = false; // for filtering
163 struct prevpixes prevpixes;
164 prevpixes.used = 0;
165 //const sImLib::pixel* colors[3] = { &red, &green, &blue };
166 sImLib_forline(ii->imgp, ptrd,
167 x0, y0, x1, y1,
169 unsigned x=ii->imgp->x(ptrd);
170 unsigned y=ii->imgp->y(ptrd);
171 unsigned sum1pix=0;
172 unsigned maxdelta1=0;
173 unsigned sum2pix=0;
174 unsigned maxdelta2=0;
175 bool edge_found = false;
177 bool is_known_edge = (known_edges.size() > 0 &&
178 (x >= known_edges[next_known_edge].first) &&
179 (y >= known_edges[next_known_edge].second));
180 if (is_known_edge) next_known_edge++;
182 // no algo has anything to do on 1st point
183 if (prevpixes.used < 1)
184 goto endofloop;
186 unsigned delta1[3];
187 unsigned delta2[3];
188 for (int k = 0; k<3; k++) {
189 // algo: 1-pixel per-component (1/2)
190 delta1[k] = abs((*ptrd)[k] - (int)prevpixes.data[0][k]);
191 if (delta1[k]>maxdelta1) maxdelta1 = delta1[k];
192 // end algo
193 // algo: 1-pixel summed (1/2)
194 sum1pix += delta1[k];
195 // end algo
197 // following algos need 2 previous pixels recorded
198 if (prevpixes.used < 2)
199 continue;
201 // algo: 2-pixel per-component (1/2)
202 delta2[k] = abs((*ptrd)[k] - (int)prevpixes.data[1][k]);
203 if (delta2[k]>maxdelta2) maxdelta2 = delta2[k];
204 // end algo
205 // algo: 2-pixel summed (1/2)
206 sum2pix += delta2[k];
207 // end algo
210 // algo: 1-pixel per-component (2/2)
212 ImageInfo::AlgoResults::MatchData m(x,y,
213 abs(maxdelta1-percomp_threshold_1pix),
214 is_known_edge);
215 if (maxdelta1 >= percomp_threshold_1pix) {
216 results[ALGO_1PIX_PERCOMP].found_edges.push_back(m);
217 edge_found = true;
218 } else if (is_known_edge)
219 results[ALGO_1PIX_PERCOMP].missed.push_back(m);
220 maybe_update_watermarks(ALGO_1PIX_PERCOMP, x, is_known_edge, maxdelta1);
222 // end algo
223 // algo: 2-pixel per-component (2/2)
225 ImageInfo::AlgoResults::MatchData m(x,y,
226 abs(maxdelta2-percomp_threshold_2pix),
227 is_known_edge);
228 if (maxdelta2 >= percomp_threshold_2pix) {
229 results[ALGO_2PIX_PERCOMP].found_edges.push_back(m);
230 edge_found = true;
231 } else if (is_known_edge)
232 results[ALGO_2PIX_PERCOMP].missed.push_back(m);
233 maybe_update_watermarks(ALGO_2PIX_PERCOMP, x, is_known_edge, maxdelta2);
235 // end algo
237 // algo: 1-pixel summed (2/2) and filtered
239 ImageInfo::AlgoResults::MatchData m(x,y,
240 abs(sum1pix-summed_threshold_1pix),
241 is_known_edge);
242 if (sum1pix >= summed_threshold_1pix) {
243 results[ALGO_1PIX_COMPSUM].found_edges.push_back(m);
245 if (near_edge) {
246 ImageInfo::AlgoResults::MatchData & prev =
247 results[ALGO_1PIX_COMPSUM_FILTERED].found_edges.back();
248 if (prev.delta < m.delta) {
249 // overwrite previous match with better one
250 prev = m;
252 } else {
253 results[ALGO_1PIX_COMPSUM_FILTERED].found_edges.push_back(m);
254 near_edge = true;
256 } else {
257 near_edge = false;
258 if (is_known_edge) {
259 results[ALGO_1PIX_COMPSUM].missed.push_back(m);
260 results[ALGO_1PIX_COMPSUM_FILTERED].missed.push_back(m);
263 maybe_update_watermarks(ALGO_1PIX_COMPSUM, x, is_known_edge, sum1pix);
264 // FIXME: such watermaking inadequate with filtering
266 // end algo
267 // algo: 2-pixel summed (2/2)
269 ImageInfo::AlgoResults::MatchData m(x,y,
270 abs(sum2pix-summed_threshold_2pix),
271 is_known_edge);
272 if (prevpixes.used >= 2 &&
273 sum2pix >= summed_threshold_2pix) {
274 results[ALGO_2PIX_COMPSUM].found_edges.push_back(m);
275 edge_found = true;
276 } else if (is_known_edge)
277 results[ALGO_2PIX_COMPSUM].missed.push_back(m);
278 maybe_update_watermarks(ALGO_2PIX_COMPSUM, x, is_known_edge, sum2pix);
280 // end algo
282 //if (edge_found) goir::permute(colors[0],colors[1],colors[2]);
284 if (edge_found) {
285 log::trace(log::edge_details,
286 "# %u: [%u,%u,%u] -> [%u,%u,%u] : "
287 "%u = %u+%u+%u\n",
288 x, color_components(prevpixes.data[0]),
289 color_components((*ptrd)),
290 sum1pix, color_components(delta1));
293 endofloop:
294 if (prevpixes.used < NKEPTPIXES)
295 prevpixes.used++;
296 else
297 memcpy(prevpixes.data+1, prevpixes.data,
298 sizeof(sImLib::pixel)*(NKEPTPIXES-1));
299 memcpy(prevpixes.data[0], ptrd, sizeof(sImLib::pixel));
300 //memcpy(ptrd, colors[0], sizeof(sImLib::pixel));
303 if (edges_are_known()) {
304 log::trace(log::wm_summary, "COMP1: - %u | %u -\n",
305 results[ALGO_1PIX_PERCOMP].max_negative,
306 results[ALGO_1PIX_PERCOMP].min_positive);
307 log::trace(log::wm_summary, "GLOB1: - %u | %u -\n",
308 results[ALGO_1PIX_COMPSUM].max_negative,
309 results[ALGO_1PIX_COMPSUM].min_positive);
310 log::trace(log::wm_summary, "COMP2: - %u | %u -\n",
311 results[ALGO_2PIX_PERCOMP].max_negative,
312 results[ALGO_2PIX_PERCOMP].min_positive);
313 log::trace(log::wm_summary, "GLOB2: - %u | %u -\n",
314 results[ALGO_2PIX_COMPSUM].max_negative,
315 results[ALGO_2PIX_COMPSUM].min_positive);
319 void ImageInfo::LineData::fprint(FILE* outf) {
320 fprintf(outf, "[%u %u %u %u]\n", x0, y0, x1, y1);
321 if (known_edges.size() > 0)
322 known_edges.fprint(outf);
323 for (unsigned i = 0; i < results.size(); i++) {
324 fprintf(outf, "F%u:", i);
325 results[i].fprint(outf);
329 void ImageInfo::maybe_read_edge_data() {
330 std::string datafilename(imgp->filename);
331 datafilename += ".data";
332 FILE* f = fopen(datafilename.c_str(), "r");
333 if (f == NULL) {
334 if (errno != ENOENT) {
335 perror("fopen .data"); exit(1);
337 // else there is simply no .data file
338 } else {
339 LineData* line = NULL;
340 do {
341 int ret = getc(f);
342 if (ret == EOF) break;
343 if (ret == '[') { // line coords
344 unsigned x0,y0,x1,y1;
345 int ret = fscanf(f, "%u %u %u %u", &x0, &y0, &x1, &y1);
346 if (ret == EOF) {
347 fprintf(stderr, "EOF reading line coords\n"); exit(1);
348 } else if (ret < 4) {
349 fprintf(stderr, "Error: not enough numbers (%d) for line coords\n", ret); exit(1);
351 line = new LineData(this, x0, y0, x1, y1);
352 } else if (isdigit(ret)) { // edges
353 if (!line) {
354 fprintf(stderr, "Error: edges data with no previous line coords\n"); exit(1);
356 if (ungetc(ret, f) == EOF) {
357 perror("ungetc"); exit(1);
359 line->read_edges(f);
361 // make sure that there is always a "next edge"
362 std::pair<unsigned,unsigned> pair;
363 pair.first = imgp->width+1;
364 pair.second = imgp->height+1;
365 line->known_edges.push_back(pair);
367 lines.push_back(line);
368 line = NULL;
369 } else {
370 // consume till EOL
371 char* lineptr = NULL;
372 size_t dummy;
373 errno = 0;
374 getline(&lineptr, &dummy, f);
375 if (ferror(f)) {
376 perror("getline"); exit(1);
378 if (lineptr) free(lineptr);
380 } while(!feof(f));
381 fclose(f);
385 void ImageInfo::add_to_histo(unsigned x0, unsigned y0, unsigned x1, unsigned y1,
386 UVector *known_edges) {
387 // walk over the line for histo data
388 unsigned next_known_edge = 0;
389 struct prevpixes prevpixes;
390 prevpixes.used = 0;
391 sImLib_forline(imgp, ptrd,
392 x0, y0, x1, y1,
394 // FIXME: check what happens if we start from other end of line
396 unsigned x=imgp->x(ptrd);
397 unsigned y=imgp->y(ptrd);
398 unsigned sum1pix=0;
399 unsigned maxdelta1=0;
400 unsigned histo_selector = NONEDGES;
402 // no algo has anything to do on 1st point
403 if (prevpixes.used < 1)
404 goto endofloop;
406 // in which set of histograms shall we record this point ?
407 if (known_edges->size() > 0) {
408 if ((x >= (*known_edges)[next_known_edge].first) &&
409 (y >= (*known_edges)[next_known_edge].second)) {
410 next_known_edge++;
411 histo_selector = EDGES;
413 if ((x > (*known_edges)[next_known_edge].first) ||
414 (y > (*known_edges)[next_known_edge].second))
415 fprintf(stderr, "Missed an edge, using approximation!\n");
418 for (int k = 0; k<3; k++) {
419 unsigned delta1 = abs((*ptrd)[k] - (int)prevpixes.data[0][k]);
420 if (delta1>maxdelta1) maxdelta1 = delta1;
421 histos_c[histo_selector][k].record(delta1);
422 sum1pix += delta1;
424 histos[histo_selector].record(sum1pix);
425 histos_allc[histo_selector].record(maxdelta1);
427 endofloop:
428 if (prevpixes.used < NKEPTPIXES)
429 prevpixes.used++;
430 else
431 memcpy(prevpixes.data+1, prevpixes.data,
432 sizeof(sImLib::pixel)*(NKEPTPIXES-1));
433 memcpy(prevpixes.data[0], ptrd, sizeof(sImLib::pixel));
438 void ImageInfo::display_histo() {
439 printf("N: "), histos[NONEDGES].display();
440 if (histos[NONEDGES].samples())
441 printf("FP: "), histos[NONEDGES].display_potential_false_matches();
442 if (histos[EDGES].samples())
443 printf("E: "), histos[EDGES].display();
445 for (int k = 0; k<3; k++) {
446 printf("C%d N: ", k), histos_c[NONEDGES][k].display();
447 if (histos_c[NONEDGES][k].samples())
448 printf("C%d FP: ", k), histos_c[NONEDGES][k].display_potential_false_matches();
449 if (histos_c[EDGES][k].samples())
450 printf("C%d E: ", k), histos_c[EDGES][k].display();
452 printf("C_ N: "), histos_allc[NONEDGES].display();
453 if (histos_allc[NONEDGES].samples())
454 printf("C_ FP: "), histos_allc[NONEDGES].display_potential_false_matches();
455 if (histos_allc[EDGES].samples())
456 printf("C_ E: "), histos_allc[EDGES].display();