1 #include "sImLib/img_path.hh"
3 #include "ImageInfo.hh"
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
];
21 void ImageInfo::AlgoResults::update_watermarks(bool is_known_edge
,
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
);
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
);
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
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
)
55 fprintf(outf
, "(%u,%u)(%u) ", m
->x
, m
->y
, m
->delta
);
60 fprintf(outf
, "*%u,%u(%u) ", f
->x
, f
->y
, f
->delta
);
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
);
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
,
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);
96 sImLib::pixel
const *color
;
98 color
= &sImLib::cyan
;
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
,
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
127 // skip until matching ')'
130 } while ((c
!= EOF
) && (c
!= ')'));
132 if (EOF
== ungetc(c
, f
)) {
133 perror("ungetc"); exit(1);
137 perror("fscanf"); exit(1);
139 _edges_are_known
= true;
142 void ImageInfo::LineData::maybe_update_watermarks(int algo_id
,
146 if (! edges_are_known())
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
;
165 //const sImLib::pixel* colors[3] = { &red, &green, &blue };
166 sImLib_forline(ii
->imgp
, ptrd
,
169 unsigned x
=ii
->imgp
->x(ptrd
);
170 unsigned y
=ii
->imgp
->y(ptrd
);
172 unsigned maxdelta1
=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)
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
];
193 // algo: 1-pixel summed (1/2)
194 sum1pix
+= delta1
[k
];
197 // following algos need 2 previous pixels recorded
198 if (prevpixes
.used
< 2)
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
];
205 // algo: 2-pixel summed (1/2)
206 sum2pix
+= delta2
[k
];
210 // algo: 1-pixel per-component (2/2)
212 ImageInfo::AlgoResults::MatchData
m(x
,y
,
213 abs(maxdelta1
-percomp_threshold_1pix
),
215 if (maxdelta1
>= percomp_threshold_1pix
) {
216 results
[ALGO_1PIX_PERCOMP
].found_edges
.push_back(m
);
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
);
223 // algo: 2-pixel per-component (2/2)
225 ImageInfo::AlgoResults::MatchData
m(x
,y
,
226 abs(maxdelta2
-percomp_threshold_2pix
),
228 if (maxdelta2
>= percomp_threshold_2pix
) {
229 results
[ALGO_2PIX_PERCOMP
].found_edges
.push_back(m
);
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
);
237 // algo: 1-pixel summed (2/2) and filtered
239 ImageInfo::AlgoResults::MatchData
m(x
,y
,
240 abs(sum1pix
-summed_threshold_1pix
),
242 if (sum1pix
>= summed_threshold_1pix
) {
243 results
[ALGO_1PIX_COMPSUM
].found_edges
.push_back(m
);
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
253 results
[ALGO_1PIX_COMPSUM_FILTERED
].found_edges
.push_back(m
);
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
267 // algo: 2-pixel summed (2/2)
269 ImageInfo::AlgoResults::MatchData
m(x
,y
,
270 abs(sum2pix
-summed_threshold_2pix
),
272 if (prevpixes
.used
>= 2 &&
273 sum2pix
>= summed_threshold_2pix
) {
274 results
[ALGO_2PIX_COMPSUM
].found_edges
.push_back(m
);
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
);
282 //if (edge_found) goir::permute(colors[0],colors[1],colors[2]);
285 log::trace(log::edge_details
,
286 "# %u: [%u,%u,%u] -> [%u,%u,%u] : "
288 x
, color_components(prevpixes
.data
[0]),
289 color_components((*ptrd
)),
290 sum1pix
, color_components(delta1
));
294 if (prevpixes
.used
< NKEPTPIXES
)
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");
334 if (errno
!= ENOENT
) {
335 perror("fopen .data"); exit(1);
337 // else there is simply no .data file
339 LineData
* line
= NULL
;
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
);
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
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);
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
);
371 char* lineptr
= NULL
;
374 getline(&lineptr
, &dummy
, f
);
376 perror("getline"); exit(1);
378 if (lineptr
) free(lineptr
);
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
;
391 sImLib_forline(imgp
, ptrd
,
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
);
399 unsigned maxdelta1
=0;
400 unsigned histo_selector
= NONEDGES
;
402 // no algo has anything to do on 1st point
403 if (prevpixes
.used
< 1)
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
)) {
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
);
424 histos
[histo_selector
].record(sum1pix
);
425 histos_allc
[histo_selector
].record(maxdelta1
);
428 if (prevpixes
.used
< NKEPTPIXES
)
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();