* Small changes, for some reason VS 2015 can't link cprintf and they added a second function std::map::count and bind couldn't resolve the function. Substituted with a lambda-expression. * Created all 64bit version of the 3rd party libs. * Cleanup popup opens CleanupSettings instead of CleanupSettingsPane. Not finished, panel is wrong now (also uses CleanupSettings). * Tried to get Twain working, failed. * When there are more then 3 peg holes create additional holes. * When a hole touches the boundary it should still be considered a hole. * Do not compare dot size against fixed size. Will not work in some scan resolutions. Used the same value (PERCENT * max dot size) as later in the code in compare_dots. * When there are more than three peg holes the code looked for the best fit and used that. The problem was, that the middle hole was used as center for auto center. For three peg holes that is correct, but does not work for more than three. Now the best fit is calculated and from there the offset of the middle of the three best fit holes is calculated and applied. * One of the last changes broke VS2015 compatibility, it chocked on a missing include. * fix for msvs2013 * revert autopos.cpp * apply clang-format * use GIT-LFS
1500 lines
44 KiB
C++
1500 lines
44 KiB
C++
|
|
|
|
#include "autopos.h"
|
|
#include "cleanupcommon.h"
|
|
|
|
#include <sstream>
|
|
|
|
using namespace CleanupTypes;
|
|
|
|
/*
|
|
guardare DAFARE
|
|
guardare assumo
|
|
autoalign rgb ->ora viene chiamata con un buffer rgbm ! modificare
|
|
opportunamente
|
|
|
|
fare resize e realloc size dello stack a 65000 unita'
|
|
|
|
*/
|
|
|
|
#if defined(MACOSX) || defined(LINUX)
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
#endif
|
|
|
|
#define SECURITY_MARGIN_MM 4.0
|
|
|
|
static int Debug_flag = FALSE;
|
|
|
|
/*===========================================================================*/
|
|
/*
|
|
|
|
AUTOALIGNMENT
|
|
|
|
|
|
*/
|
|
|
|
#define AUTOAL_BLACK_COLS 2
|
|
#define AUTOAL_WHITE_COLS 2
|
|
#define AUTOAL_THRESHOLD 160
|
|
|
|
static int autoalign_gr8(UCHAR *buffer_gr8, int wrap, int lx, int ly,
|
|
int pix_origin, int dpix_dx, int dpix_dy,
|
|
int strip_width) {
|
|
int first_x[2], dx_dcol[2], target[2];
|
|
int i, x, y, cols;
|
|
int col_value, threshold;
|
|
int consec_black_cols, consec_white_cols, black_strip_edge;
|
|
UCHAR *pix, *origin;
|
|
int delta_x, delta_pix;
|
|
|
|
origin = buffer_gr8 + pix_origin;
|
|
|
|
first_x[0] = 0;
|
|
dx_dcol[0] = 1;
|
|
target[0] = strip_width / 2;
|
|
first_x[1] = lx - 1;
|
|
dx_dcol[1] = -1;
|
|
target[1] = lx - 1 - strip_width / 2;
|
|
|
|
threshold = AUTOAL_THRESHOLD * ly;
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
consec_black_cols = 0;
|
|
consec_white_cols = 0;
|
|
black_strip_edge = 0;
|
|
for (x = first_x[i], cols = 0; cols < strip_width;
|
|
x += dx_dcol[i], cols++) {
|
|
col_value = 0;
|
|
pix = origin + x * dpix_dx;
|
|
for (y = 0; y < ly; y++) {
|
|
col_value += *pix;
|
|
pix += dpix_dy;
|
|
}
|
|
if (col_value < threshold) {
|
|
consec_white_cols = 0;
|
|
consec_black_cols++;
|
|
if (consec_black_cols >= AUTOAL_BLACK_COLS) black_strip_edge = x;
|
|
} else {
|
|
consec_black_cols = 0;
|
|
consec_white_cols++;
|
|
if (consec_white_cols >= AUTOAL_WHITE_COLS && black_strip_edge)
|
|
goto found;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
|
|
found:
|
|
|
|
for (x = first_x[i], cols = 0; cols < strip_width; x += dx_dcol[i], cols++) {
|
|
pix = origin + x * dpix_dx;
|
|
for (y = 0; y < ly; y++) {
|
|
*pix = 255;
|
|
pix += dpix_dy;
|
|
}
|
|
}
|
|
delta_x = target[i] - black_strip_edge;
|
|
delta_pix = delta_x * dpix_dx;
|
|
if (delta_x > 0) {
|
|
for (x = lx - 1 - delta_x; x >= 0; x--) {
|
|
pix = origin + x * dpix_dx;
|
|
for (y = 0; y < ly; y++) {
|
|
*(pix + delta_pix) = *pix;
|
|
pix += dpix_dy;
|
|
}
|
|
}
|
|
for (x = lx - delta_x; x < lx; x++) {
|
|
pix = origin + x * dpix_dx;
|
|
for (y = 0; y < ly; y++) {
|
|
*pix = 255;
|
|
pix += dpix_dy;
|
|
}
|
|
}
|
|
} else if (delta_x < 0) {
|
|
for (x = -delta_x; x < lx; x++) {
|
|
pix = origin + x * dpix_dx;
|
|
for (y = 0; y < ly; y++) {
|
|
*(pix + delta_pix) = *pix;
|
|
pix += dpix_dy;
|
|
}
|
|
}
|
|
for (x = 0; x < -delta_x; x++) {
|
|
pix = origin + x * dpix_dx;
|
|
for (y = 0; y < ly; y++) {
|
|
*pix = 255;
|
|
pix += dpix_dy;
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static int autoalign_rgb(TPixel32 *buffer_rgb, int wrap, int lx, int ly,
|
|
int pix_origin, int dpix_dx, int dpix_dy,
|
|
int strip_width) {
|
|
int first_x[2], dx_dcol[2], target[2];
|
|
int i, x, y, cols;
|
|
int col_value, threshold;
|
|
int consec_black_cols, consec_white_cols, black_strip_edge;
|
|
TPixel32 *pix, *origin;
|
|
int delta_x, delta_pix;
|
|
|
|
origin = buffer_rgb + pix_origin;
|
|
|
|
first_x[0] = 0;
|
|
dx_dcol[0] = 1;
|
|
target[0] = strip_width / 2;
|
|
first_x[1] = lx - 1;
|
|
dx_dcol[1] = -1;
|
|
target[1] = lx - 1 - strip_width / 2;
|
|
|
|
threshold = AUTOAL_THRESHOLD * ly;
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
consec_black_cols = 0;
|
|
consec_white_cols = 0;
|
|
black_strip_edge = 0;
|
|
for (x = first_x[i], cols = 0; cols < strip_width;
|
|
x += dx_dcol[i], cols++) {
|
|
col_value = 0;
|
|
pix = origin + x * dpix_dx;
|
|
for (y = 0; y < ly; y++) {
|
|
col_value += (pix->r * 2 + pix->g * 5 + pix->b) >> 3;
|
|
pix += dpix_dy;
|
|
}
|
|
if (col_value < threshold) {
|
|
consec_white_cols = 0;
|
|
consec_black_cols++;
|
|
if (consec_black_cols >= AUTOAL_BLACK_COLS) black_strip_edge = x;
|
|
} else {
|
|
consec_black_cols = 0;
|
|
consec_white_cols++;
|
|
if (consec_white_cols >= AUTOAL_WHITE_COLS && black_strip_edge)
|
|
goto found;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
|
|
found:
|
|
|
|
for (x = first_x[i], cols = 0; cols < strip_width; x += dx_dcol[i], cols++) {
|
|
pix = origin + x * dpix_dx;
|
|
for (y = 0; y < ly; y++) {
|
|
pix->r = pix->g = pix->b = 255;
|
|
pix += dpix_dy;
|
|
}
|
|
}
|
|
delta_x = target[i] - black_strip_edge;
|
|
delta_pix = delta_x * dpix_dx;
|
|
if (delta_x > 0) {
|
|
for (x = lx - 1 - delta_x; x >= 0; x--) {
|
|
pix = origin + x * dpix_dx;
|
|
for (y = 0; y < ly; y++) {
|
|
(pix + delta_pix)->r = pix->r;
|
|
(pix + delta_pix)->g = pix->g;
|
|
(pix + delta_pix)->b = pix->b;
|
|
pix += dpix_dy;
|
|
}
|
|
}
|
|
for (x = lx - delta_x; x < lx; x++) {
|
|
pix = origin + x * dpix_dx;
|
|
for (y = 0; y < ly; y++) {
|
|
pix->r = pix->g = pix->b = 255;
|
|
pix += dpix_dy;
|
|
}
|
|
}
|
|
} else if (delta_x < 0) {
|
|
for (x = -delta_x; x < lx; x++) {
|
|
pix = origin + x * dpix_dx;
|
|
for (y = 0; y < ly; y++) {
|
|
(pix + delta_pix)->r = pix->r;
|
|
(pix + delta_pix)->g = pix->g;
|
|
(pix + delta_pix)->b = pix->b;
|
|
pix += dpix_dy;
|
|
}
|
|
}
|
|
for (x = 0; x < -delta_x; x++) {
|
|
pix = origin + x * dpix_dx;
|
|
for (y = 0; y < ly; y++) {
|
|
pix->r = pix->g = pix->b = 255;
|
|
pix += dpix_dy;
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
int do_autoalign(const TRasterImageP &image) {
|
|
int wrap, lx, ly, mx, my;
|
|
int pix_origin, dpix_dx, dpix_dy;
|
|
int strip_width;
|
|
|
|
// assumo che sia sempre orientata nel modo corretto
|
|
|
|
TRasterP ras = image->getRaster();
|
|
wrap = ras->getWrap();
|
|
assert(TRaster32P(ras) ||
|
|
TRasterGR8P(ras)); // per ricordare di gestire le img bw!
|
|
|
|
// assumo TOR_BOTLEFT:__OR TOR_BOTRIGHT:__OR TOR_TOPLEFT:__OR TOR_TOPRIGHT:
|
|
double dpix, dpiy;
|
|
image->getDpi(dpix, dpiy);
|
|
strip_width = (int)mmToPixel(5.0, dpix);
|
|
lx = ras->getLx();
|
|
ly = ras->getLy();
|
|
|
|
mx = lx - 1;
|
|
my = ly - 1;
|
|
|
|
// assumo CASE TOR_BOTLEFT:
|
|
pix_origin = 0;
|
|
dpix_dx = 1;
|
|
dpix_dy = wrap;
|
|
|
|
TRasterGR8P ras8(ras);
|
|
TRaster32P ras32(ras);
|
|
ras->lock();
|
|
int ret = FALSE;
|
|
|
|
if (ras8)
|
|
ret = autoalign_gr8(ras8->getRawData(), wrap, lx, ly, pix_origin, dpix_dx,
|
|
dpix_dy, strip_width);
|
|
|
|
else if (ras32)
|
|
ret = autoalign_rgb(ras32->pixels(), wrap, lx, ly, pix_origin, dpix_dx,
|
|
dpix_dy, strip_width);
|
|
else
|
|
assert(!"Unsupported pixel type");
|
|
|
|
ras->unlock();
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*===========================================================================*/
|
|
/*
|
|
|
|
AUTOCENTERING
|
|
|
|
|
|
*/
|
|
/*
|
|
* Calcoli in millimetri per questa funzione che alla fine restituisce un
|
|
* valore per la striscia di ricerca direttamente in pixel
|
|
*/
|
|
|
|
int compute_strip_pixel(FDG_INFO *fdg, double dpi) {
|
|
int i, strip_size_pix;
|
|
double half_size, max_half_size, strip_size_mm;
|
|
|
|
max_half_size = -1.0;
|
|
for (i = 0; i < (int)fdg->dots.size(); i++) {
|
|
half_size = (double)fdg->dots[i].lx * 0.5;
|
|
if (max_half_size < half_size) max_half_size = half_size;
|
|
}
|
|
strip_size_mm =
|
|
fdg->dist_ctr_hole_to_edge + max_half_size + SECURITY_MARGIN_MM;
|
|
|
|
strip_size_pix = (int)mmToPixel(strip_size_mm, dpi);
|
|
|
|
if (Debug_flag)
|
|
printf("Controllo una striscia larga %g mm e %d pixels\n", strip_size_mm,
|
|
strip_size_pix);
|
|
|
|
return strip_size_pix;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
#define SQMM_TO_SQPIXEL(area, x_res, y_res) \
|
|
((double)((x_res) * (y_res)) * (double)(area) * ((1.0 / 25.4) * (1.0 / 25.4)))
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
void convert_dots_mm_to_pixel(DOT *dots, int nd, double x_res, double y_res) {
|
|
int i;
|
|
|
|
for (i = 0; i < nd; i++) {
|
|
dots[i].x1 = troundp(mmToPixel(dots[i].x1, x_res));
|
|
dots[i].y1 = troundp(mmToPixel(dots[i].y1, y_res));
|
|
dots[i].x2 = troundp(mmToPixel(dots[i].x2, x_res));
|
|
dots[i].y2 = troundp(mmToPixel(dots[i].y2, y_res));
|
|
dots[i].x = (float)mmToPixel(dots[i].x, x_res);
|
|
dots[i].y = (float)mmToPixel(dots[i].y, y_res);
|
|
dots[i].lx = troundp(mmToPixel(dots[i].lx, x_res));
|
|
dots[i].ly = troundp(mmToPixel(dots[i].ly, y_res));
|
|
dots[i].area = troundp(SQMM_TO_SQPIXEL(dots[i].area, x_res, y_res));
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static char *Done = 0;
|
|
static int Done_rowsize = 0;
|
|
static int Done_colsize = 0;
|
|
#define DONE_MASK(I, J) (1 << (((I) + (J)*Done_rowsize) & 7))
|
|
#define DONE_BYTE(I, J) (((I) + (J)*Done_rowsize) >> 3)
|
|
#define SET_DONE(I, J) (Done[DONE_BYTE(I, J)] |= DONE_MASK(I, J))
|
|
#define NOT_DONE(I, J) (!(Done[DONE_BYTE(I, J)] & DONE_MASK(I, J)))
|
|
|
|
static int Pix_ystep = 0;
|
|
|
|
typedef struct big { unsigned lo, hi; } BIG;
|
|
#define CLEARBIG(B) ((B).lo = 0, (B).hi = 0, (B))
|
|
#define ADDBIG(B, X) \
|
|
((B).lo += (unsigned)(X), (B).hi += (B).lo >> 30, (B).lo &= 0x3fffffff, (B))
|
|
#define BIG_TO_DOUBLE(B) ((double)(B).hi * (double)0x40000000 + (double)(B).lo)
|
|
|
|
#define IS_BLACK_GR8(PIX) (*(PIX) < 110)
|
|
#define IS_VERY_BLACK_GR8(PIX) (*(PIX) < 30)
|
|
#define BLACK_WEIGHT_GR8(PIX) (256 - *(PIX))
|
|
|
|
#define RGBR(PIX) ((PIX)->r << 1)
|
|
#define RGBG(PIX) ((PIX)->g << 2)
|
|
#define RGBB(PIX) ((PIX)->b)
|
|
#define RGBVAL(PIX) (RGBR(PIX) + RGBG(PIX) + RGBB(PIX))
|
|
|
|
#define IS_BLACK_RGB(PIX) (RGBVAL(PIX) < 110 * 7)
|
|
#define IS_VERY_BLACK_RGB(PIX) (RGBVAL(PIX) < 30 * 7)
|
|
#define BLACK_WEIGHT_RGB(PIX) ((256 * 7 - RGBVAL(PIX)) >> 3)
|
|
|
|
static BIG Xsum, Ysum, Weightsum;
|
|
static int Xmin, Xmax, Ymin, Ymax, Npix;
|
|
#ifdef RECURSIVE_VERSION
|
|
static int Level, Max_level;
|
|
#endif
|
|
static int Very_black_found;
|
|
|
|
#ifdef DAFARE
|
|
#ifdef RECURSIVE_VERSION
|
|
static int Black_pixel = 0;
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef DAFARE
|
|
static int find_dots_bw(const TRasterP &img, int strip_width,
|
|
PEGS_SIDE pegs_side, DOT dotarray[], int dotarray_size,
|
|
int max_area);
|
|
#endif
|
|
static int find_dots_gr8(const TRasterGR8P &img, int strip_width,
|
|
PEGS_SIDE pegs_side, DOT dotarray[], int dotarray_size,
|
|
int max_area);
|
|
static int find_dots_rgb(const TRaster32P &img, int strip_width,
|
|
PEGS_SIDE pegs_side, DOT dotarray[], int dotarray_size,
|
|
int max_area);
|
|
static void
|
|
#ifdef DAFARE
|
|
visit_bw(int i, int j, int x, int y, int bit, UCHAR *byte),
|
|
#endif
|
|
visit_gr8(int i, int j, int x, int y, UCHAR *pix),
|
|
visit_rgb(int i, int j, int x, int y, TPixel32 *pix),
|
|
stampa_dot(DOT const *dot);
|
|
|
|
//! \brief Find the best matching pegs
|
|
//!
|
|
//! The found pegs are in array dots. The function checks, which of those best
|
|
//! fits the reference in
|
|
//! reference. The three best matching dots are returned in parameters i, j, k.
|
|
static int compare_dots(DOT const dots[], int ndots, DOT reference[],
|
|
int ref_dot, int &i, int &j, int &k);
|
|
|
|
#define REVERSE(byte, bit) \
|
|
{ \
|
|
unsigned char mask; \
|
|
mask = 1 << (bit); \
|
|
*(byte) ^= mask; \
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
typedef struct {
|
|
short ret, bit;
|
|
int x, y, i, j;
|
|
void *ptr;
|
|
} STACK_INFO;
|
|
|
|
static STACK_INFO *Stack = 0;
|
|
static int Stack_alloc_size = 0;
|
|
static int Stack_size = 0;
|
|
|
|
#define CREATE_STACK \
|
|
{ \
|
|
assert(!Stack); \
|
|
Stack_alloc_size = 65500; \
|
|
Stack_size = 0; \
|
|
Stack = (STACK_INFO *)malloc(Stack_alloc_size * sizeof(STACK_INFO)); \
|
|
if (!Stack) return FALSE; \
|
|
}
|
|
|
|
#define DESTROY_STACK \
|
|
{ \
|
|
Stack_alloc_size = 0; \
|
|
Stack_size = 0; \
|
|
free(Stack); \
|
|
Stack = 0; \
|
|
}
|
|
|
|
#define STACK_IS_EMPTY (!Stack_size)
|
|
|
|
#define PUSH_ONTO_STACK(RET, X, Y, I, J, BIT, PTR) \
|
|
{ \
|
|
if (Stack_size >= Stack_alloc_size) { \
|
|
Stack_alloc_size += 65500; \
|
|
Stack = \
|
|
(STACK_INFO *)realloc(Stack, Stack_alloc_size * sizeof(STACK_INFO)); \
|
|
if (!Stack) return; \
|
|
} \
|
|
Stack[Stack_size].ret = (RET); \
|
|
Stack[Stack_size].x = (X); \
|
|
Stack[Stack_size].y = (Y); \
|
|
Stack[Stack_size].i = (I); \
|
|
Stack[Stack_size].j = (J); \
|
|
Stack[Stack_size].bit = (BIT); \
|
|
Stack[Stack_size].ptr = (PTR); \
|
|
Stack_size++; \
|
|
}
|
|
|
|
#define POP_FROM_STACK_U(RET, X, Y, I, J, BIT, PTR) \
|
|
{ \
|
|
Stack_size--; \
|
|
(RET) = Stack[Stack_size].ret; \
|
|
(X) = Stack[Stack_size].x; \
|
|
(Y) = Stack[Stack_size].y; \
|
|
(I) = Stack[Stack_size].i; \
|
|
(J) = Stack[Stack_size].j; \
|
|
(BIT) = Stack[Stack_size].bit; \
|
|
(PTR) = (UCHAR *)Stack[Stack_size].ptr; \
|
|
}
|
|
|
|
#define POP_FROM_STACK_TPIXEL32(RET, X, Y, I, J, BIT, PTR) \
|
|
{ \
|
|
Stack_size--; \
|
|
(RET) = Stack[Stack_size].ret; \
|
|
(X) = Stack[Stack_size].x; \
|
|
(Y) = Stack[Stack_size].y; \
|
|
(I) = Stack[Stack_size].i; \
|
|
(J) = Stack[Stack_size].j; \
|
|
(BIT) = Stack[Stack_size].bit; \
|
|
(PTR) = (TPixel32 *)Stack[Stack_size].ptr; \
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static int find_dots(const TRasterP &img, int strip_width, PEGS_SIDE pegs_side,
|
|
DOT dotarray[], int dotarray_size, int max_area) {
|
|
TRaster32P ras32(img);
|
|
if (ras32)
|
|
return find_dots_rgb(ras32, strip_width, pegs_side, dotarray, dotarray_size,
|
|
max_area);
|
|
TRasterGR8P ras8(img);
|
|
if (ras8)
|
|
return find_dots_gr8(ras8, strip_width, pegs_side, dotarray, dotarray_size,
|
|
max_area);
|
|
assert(!"Unsupported pixel type");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
#ifdef DAFARE
|
|
static int find_dots_bw(const TRasterP &img, int strip_width,
|
|
PEGS_SIDE pegs_side, DOT dotarray[], int dotarray_size,
|
|
int max_area) {
|
|
int n_dots, ins, shift;
|
|
int x, y; // coords in img coord system
|
|
int i, j; // coords in done coord system
|
|
int x0, y0, xsize, ysize, xlast, ylast, bit;
|
|
UCHAR *byte, *buffer;
|
|
int dot_lx, dot_ly;
|
|
float dot_x, dot_y;
|
|
int vertical;
|
|
|
|
if (img->type == RAS_WB)
|
|
Black_pixel = 1;
|
|
else if (img->type == RAS_BW)
|
|
Black_pixel = 0;
|
|
else {
|
|
TERROR("find dots error: bad image type");
|
|
return 0;
|
|
}
|
|
|
|
switch (pegs_side) {
|
|
case PEGS_BOTTOM:
|
|
case PEGS_TOP:
|
|
x0 = 0;
|
|
y0 = pegs_side == PEGS_BOTTOM ? 0 : img->ly - strip_width;
|
|
xsize = img->lx;
|
|
ysize = strip_width;
|
|
vertical = FALSE;
|
|
break;
|
|
|
|
case PEGS_LEFT:
|
|
case PEGS_RIGHT:
|
|
x0 = pegs_side == PEGS_LEFT ? 0 : img->lx - strip_width;
|
|
y0 = 0;
|
|
xsize = strip_width;
|
|
ysize = img->ly;
|
|
vertical = TRUE;
|
|
break;
|
|
|
|
default: {
|
|
std::ostringstream os;
|
|
os << "find dots internal error: pegs_side = " << std::hex << pegs_side
|
|
<< '\0';
|
|
throw TCleanupException(os.str().c_str());
|
|
x0 = y0 = xsize = ysize = vertical = 0;
|
|
}
|
|
}
|
|
xlast = x0 + xsize - 1;
|
|
ylast = y0 + ysize - 1;
|
|
n_dots = 0;
|
|
Done_rowsize = xsize + 2;
|
|
Done_colsize = ysize + 2;
|
|
Done = (char *)calloc((size_t)((Done_rowsize * Done_colsize + 7) >> 3),
|
|
sizeof(char));
|
|
if (!Done) {
|
|
throw TCleanupException("find_dots: out of memory");
|
|
}
|
|
for (i = 0; i < Done_rowsize; i++) {
|
|
SET_DONE(i, 0);
|
|
SET_DONE(i, Done_colsize - 1);
|
|
}
|
|
for (j = 0; j < Done_colsize; j++) {
|
|
SET_DONE(0, j);
|
|
SET_DONE(Done_rowsize - 1, j);
|
|
}
|
|
buffer = (UCHAR *)img->buffer;
|
|
Pix_ystep = (img->wrap + 7) >> 3;
|
|
|
|
if (Debug_flag) {
|
|
printf("Zona di scansione: (%d,%d) -- (%d,%d)\n", x0, y0, xlast, ylast);
|
|
printf("wrap: %d\n", img->wrap);
|
|
}
|
|
|
|
#ifdef RECURSIVE_VERSION
|
|
Max_level = max_area * 6 / 5;
|
|
#endif
|
|
|
|
CREATE_STACK
|
|
for (j = 1, y = y0; y <= ylast; j++, y++) {
|
|
bit = 7 - (x0 & 7);
|
|
byte = buffer + (x0 >> 3) + y * Pix_ystep;
|
|
for (i = 1, x = x0; x <= xlast; i++, x++) {
|
|
if (NOT_DONE(i, j) && ((*byte >> bit) & 1) == Black_pixel) {
|
|
CLEARBIG(Xsum);
|
|
CLEARBIG(Ysum);
|
|
CLEARBIG(Weightsum);
|
|
Xmin = Xmax = x;
|
|
Ymin = Ymax = y;
|
|
Npix = 0;
|
|
#ifdef RECURSIVE_VERSION
|
|
Level = 0;
|
|
#endif
|
|
visit_bw(i, j, x, y, bit, byte);
|
|
dot_lx = Xmax - Xmin + 1;
|
|
dot_ly = Ymax - Ymin + 1;
|
|
if (Npix < max_area * 3 / 2 && dot_lx > 3 && dot_lx < (xsize >> 1) &&
|
|
dot_ly > 3 && dot_ly < (ysize >> 1) && Xmin > x0 && Xmax < xlast &&
|
|
Ymin > y0 && Ymax < ylast) {
|
|
dot_x = BIG_TO_DOUBLE(Xsum) / BIG_TO_DOUBLE(Weightsum);
|
|
dot_y = BIG_TO_DOUBLE(Ysum) / BIG_TO_DOUBLE(Weightsum);
|
|
if (vertical) {
|
|
for (ins = 0; ins < n_dots; ins++)
|
|
if (dotarray[ins].y > dot_y) break;
|
|
} else {
|
|
for (ins = 0; ins < n_dots; ins++)
|
|
if (dotarray[ins].x > dot_x) break;
|
|
}
|
|
for (shift = n_dots - 1; shift >= ins; shift--)
|
|
dotarray[shift + 1] = dotarray[shift];
|
|
n_dots++;
|
|
dotarray[ins].x1 = Xmin;
|
|
dotarray[ins].x2 = Xmax;
|
|
dotarray[ins].y1 = Ymin;
|
|
dotarray[ins].y2 = Ymax;
|
|
dotarray[ins].lx = dot_lx;
|
|
dotarray[ins].ly = dot_ly;
|
|
dotarray[ins].area = Npix;
|
|
dotarray[ins].x = dot_x;
|
|
dotarray[ins].y = dot_y;
|
|
if (n_dots >= dotarray_size) goto end_loop;
|
|
}
|
|
}
|
|
if (bit == 0) {
|
|
bit = 7;
|
|
byte++;
|
|
} else
|
|
bit--;
|
|
}
|
|
}
|
|
end_loop:
|
|
DESTROY_STACK
|
|
free(Done);
|
|
Done = NIL;
|
|
return n_dots;
|
|
}
|
|
#endif
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static int find_dots_gr8(const TRasterGR8P &img, int strip_width,
|
|
PEGS_SIDE pegs_side, DOT dotarray[], int dotarray_size,
|
|
int max_area) {
|
|
int n_dots, ins, shift;
|
|
int x, y; /* coords in img coord system */
|
|
int i, j; /* coords in done coord system */
|
|
int x0, y0, xsize, ysize, xlast, ylast;
|
|
UCHAR *pix, *buffer;
|
|
int dot_lx, dot_ly;
|
|
float dot_x, dot_y;
|
|
int vertical;
|
|
|
|
switch (pegs_side) {
|
|
case PEGS_BOTTOM:
|
|
case PEGS_TOP:
|
|
x0 = 0;
|
|
y0 = pegs_side == PEGS_BOTTOM ? 0 : img->getLy() - strip_width;
|
|
xsize = img->getLx();
|
|
ysize = strip_width;
|
|
vertical = FALSE;
|
|
break;
|
|
case PEGS_LEFT:
|
|
case PEGS_RIGHT:
|
|
x0 = pegs_side == PEGS_LEFT ? 0 : img->getLx() - strip_width;
|
|
y0 = 0;
|
|
xsize = strip_width;
|
|
ysize = img->getLy();
|
|
vertical = TRUE;
|
|
break;
|
|
default: {
|
|
std::ostringstream os;
|
|
os << "find dots internal error: pegs_side = " << std::hex << pegs_side
|
|
<< '\0';
|
|
throw TCleanupException(os.str().c_str());
|
|
x0 = y0 = xsize = ysize = vertical = 0;
|
|
}
|
|
}
|
|
|
|
xlast = x0 + xsize - 1;
|
|
ylast = y0 + ysize - 1;
|
|
n_dots = 0;
|
|
Done_rowsize = xsize + 2;
|
|
Done_colsize = ysize + 2;
|
|
Done = (char *)calloc((size_t)((Done_rowsize * Done_colsize + 7) >> 3),
|
|
sizeof(char));
|
|
if (!Done) {
|
|
throw TCleanupException("find_dots: out of memory");
|
|
}
|
|
for (i = 0; i < Done_rowsize; i++) {
|
|
SET_DONE(i, 0);
|
|
SET_DONE(i, Done_colsize - 1);
|
|
}
|
|
for (j = 0; j < Done_colsize; j++) {
|
|
SET_DONE(0, j);
|
|
SET_DONE(Done_rowsize - 1, j);
|
|
}
|
|
|
|
Pix_ystep = img->getWrap();
|
|
if (Debug_flag) {
|
|
printf("Zona di scansione: (%d,%d) -- (%d,%d)\n", x0, y0, xlast, ylast);
|
|
printf("wrap: %d\n", img->getWrap());
|
|
}
|
|
#ifdef RECURSIVE_VERSION
|
|
Max_level = max_area * 6 / 5;
|
|
#endif
|
|
|
|
img->lock();
|
|
|
|
buffer = (UCHAR *)img->getRawData();
|
|
CREATE_STACK
|
|
for (j = 1, y = y0; y <= ylast; j++, y++)
|
|
for (i = 1, x = x0, pix = buffer + x0 + y * Pix_ystep; x <= xlast;
|
|
i++, x++, pix++)
|
|
if (NOT_DONE(i, j) && IS_BLACK_GR8(pix)) {
|
|
CLEARBIG(Xsum);
|
|
CLEARBIG(Ysum);
|
|
CLEARBIG(Weightsum);
|
|
Xmin = Xmax = x;
|
|
Ymin = Ymax = y;
|
|
Npix = 0;
|
|
#ifdef RECURSIVE_VERSION
|
|
Level = 0;
|
|
#endif
|
|
visit_gr8(i, j, x, y, pix);
|
|
dot_lx = Xmax - Xmin + 1;
|
|
dot_ly = Ymax - Ymin + 1;
|
|
if (Npix < max_area * 3 / 2 && dot_lx > 3 && dot_lx < (xsize >> 1) &&
|
|
dot_ly > 3 && dot_ly < (ysize >> 1) && Xmin > x0 && Xmax < xlast &&
|
|
Ymin > y0 && Ymax < ylast) {
|
|
dot_x = (float)(BIG_TO_DOUBLE(Xsum) / BIG_TO_DOUBLE(Weightsum));
|
|
dot_y = (float)(BIG_TO_DOUBLE(Ysum) / BIG_TO_DOUBLE(Weightsum));
|
|
if (vertical) {
|
|
for (ins = 0; ins < n_dots; ins++)
|
|
if (dotarray[ins].y > dot_y) break;
|
|
} else {
|
|
for (ins = 0; ins < n_dots; ins++)
|
|
if (dotarray[ins].x > dot_x) break;
|
|
}
|
|
for (shift = n_dots - 1; shift >= ins; shift--)
|
|
dotarray[shift + 1] = dotarray[shift];
|
|
n_dots++;
|
|
dotarray[ins].x1 = Xmin;
|
|
dotarray[ins].x2 = Xmax;
|
|
dotarray[ins].y1 = Ymin;
|
|
dotarray[ins].y2 = Ymax;
|
|
dotarray[ins].lx = dot_lx;
|
|
dotarray[ins].ly = dot_ly;
|
|
dotarray[ins].area = Npix;
|
|
dotarray[ins].x = dot_x;
|
|
dotarray[ins].y = dot_y;
|
|
if (n_dots >= dotarray_size) goto end_loop;
|
|
}
|
|
}
|
|
end_loop:
|
|
DESTROY_STACK
|
|
|
|
free(Done);
|
|
Done = 0;
|
|
img->unlock();
|
|
return n_dots;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static int find_dots_rgb(const TRaster32P &img, int strip_width,
|
|
PEGS_SIDE pegs_side, DOT dotarray[], int dotarray_size,
|
|
int max_area) {
|
|
int n_dots, ins, shift;
|
|
int x, y; /* coords in img coord system */
|
|
int i, j; /* coords in done coord system */
|
|
int x0, y0, xsize, ysize, xlast, ylast;
|
|
TPixel32 *pix, *buffer;
|
|
int dot_lx, dot_ly;
|
|
float dot_x, dot_y;
|
|
int vertical;
|
|
|
|
assert(img->getPixelSize() ==
|
|
4); /*questo e' per ricordare che l'algo e' per RGB*/
|
|
switch (pegs_side) {
|
|
case PEGS_BOTTOM:
|
|
case PEGS_TOP:
|
|
x0 = 0;
|
|
y0 = pegs_side == PEGS_BOTTOM ? 0 : img->getLy() - strip_width;
|
|
xsize = img->getLx();
|
|
ysize = strip_width;
|
|
vertical = FALSE;
|
|
break;
|
|
case PEGS_LEFT:
|
|
case PEGS_RIGHT:
|
|
x0 = pegs_side == PEGS_LEFT ? 0 : img->getLx() - strip_width;
|
|
y0 = 0;
|
|
xsize = strip_width;
|
|
ysize = img->getLy();
|
|
vertical = TRUE;
|
|
break;
|
|
default: {
|
|
std::ostringstream os;
|
|
os << "find dots internal error: pegs_side = " << std::hex << pegs_side
|
|
<< '\0';
|
|
throw TCleanupException(os.str().c_str());
|
|
x0 = y0 = xsize = ysize = vertical = 0;
|
|
break;
|
|
}
|
|
}
|
|
xlast = x0 + xsize - 1;
|
|
ylast = y0 + ysize - 1;
|
|
n_dots = 0;
|
|
Done_rowsize = xsize + 2;
|
|
Done_colsize = ysize + 2;
|
|
Done = (char *)calloc((size_t)((Done_rowsize * Done_colsize + 7) >> 3),
|
|
sizeof(char));
|
|
if (!Done) {
|
|
throw TCleanupException("find_dots: out of memory");
|
|
}
|
|
for (i = 0; i < Done_rowsize; i++) {
|
|
SET_DONE(i, 0);
|
|
SET_DONE(i, Done_colsize - 1);
|
|
}
|
|
for (j = 0; j < Done_colsize; j++) {
|
|
SET_DONE(0, j);
|
|
SET_DONE(Done_rowsize - 1, j);
|
|
}
|
|
buffer = img->pixels();
|
|
Pix_ystep = img->getWrap();
|
|
if (Debug_flag) {
|
|
printf("Zona di scansione: (%d,%d) -- (%d,%d)\n", x0, y0, xlast, ylast);
|
|
printf("wrap: %d\n", img->getWrap());
|
|
}
|
|
#ifdef RECURSIVE_VERSION
|
|
Max_level = max_area * 6 / 5;
|
|
#endif
|
|
|
|
CREATE_STACK
|
|
for (j = 1, y = y0; y <= ylast; j++, y++)
|
|
for (i = 1, x = x0, pix = buffer + x0 + y * Pix_ystep; x <= xlast;
|
|
i++, x++, pix++)
|
|
if (NOT_DONE(i, j) && IS_BLACK_RGB(pix)) {
|
|
CLEARBIG(Xsum);
|
|
CLEARBIG(Ysum);
|
|
CLEARBIG(Weightsum);
|
|
Xmin = Xmax = x;
|
|
Ymin = Ymax = y;
|
|
Npix = 0;
|
|
#ifdef RECURSIVE_VERSION
|
|
Level = 0;
|
|
#endif
|
|
visit_rgb(i, j, x, y, pix);
|
|
dot_lx = Xmax - Xmin + 1;
|
|
dot_ly = Ymax - Ymin + 1;
|
|
if (Npix < max_area * 3 / 2 && dot_lx > 3 && dot_lx < (xsize >> 1) &&
|
|
dot_ly > 3 && dot_ly < (ysize >> 1) && Xmin > x0 && Xmax <= xlast &&
|
|
Ymin > y0 && Ymax <= ylast) {
|
|
dot_x = (float)(BIG_TO_DOUBLE(Xsum) / BIG_TO_DOUBLE(Weightsum));
|
|
dot_y = (float)(BIG_TO_DOUBLE(Ysum) / BIG_TO_DOUBLE(Weightsum));
|
|
if (vertical) {
|
|
for (ins = 0; ins < n_dots; ins++)
|
|
if (dotarray[ins].y > dot_y) break;
|
|
} else {
|
|
for (ins = 0; ins < n_dots; ins++)
|
|
if (dotarray[ins].x > dot_x) break;
|
|
}
|
|
for (shift = n_dots - 1; shift >= ins; shift--)
|
|
dotarray[shift + 1] = dotarray[shift];
|
|
n_dots++;
|
|
dotarray[ins].x1 = Xmin;
|
|
dotarray[ins].x2 = Xmax;
|
|
dotarray[ins].y1 = Ymin;
|
|
dotarray[ins].y2 = Ymax;
|
|
dotarray[ins].lx = dot_lx;
|
|
dotarray[ins].ly = dot_ly;
|
|
dotarray[ins].area = Npix;
|
|
dotarray[ins].x = dot_x;
|
|
dotarray[ins].y = dot_y;
|
|
if (n_dots >= dotarray_size) goto end_loop;
|
|
}
|
|
}
|
|
end_loop:
|
|
DESTROY_STACK
|
|
free(Done);
|
|
Done = 0;
|
|
return n_dots;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
#ifdef DAFARE
|
|
static void visit_bw(int i, int j, int x, int y, int bit, UCHAR *byte) {
|
|
int ret, next_bit, prev_bit;
|
|
UCHAR *next_byte, *prev_byte;
|
|
|
|
start:
|
|
ADDBIG(Xsum, x);
|
|
ADDBIG(Ysum, y);
|
|
ADDBIG(Weightsum, 1);
|
|
if (x < Xmin) Xmin = x;
|
|
if (x > Xmax) Xmax = x;
|
|
if (y < Ymin) Ymin = y;
|
|
if (y > Ymax) Ymax = y;
|
|
Npix++;
|
|
SET_DONE(i, j);
|
|
|
|
next_bit = bit - 1;
|
|
if (next_bit < 0) {
|
|
next_bit = 7;
|
|
next_byte = byte + 1;
|
|
} else
|
|
next_byte = byte;
|
|
if (NOT_DONE(i + 1, j) && ((*next_byte >> next_bit) & 1) == Black_pixel) {
|
|
PUSH_ONTO_STACK(1, x, y, i, j, bit, byte)
|
|
i++;
|
|
x++;
|
|
bit = next_bit;
|
|
byte = next_byte;
|
|
goto start;
|
|
return_1:;
|
|
}
|
|
prev_bit = bit + 1;
|
|
if (prev_bit > 7) {
|
|
prev_bit = 0;
|
|
prev_byte = byte - 1;
|
|
} else
|
|
prev_byte = byte;
|
|
if (NOT_DONE(i - 1, j) && ((*prev_byte >> prev_bit) & 1) == Black_pixel) {
|
|
PUSH_ONTO_STACK(2, x, y, i, j, bit, byte)
|
|
i--;
|
|
x--;
|
|
bit = prev_bit;
|
|
byte = prev_byte;
|
|
goto start;
|
|
return_2:;
|
|
}
|
|
if (NOT_DONE(i, j + 1) && ((*(byte + Pix_ystep) >> bit) & 1) == Black_pixel) {
|
|
PUSH_ONTO_STACK(3, x, y, i, j, bit, byte)
|
|
j++;
|
|
y++;
|
|
byte += Pix_ystep;
|
|
goto start;
|
|
return_3:;
|
|
}
|
|
if (NOT_DONE(i, j - 1) && ((*(byte - Pix_ystep) >> bit) & 1) == Black_pixel) {
|
|
PUSH_ONTO_STACK(4, x, y, i, j, bit, byte)
|
|
j--;
|
|
y--;
|
|
byte -= Pix_ystep;
|
|
goto start;
|
|
return_4:;
|
|
}
|
|
if (!STACK_IS_EMPTY) {
|
|
POP_FROM_STACK_U(ret, x, y, i, j, bit, byte);
|
|
switch (ret) {
|
|
case 1:
|
|
goto return_1;
|
|
case 2:
|
|
goto return_2;
|
|
case 3:
|
|
goto return_3;
|
|
case 4:
|
|
goto return_4;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
#ifdef RECURSIVE_VERSION
|
|
|
|
static void visit_bw(int i, int j, int x, int y, int bit, UCHAR *byte) {
|
|
int next_bit, prev_bit;
|
|
UCHAR *next_byte, *prev_byte;
|
|
|
|
if (Level >= Max_level)
|
|
return;
|
|
else
|
|
Level++;
|
|
|
|
ADDBIG(Xsum, x);
|
|
ADDBIG(Ysum, y);
|
|
ADDBIG(Weightsum, 1);
|
|
|
|
if (x < Xmin) Xmin = x;
|
|
if (x > Xmax) Xmax = x;
|
|
if (y < Ymin) Ymin = y;
|
|
if (y > Ymax) Ymax = y;
|
|
Npix++;
|
|
SET_DONE(i, j);
|
|
|
|
next_bit = bit - 1;
|
|
if (next_bit < 0) {
|
|
next_byte = byte + 1;
|
|
next_bit = 7;
|
|
} else
|
|
next_byte = byte;
|
|
|
|
prev_bit = bit + 1;
|
|
if (prev_bit > 7) {
|
|
prev_byte = byte - 1;
|
|
prev_bit = 0;
|
|
} else
|
|
prev_byte = byte;
|
|
|
|
if (NOT_DONE(i + 1, j) && ((*next_byte >> next_bit) & 1) == Black_pixel)
|
|
visit_bw(i + 1, j, x + 1, y, next_bit, next_byte);
|
|
|
|
if (NOT_DONE(i - 1, j) && ((*prev_byte >> prev_bit) & 1) == Black_pixel)
|
|
visit_bw(i - 1, j, x - 1, y, prev_bit, prev_byte);
|
|
|
|
if (NOT_DONE(i, j + 1) && ((*(byte + Pix_ystep) >> bit) & 1) == Black_pixel)
|
|
visit_bw(i, j + 1, x, y + 1, bit, byte + Pix_ystep);
|
|
|
|
if (NOT_DONE(i, j - 1) && ((*(byte - Pix_ystep) >> bit) & 1) == Black_pixel)
|
|
visit_bw(i, j - 1, x, y - 1, bit, byte - Pix_ystep);
|
|
|
|
Level--;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static void visit_gr8(int i, int j, int x, int y, UCHAR *pix) {
|
|
int weight, ret, dummy;
|
|
|
|
start:
|
|
weight = BLACK_WEIGHT_GR8(pix);
|
|
ADDBIG(Xsum, x * weight);
|
|
ADDBIG(Ysum, y * weight);
|
|
ADDBIG(Weightsum, weight);
|
|
if (IS_VERY_BLACK_GR8(pix)) Very_black_found = TRUE;
|
|
if (x < Xmin) Xmin = x;
|
|
if (x > Xmax) Xmax = x;
|
|
if (y < Ymin) Ymin = y;
|
|
if (y > Ymax) Ymax = y;
|
|
Npix++;
|
|
SET_DONE(i, j);
|
|
|
|
if (NOT_DONE(i + 1, j) && IS_BLACK_GR8(pix + 1)) {
|
|
PUSH_ONTO_STACK(1, x, y, i, j, 0, pix)
|
|
i++;
|
|
x++;
|
|
pix++;
|
|
goto start;
|
|
return_1:;
|
|
}
|
|
if (NOT_DONE(i - 1, j) && IS_BLACK_GR8(pix - 1)) {
|
|
PUSH_ONTO_STACK(2, x, y, i, j, 0, pix)
|
|
i--;
|
|
x--;
|
|
pix--;
|
|
goto start;
|
|
return_2:;
|
|
}
|
|
if (NOT_DONE(i, j + 1) && IS_BLACK_GR8(pix + Pix_ystep)) {
|
|
PUSH_ONTO_STACK(3, x, y, i, j, 0, pix)
|
|
j++;
|
|
y++;
|
|
pix += Pix_ystep;
|
|
goto start;
|
|
return_3:;
|
|
}
|
|
if (NOT_DONE(i, j - 1) && IS_BLACK_GR8(pix - Pix_ystep)) {
|
|
PUSH_ONTO_STACK(4, x, y, i, j, 0, pix)
|
|
j--;
|
|
y--;
|
|
pix -= Pix_ystep;
|
|
goto start;
|
|
return_4:;
|
|
}
|
|
if (!STACK_IS_EMPTY) {
|
|
POP_FROM_STACK_U(ret, x, y, i, j, dummy, pix);
|
|
switch (ret) {
|
|
case 1:
|
|
goto return_1;
|
|
case 2:
|
|
goto return_2;
|
|
case 3:
|
|
goto return_3;
|
|
case 4:
|
|
goto return_4;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
#ifdef RECURSIVE_VERSION
|
|
|
|
static void visit_gr8(int i, int j, int x, int y, UCHAR *pix) {
|
|
int weight;
|
|
|
|
if (Level >= Max_level)
|
|
return;
|
|
else
|
|
Level++;
|
|
|
|
weight = BLACK_WEIGHT_GR8(pix);
|
|
ADDBIG(Xsum, x * weight);
|
|
ADDBIG(Ysum, y * weight);
|
|
ADDBIG(Weightsum, weight);
|
|
if (IS_VERY_BLACK_GR8(pix)) Very_black_found = TRUE;
|
|
if (x < Xmin) Xmin = x;
|
|
if (x > Xmax) Xmax = x;
|
|
if (y < Ymin) Ymin = y;
|
|
if (y > Ymax) Ymax = y;
|
|
Npix++;
|
|
SET_DONE(i, j);
|
|
|
|
if (NOT_DONE(i + 1, j) && IS_BLACK_GR8(pix + 1))
|
|
visit_gr8(i + 1, j, x + 1, y, pix + 1);
|
|
if (NOT_DONE(i - 1, j) && IS_BLACK_GR8(pix - 1))
|
|
visit_gr8(i - 1, j, x - 1, y, pix - 1);
|
|
if (NOT_DONE(i, j + 1) && IS_BLACK_GR8(pix + Pix_ystep))
|
|
visit_gr8(i, j + 1, x, y + 1, pix + Pix_ystep);
|
|
if (NOT_DONE(i, j - 1) && IS_BLACK_GR8(pix - Pix_ystep))
|
|
visit_gr8(i, j - 1, x, y - 1, pix - Pix_ystep);
|
|
|
|
Level--;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static void visit_rgb(int i, int j, int x, int y, TPixel32 *pix) {
|
|
int weight, ret, dummy;
|
|
|
|
start:
|
|
weight = BLACK_WEIGHT_RGB(pix);
|
|
ADDBIG(Xsum, x * weight);
|
|
ADDBIG(Ysum, y * weight);
|
|
ADDBIG(Weightsum, weight);
|
|
if (IS_VERY_BLACK_RGB(pix)) Very_black_found = TRUE;
|
|
if (x < Xmin) Xmin = x;
|
|
if (x > Xmax) Xmax = x;
|
|
if (y < Ymin) Ymin = y;
|
|
if (y > Ymax) Ymax = y;
|
|
Npix++;
|
|
SET_DONE(i, j);
|
|
|
|
if (NOT_DONE(i + 1, j) && IS_BLACK_RGB(pix + 1)) {
|
|
PUSH_ONTO_STACK(1, x, y, i, j, 0, pix)
|
|
i++;
|
|
x++;
|
|
pix += 1;
|
|
goto start;
|
|
return_1:;
|
|
}
|
|
if (NOT_DONE(i - 1, j) && IS_BLACK_RGB(pix - 1)) {
|
|
PUSH_ONTO_STACK(2, x, y, i, j, 0, pix)
|
|
i--;
|
|
x--;
|
|
pix -= 1;
|
|
goto start;
|
|
return_2:;
|
|
}
|
|
if (NOT_DONE(i, j + 1) && IS_BLACK_RGB(pix + Pix_ystep)) {
|
|
PUSH_ONTO_STACK(3, x, y, i, j, 0, pix)
|
|
j++;
|
|
y++;
|
|
pix += Pix_ystep;
|
|
goto start;
|
|
return_3:;
|
|
}
|
|
if (NOT_DONE(i, j - 1) && IS_BLACK_RGB(pix - Pix_ystep)) {
|
|
PUSH_ONTO_STACK(4, x, y, i, j, 0, pix)
|
|
j--;
|
|
y--;
|
|
pix -= Pix_ystep;
|
|
goto start;
|
|
return_4:;
|
|
}
|
|
if (!STACK_IS_EMPTY) {
|
|
POP_FROM_STACK_TPIXEL32(ret, x, y, i, j, dummy, pix);
|
|
switch (ret) {
|
|
case 1:
|
|
goto return_1;
|
|
case 2:
|
|
goto return_2;
|
|
case 3:
|
|
goto return_3;
|
|
case 4:
|
|
goto return_4;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
#ifdef RECURSIVE_VERSION
|
|
|
|
static void visit_rgb(int i, int j, int x, int y, TPixel32 *pix) {
|
|
int weight;
|
|
|
|
if (Level >= Max_level)
|
|
return;
|
|
else
|
|
Level++;
|
|
|
|
weight = BLACK_WEIGHT_RGB(pix);
|
|
ADDBIG(Xsum, x * weight);
|
|
ADDBIG(Ysum, y * weight);
|
|
ADDBIG(Weightsum, weight);
|
|
if (IS_VERY_BLACK_RGB(pix)) Very_black_found = TRUE;
|
|
if (x < Xmin) Xmin = x;
|
|
if (x > Xmax) Xmax = x;
|
|
if (y < Ymin) Ymin = y;
|
|
if (y > Ymax) Ymax = y;
|
|
Npix++;
|
|
SET_DONE(i, j);
|
|
|
|
if (NOT_DONE(i + 1, j) && IS_BLACK_RGB(pix + 1))
|
|
visit_rgb(i + 1, j, x + 1, y, pix + 1);
|
|
if (NOT_DONE(i - 1, j) && IS_BLACK_RGB(pix - 1))
|
|
visit_rgb(i - 1, j, x - 1, y, pix - 1);
|
|
if (NOT_DONE(i, j + 1) && IS_BLACK_RGB(pix + Pix_ystep))
|
|
visit_rgb(i, j + 1, x, y + 1, pix + Pix_ystep);
|
|
if (NOT_DONE(i, j - 1) && IS_BLACK_RGB(pix - Pix_ystep))
|
|
visit_rgb(i, j - 1, x, y - 1, pix - Pix_ystep);
|
|
|
|
Level--;
|
|
}
|
|
|
|
#endif
|
|
|
|
#define PERCENT (40.0 / 100.0)
|
|
/*---------------------------------------------------------------------------*/
|
|
/*
|
|
* Attenzione: tutti i controlli e i calcoli vengono fatti in pixel.
|
|
* Quindi bisogna convertire tutti i valori in pixel prima di arrivare a
|
|
* questo livello.
|
|
* Inoltre: la pegs_side si riferisce alle coordinate di raster.
|
|
*/
|
|
int get_image_rotation_and_center(const TRasterP &img, int strip_width,
|
|
PEGS_SIDE pegs_side, double *p_ang,
|
|
double *cx, double *cy, DOT ref[],
|
|
int ref_dot) {
|
|
double angle;
|
|
int found, i;
|
|
float dx, dy;
|
|
DOT _dotarray[MAX_DOT];
|
|
DOT *dotarray = _dotarray;
|
|
int ndot;
|
|
int max_area, min_area;
|
|
|
|
*p_ang = 0.0;
|
|
|
|
if (Debug_flag) {
|
|
for (i = 0; i < ref_dot; i++) {
|
|
printf("Reference dot <%d>\n", i);
|
|
stampa_dot(ref + i);
|
|
}
|
|
}
|
|
|
|
max_area = 0;
|
|
if (ref_dot > 0) {
|
|
min_area = ref[0].area;
|
|
}
|
|
for (i = 0; i < ref_dot; i++) {
|
|
if (ref[i].area > max_area) {
|
|
max_area = ref[i].area;
|
|
}
|
|
if (ref[i].area < min_area) {
|
|
min_area = ref[i].area;
|
|
}
|
|
}
|
|
|
|
ndot = find_dots(img, strip_width, pegs_side, dotarray, MAX_DOT, max_area);
|
|
if (Debug_flag) printf(">>>> %d dots found\n", ndot);
|
|
|
|
i = 0;
|
|
while (i < ndot) // elimino i dots troppo piccoli
|
|
{
|
|
if (dotarray[i].area < min_area * PERCENT) {
|
|
for (int j = i; j < ndot - 1; j++) dotarray[j] = dotarray[j + 1];
|
|
ndot--;
|
|
} else
|
|
i++;
|
|
}
|
|
|
|
/* controllo il pattern delle perforazioni */
|
|
if (ndot <= 1) {
|
|
return FALSE;
|
|
}
|
|
|
|
int indexArray[3] = {0, 1, 2};
|
|
found = compare_dots(dotarray, ndot, ref, ref_dot, indexArray[0],
|
|
indexArray[1], indexArray[2]);
|
|
|
|
if (Debug_flag)
|
|
for (i = 0; i < ndot; i++) {
|
|
printf("**** Dot[%d]\n", i);
|
|
stampa_dot(dotarray + i);
|
|
}
|
|
|
|
if (!found) return FALSE;
|
|
|
|
angle = 0;
|
|
for (i = 0; i < 2; i++) {
|
|
dx = dotarray[indexArray[i + 1]].x - dotarray[indexArray[i]].x;
|
|
dy = dotarray[indexArray[i + 1]].y - dotarray[indexArray[i]].y;
|
|
switch (pegs_side) {
|
|
case PEGS_LEFT:
|
|
case PEGS_RIGHT:
|
|
angle += dy == 0.0 ? M_PI_2 : atan(dx / dy);
|
|
break;
|
|
default:
|
|
angle -= dx == 0.0 ? M_PI_2 : atan(dy / dx);
|
|
break;
|
|
}
|
|
}
|
|
|
|
*p_ang = angle / 2;
|
|
|
|
// Now calculate the center, we have to get the offset of the center for the
|
|
// dot at point indexArray[1]
|
|
// from the reference and then use the angle to calculate the offsets for the
|
|
// center.
|
|
//
|
|
// It is assumed, that the holes are all on one line.
|
|
float pegWidth =
|
|
sqrt((ref[ref_dot - 1].x - ref[0].x) * (ref[ref_dot - 1].x - ref[0].x) +
|
|
(ref[ref_dot - 1].y - ref[0].y) * (ref[ref_dot - 1].y - ref[0].y));
|
|
float refPegOffset = sqrt(
|
|
(ref[indexArray[1]].x - ref[0].x) * (ref[indexArray[1]].x - ref[0].x) +
|
|
(ref[indexArray[1]].y - ref[0].y) * (ref[indexArray[1]].y - ref[0].y));
|
|
*cx = dotarray[indexArray[1]].x +
|
|
cos(*p_ang) * (pegWidth / 2.0f - refPegOffset);
|
|
*cy = dotarray[indexArray[1]].y +
|
|
sin(*p_ang) * (pegWidth / 2.0f - refPegOffset);
|
|
|
|
if (Debug_flag) {
|
|
printf("\nang: %g\ncx : %g\ncy : %g\n\n", *p_ang, *cx, *cy);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
#define MIN_V 100.0
|
|
|
|
static int compare_dots(DOT const dots[], int ndots, DOT reference[],
|
|
int ref_dot, int &i_ok, int &j_ok, int &k_ok) {
|
|
int found;
|
|
int toll;
|
|
float tolld;
|
|
int i, j, k;
|
|
bool *dot_ok = 0;
|
|
float *ref_dis = 0, dx, dy;
|
|
float vmin, v, dist_i_j, dist_i_k, dist_j_k, del1, del2;
|
|
float ref_dis_0_1, ref_dis_1_2;
|
|
|
|
i_ok = 0;
|
|
j_ok = 0;
|
|
k_ok = 0;
|
|
|
|
/* questa funz e' indipendente da posizione e orientamento dei dots */
|
|
|
|
if (ndots < 1 || ref_dot < 1) {
|
|
goto error;
|
|
}
|
|
|
|
/* controllo quanti dots sono realmente buoni per il confronto */
|
|
dot_ok = (bool *)calloc(ndots, sizeof(bool));
|
|
found = 0;
|
|
|
|
for (i = 0; i < ndots; i++) {
|
|
dot_ok[i] = false;
|
|
for (j = 0; j < ref_dot; j++) {
|
|
toll = (int)((float)reference[j].area * PERCENT);
|
|
if (abs(dots[i].area - reference[j].area) < toll) {
|
|
dot_ok[i] = true;
|
|
found++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
goto error;
|
|
}
|
|
|
|
ref_dis = (float *)calloc(ref_dot, sizeof(float));
|
|
|
|
/* calcolo le distanze di riferimento e la tolleranza stessa */
|
|
|
|
tolld = (float)reference[0].lx;
|
|
if (tolld < reference[0].ly) tolld = (float)reference[0].ly;
|
|
for (i = 1; i < ref_dot; i++) {
|
|
dx = reference[0].x - reference[i].x;
|
|
dy = reference[0].y - reference[i].y;
|
|
ref_dis[i - 1] = sqrtf((dx * dx) + (dy * dy));
|
|
if (tolld < reference[i].lx) tolld = (float)reference[i].lx;
|
|
if (tolld < reference[i].ly) tolld = (float)reference[i].ly;
|
|
}
|
|
|
|
// I suspect that the following expects symmetry in the holes layout...
|
|
// Besides, if the layout is not symmetric, the peg is supposed to be rotated
|
|
// OR translated
|
|
// switching from, say, top to bottom? (Daniele)
|
|
|
|
i_ok = -1;
|
|
v = vmin = 10000000.0;
|
|
for (i = 0; i < ndots - 2; i++) {
|
|
if (!dot_ok[i]) continue;
|
|
|
|
for (j = i + 1; j < ndots - 1; j++) {
|
|
if (!dot_ok[j]) continue;
|
|
for (k = j + 1; k < ndots; k++) {
|
|
if (!dot_ok[k]) continue;
|
|
|
|
// Build square discrepancies from the reference relative hole distances
|
|
dx = dots[i].x - dots[j].x;
|
|
dy = dots[i].y - dots[j].y;
|
|
dist_i_j = sqrtf((dx * dx) + (dy * dy));
|
|
dx = dots[i].x - dots[k].x;
|
|
dy = dots[i].y - dots[k].y;
|
|
dist_i_k = sqrtf((dx * dx) + (dy * dy));
|
|
del1 = (dist_i_j - ref_dis[0]);
|
|
del2 = (dist_i_k - ref_dis[1]);
|
|
v = ((del1 * del1) + (del2 * del2));
|
|
|
|
// Furthermore, add discrepancies from the reference hole areas
|
|
v += abs(dots[i].area -
|
|
reference[0].area); // fabs since areas are already squared
|
|
v += abs(dots[j].area - reference[1].area);
|
|
v += abs(dots[k].area - reference[2].area);
|
|
|
|
if (v < vmin) {
|
|
i_ok = i;
|
|
j_ok = j;
|
|
k_ok = k;
|
|
vmin = v;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Debug_flag) {
|
|
printf("Ho trovato v = %f su %f per %d %d %d \n", v, vmin, i_ok, j_ok,
|
|
k_ok);
|
|
printf("---- Dot <%d> ----\n", i_ok);
|
|
stampa_dot(dots + i_ok);
|
|
printf("---- Dot <%d> ----\n", j_ok);
|
|
stampa_dot(dots + j_ok);
|
|
printf("---- Dot <%d> ----\n", k_ok);
|
|
stampa_dot(dots + k_ok);
|
|
}
|
|
|
|
if (i_ok < 0)
|
|
goto error;
|
|
else {
|
|
dx = dots[i_ok].x - dots[j_ok].x;
|
|
dy = dots[i_ok].y - dots[j_ok].y;
|
|
dist_i_j = sqrtf((dx * dx) + (dy * dy));
|
|
|
|
dx = dots[k_ok].x - dots[j_ok].x;
|
|
dy = dots[k_ok].y - dots[j_ok].y;
|
|
dist_j_k = sqrtf((dx * dx) + (dy * dy));
|
|
|
|
ref_dis_0_1 = ref_dis[0];
|
|
|
|
dx = reference[1].x - reference[2].x;
|
|
dy = reference[1].y - reference[2].y;
|
|
ref_dis_1_2 = sqrtf((dx * dx) + (dy * dy));
|
|
|
|
if (fabsf(dist_i_j - ref_dis_0_1) >= tolld ||
|
|
fabsf(dist_j_k - ref_dis_1_2) >= tolld) {
|
|
i_ok = 0;
|
|
j_ok = 1;
|
|
k_ok = 2;
|
|
}
|
|
}
|
|
|
|
if (ref_dis) free(ref_dis);
|
|
if (dot_ok) free(dot_ok);
|
|
|
|
return TRUE;
|
|
|
|
error:
|
|
if (ref_dis) free(ref_dis);
|
|
if (dot_ok) free(dot_ok);
|
|
return FALSE;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static void stampa_dot(DOT const *dot) {
|
|
printf("Dimensioni: %d,\t%d\n", dot->lx, dot->ly);
|
|
printf("Start : %d,\t%d\n", dot->x1, dot->y1);
|
|
printf("End : %d,\t%d\n", dot->x2, dot->y2);
|
|
printf("Baricentro: %5.3f,\t%5.3f\n", dot->x, dot->y);
|
|
printf("Area : %d\n", dot->area);
|
|
}
|