internal static int lfs_detect_minutiae_V2(ref int[,] odmap, ref int[,] olcmap, ref int[,] olfmap, ref int[,] ohcmap, ref int[,] obdata, Bitmap idata, int iw, int ih, ref int mw, ref int mh, string name) { int[,] pdata = new int[iw, ih]; int[,] bdata = new int[iw, ih]; DIR2RAD dir2rad = new DIR2RAD(); DFTWAVES dftwaves = new DFTWAVES(); ROTGRIDS dftgrids = new ROTGRIDS(); ROTGRIDS dirbingrids = new ROTGRIDS(); int[,] direction_map = new int[iw, ih]; int[,] low_contrast_map = new int[iw, ih]; int[,] low_flow_map = new int[iw, ih]; int[,] high_curve_map = new int[iw, ih]; int maxpad = 0; int pw = 0, ph = 0; // initialization maxpad = get_max_padding_V2(lfsparms.windowsize, lfsparms.windowoffset, lfsparms.dirbin_grid_w, lfsparms.dirbin_grid_h); dir2rad = init_dir2rad(lfsparms.num_directions); dftwaves = init_dftwaves(dft_coefs, lfsparms.num_dft_waves, lfsparms.windowsize); dftgrids = init_rotgrids(iw, ih, maxpad, lfsparms.start_dir_angle, lfsparms.num_directions, lfsparms.windowsize, lfsparms.windowsize, RELATIVE2ORIGIN); if (maxpad > 0) { // печать pdata = pad_uchar_image(ref pw, ref ph, idata, iw, ih, maxpad, lfsparms.pad_value); // PrintMap(pdata, "pdata"); } else { for (int i = 0; i < iw; i++) { for (int j = 0; j < ih; j++) { pdata[i, j] = idata.GetPixel(i, j).R; } } pw = iw; ph = ih; } gen_image_maps(ref direction_map, ref low_contrast_map, ref low_flow_map, ref high_curve_map, ref mw, ref mh, pdata, pw, ph, dir2rad, dftwaves, dftgrids, name); /* Assign results to output pointers. */ odmap = direction_map; olcmap = low_contrast_map; olfmap = low_flow_map; ohcmap = high_curve_map; obdata = bdata; return(0); }
internal static int lfs_detect_minutiae_V2(ref int[,] odmap, ref int[,] olcmap, ref int[,] olfmap, ref int[,] ohcmap, ref int[,] obdata, Bitmap idata, int iw, int ih, ref int mw, ref int mh, string name) { int[,] pdata = new int[iw, ih]; int[,] bdata = new int[iw, ih]; DIR2RAD dir2rad = new DIR2RAD(); DFTWAVES dftwaves = new DFTWAVES(); ROTGRIDS dftgrids = new ROTGRIDS(); ROTGRIDS dirbingrids = new ROTGRIDS(); int[,] direction_map = new int[iw, ih]; int[,] low_contrast_map = new int[iw, ih]; int[,] low_flow_map = new int[iw, ih]; int[,] high_curve_map = new int[iw, ih]; int maxpad = 0; int pw = 0, ph = 0; // initialization maxpad = get_max_padding_V2(lfsparms.windowsize, lfsparms.windowoffset, lfsparms.dirbin_grid_w, lfsparms.dirbin_grid_h); dir2rad = init_dir2rad(lfsparms.num_directions); dftwaves = init_dftwaves(dft_coefs, lfsparms.num_dft_waves, lfsparms.windowsize); dftgrids = init_rotgrids(iw, ih, maxpad, lfsparms.start_dir_angle, lfsparms.num_directions, lfsparms.windowsize, lfsparms.windowsize, RELATIVE2ORIGIN); if (maxpad > 0) { // печать pdata = pad_uchar_image(ref pw, ref ph, idata, iw, ih, maxpad, lfsparms.pad_value); // PrintMap(pdata, "pdata"); } else { for (int i = 0; i < iw; i++) { for (int j = 0; j < ih; j++) { pdata[i, j] = idata.GetPixel(i, j).R; } } pw = iw; ph = ih; } gen_image_maps(ref direction_map, ref low_contrast_map, ref low_flow_map, ref high_curve_map, ref mw, ref mh, pdata, pw, ph, dir2rad, dftwaves, dftgrids, name); /* Assign results to output pointers. */ odmap = direction_map; olcmap = low_contrast_map; olfmap = low_flow_map; ohcmap = high_curve_map; obdata = bdata; return (0); }
internal static DIR2RAD init_dir2rad(int ndirs) { DIR2RAD dir2rad = new DIR2RAD(); double theta, pi_factor; double cs, sn; dir2rad.ndirs = ndirs; pi_factor = 2.0 * M_PI / (double)ndirs; dir2rad.cos = new List <double>(); dir2rad.sin = new List <double>(); for (int i = 0; i < ndirs; ++i) { theta = (double)(i * pi_factor); cs = Math.Cos(theta); sn = Math.Sin(theta); cs = trunc_dbl_precision(cs, TRUNC_SCALE); sn = trunc_dbl_precision(sn, TRUNC_SCALE); dir2rad.cos.Add(cs); dir2rad.sin.Add(sn); } return(dir2rad); }
internal static DIR2RAD init_dir2rad(int ndirs) { DIR2RAD dir2rad = new DIR2RAD(); double theta, pi_factor; double cs, sn; dir2rad.ndirs = ndirs; pi_factor = 2.0 * M_PI / (double)ndirs; dir2rad.cos = new List<double>(); dir2rad.sin = new List<double>(); for (int i = 0; i < ndirs; ++i) { theta = (double)(i * pi_factor); cs = Math.Cos(theta); sn = Math.Sin(theta); cs = trunc_dbl_precision(cs, TRUNC_SCALE); sn = trunc_dbl_precision(sn, TRUNC_SCALE); dir2rad.cos.Add(cs); dir2rad.sin.Add(sn); } return dir2rad; }
internal static void smooth_direction_map(ref int[,] direction_map, ref int[,] low_contrast_map, int mw, int mh, DIR2RAD dir2rad) { int avrdir = 0, nvalid = 0; double dir_strength = 0; /* Foreach block in maps ... */ for (int my = 0; my < mh; my++) { for (int mx = 0; mx < mw; mx++) { /* If the current block does NOT have LOW CONTRAST ... */ if (low_contrast_map[mx, my] == 0) { /* Compute average direction from neighbors, returning the */ /* number of valid neighbors used in the computation, and */ /* the "strength" of the average direction. */ average_8nbr_dir(ref avrdir, ref dir_strength, ref nvalid, direction_map, mx, my, mw, mh, dir2rad); /* If average direction strength is strong enough */ /* (Ex. thresh==0.2)... */ if (dir_strength >= lfsparms.dir_strength_min) { /* If Direction Map direction is valid ... */ if (direction_map[mx, my] != INVALID_DIR) { /* Conduct valid neighbor test (Ex. thresh==3)... */ if (nvalid >= lfsparms.rmv_valid_nbr_min) { /* Reassign valid direction with average direction. */ direction_map[mx, my] = avrdir; } } /* Otherwise direction is invalid ... */ else { /* Even if DIRECTION_MAP value is invalid, if number of */ /* valid neighbors is big enough (Ex. thresh==7)... */ if (nvalid >= lfsparms.smth_valid_nbr_min) { /* Assign invalid direction with average direction. */ direction_map[mx, my] = avrdir; } } } } /* Otherwise, block has LOW CONTRAST, so keep INVALID direction. */ } } }
internal static void average_8nbr_dir(ref int avrdir, ref double dir_strength, ref int nvalid, int[,] imap, int mx, int my, int mw, int mh, DIR2RAD dir2rad) { double pi2, pi_factor, theta; double avr; /* Compute neighbor coordinates to current IMAP direction */ int e = mx + 1; /* East */ int w = mx - 1; /* West */ int n = my - 1; /* North */ int s = my + 1; /* South */ /* Intialize accumulators */ nvalid = 0; double cospart = 0.0; double sinpart = 0.0; /* 1. Test NW */ /* If NW point within IMAP boudaries ... */ if ((w >= 0) && (n >= 0) && (imap[w, n] != INVALID_DIR)) { /* Accumulate cosine and sine components of the direction */ cospart += dir2rad.cos[imap[w, n]]; sinpart += dir2rad.sin[imap[w, n]]; /* Bump number of accumulated directions */ nvalid++; } /* 2. Test N */ /* If N point within IMAP boudaries ... */ if ((n >= 0) && (imap[mx, n] != INVALID_DIR)) { /* Accumulate cosine and sine components of the direction */ cospart += dir2rad.cos[imap[mx, n]]; sinpart += dir2rad.sin[imap[mx, n]]; /* Bump number of accumulated directions */ nvalid++; } /* 3. Test NE */ /* If NE point within IMAP boudaries ... */ if ((e < mw) && (n >= 0) && (imap[e, n] != INVALID_DIR)) { /* Accumulate cosine and sine components of the direction */ cospart += dir2rad.cos[imap[e, n]]; sinpart += dir2rad.sin[imap[e, n]]; /* Bump number of accumulated directions */ nvalid++; } /* 4. Test E */ /* If E point within IMAP boudaries ... */ if ((e < mw) && (imap[e, my] != INVALID_DIR)) { /* Accumulate cosine and sine components of the direction */ cospart += dir2rad.cos[imap[e, my]]; sinpart += dir2rad.sin[imap[e, my]]; /* Bump number of accumulated directions */ nvalid++; } /* 5. Test SE */ /* If SE point within IMAP boudaries ... */ if ((e < mw) && (s < mh) && (imap[e, s] != INVALID_DIR)) { /* Accumulate cosine and sine components of the direction */ cospart += dir2rad.cos[imap[e, s]]; sinpart += dir2rad.sin[imap[e, s]]; /* Bump number of accumulated directions */ nvalid++; } /* 6. Test S */ /* If S point within IMAP boudaries ... */ if (s < mh && imap[mx, s] != INVALID_DIR) { /* Accumulate cosine and sine components of the direction */ cospart += dir2rad.cos[imap[mx, s]]; sinpart += dir2rad.sin[imap[mx, s]]; /* Bump number of accumulated directions */ nvalid++; } /* 7. Test SW */ /* If SW point within IMAP boudaries ... */ if ((w >= 0) && (s < mh) && imap[w, s] != INVALID_DIR) { /* Accumulate cosine and sine components of the direction */ cospart += dir2rad.cos[imap[w, s]]; sinpart += dir2rad.sin[imap[w, s]]; /* Bump number of accumulated directions */ nvalid++; } /* 8. Test W */ /* If W point within IMAP boudaries ... */ if ((w >= 0) && (imap[w, my] != INVALID_DIR)) { /* Accumulate cosine and sine components of the direction */ cospart += dir2rad.cos[imap[w, my]]; sinpart += dir2rad.sin[imap[w, my]]; /* Bump number of accumulated directions */ nvalid++; } /* If there were no neighbors found with valid direction ... */ if (nvalid == 0) { /* Return INVALID direction. */ dir_strength = 0; avrdir = INVALID_DIR; return; } /* Compute averages of accumulated cosine and sine direction components */ cospart /= (double)nvalid; sinpart /= (double)nvalid; /* Compute directional strength as hypotenuse (without sqrt) of average */ /* cosine and sine direction components. Believe this value will be on */ /* the range of [0 .. 1]. */ dir_strength = (cospart * cospart) + (sinpart * sinpart); /* Need to truncate precision so that answers are consistent */ /* on different computer architectures when comparing doubles. */ dir_strength = trunc_dbl_precision(dir_strength, TRUNC_SCALE); /* If the direction strength is not sufficiently high ... */ if (dir_strength < DIR_STRENGTH_MIN) { /* Return INVALID direction. */ dir_strength = 0; avrdir = INVALID_DIR; return; } /* Compute angle (in radians) from Arctan of avarage */ /* cosine and sine direction components. I think this order */ /* is necessary because 0 direction is vertical and positive */ /* direction is clockwise. */ theta = Math.Atan2(sinpart, cospart); /* Atan2 returns theta on range [-PI..PI]. Adjust theta so that */ /* it is on the range [0..2PI]. */ pi2 = 2 * M_PI; theta += pi2; theta = theta % pi2; /* Pi_factor sets the period of the trig functions to NDIRS units in x. */ /* For example, if NDIRS==16, then pi_factor = 2(PI/16) = .3926... */ /* Dividing theta (in radians) by this factor ((1/pi_factor)==2.546...) */ /* will produce directions on the range [0..NDIRS]. */ pi_factor = pi2 / (double)dir2rad.ndirs; /* 2(M_PI/ndirs) */ /* Round off the direction and return it as an average direction */ /* for the neighborhood. */ avr = theta / pi_factor; /* Need to truncate precision so that answers are consistent */ /* on different computer architectures when rounding doubles. */ avr = trunc_dbl_precision(avr, TRUNC_SCALE); avrdir = Sround(avr); /* Really do need to map values > NDIRS back onto [0..NDIRS) range. */ avrdir %= dir2rad.ndirs; }
internal static bool remove_dir(ref int[,] imap, int mx, int my, int mw, int mh, DIR2RAD dir2rad) { int avrdir = 0, nvalid = 0; double dir_strength = 0; /* Compute average direction from neighbors, returning the */ /* number of valid neighbors used in the computation, and */ /* the "strength" of the average direction. */ average_8nbr_dir(ref avrdir, ref dir_strength, ref nvalid, imap, mx, my, mw, mh, dir2rad); /* Conduct valid neighbor test (Ex. thresh==3) */ if (nvalid < lfsparms.rmv_valid_nbr_min) { // return 1 return true; } /* If stregnth of average neighbor direction is large enough to */ /* put credence in ... (Ex. thresh==0.2) */ if (dir_strength >= lfsparms.dir_strength_min) { /* Conduct direction distance test (Ex. thresh==3) */ /* Compute minimum absolute distance between current and */ /* average directions accounting for wrapping from 0 to NDIRS. */ int dist = Math.Abs(avrdir - imap[mx, my]); dist = Min(dist, dir2rad.ndirs - dist); if (dist > lfsparms.dir_distance_max) { // return 2 return true; } } // return 0 return false; }
internal static int test_left_edge(int lbox, int tbox, int rbox, int bbox, ref int[,] imap, int mw, int mh, DIR2RAD dir2rad) { /* Initialize number of directions removed on edge to 0 */ int nremoved = 0; /* Set start pointer to bottom-leftmost point of box, or set it to */ /* the bottommost point in IMAP column (lasty=mh-1), whichever */ /* is smaller. */ int sy = Min(bbox, mh - 1); /* Set end pointer to either 1 point short of the top-leftmost */ /* point of box, or set it to the topmost point in the IMAP */ /* column (y=0), whichever is larger. */ int ey = Max(tbox - 1, 0); /* For each point on box's edge ... */ for (int bx = lbox, by = sy; by >= ey && by >= 0; by--) { /* If valid IMAP direction and test for removal is true ... */ if ((imap[bx, by] != INVALID_DIR) && (remove_dir(ref imap, bx, by, mw, mh, dir2rad))) { /* Set to INVALID */ imap[bx, by] = INVALID_DIR; /* Bump number of removed IMAP directions */ nremoved++; } } /* Return the number of directions removed on edge */ return (nremoved); }
internal static int test_bottom_edge(int lbox, int tbox, int rbox, int bbox, ref int[,] imap, int mw, int mh, DIR2RAD dir2rad) { /* Initialize number of directions removed on edge to 0 */ int nremoved = 0; /* Set start pointer to bottom-rightmost point of box, or set it to the */ /* rightmost point in the IMAP ROW (lastx=mw-1), whichever is smaller. */ int sx = Min(rbox, mw - 1); /* Set end pointer to either 1 point short of the bottom- */ /* lefttmost point of box, or set it to the leftmost point */ /* in the IMAP row (x=0), whichever is larger. */ int ex = Max(lbox - 1, 0); /* For each point on box's edge ... */ for (int bx = sx, by = bbox; bx >= 0 && bx >= ex && by >= bbox; bx--) { /* If valid IMAP direction and test for removal is true ... */ if ((imap[bx, by] != INVALID_DIR) && (remove_dir(ref imap, bx, by, mw, mh, dir2rad))) { /* Set to INVALID */ imap[bx, by] = INVALID_DIR; /* Bump number of removed IMAP directions */ nremoved++; } } /* Return the number of directions removed on edge */ return (nremoved); }
internal static int test_top_edge(int lbox, int tbox, int rbox, int bbox, ref int[,] imap, int mw, int mh, DIR2RAD dir2rad) { /* Initialize number of directions removed on edge to 0 */ int nremoved = 0; /* Set start pointer to top-leftmost point of box, or set it to */ /* the leftmost point in the IMAP row (0), whichever is larger. */ int sx = Max(lbox, 0); /* Set end pointer to either 1 point short of the top-rightmost */ /* point of box, or set it to the rightmost point in the IMAP */ /* row (lastx=mw-1), whichever is smaller. */ int ex = Min(rbox - 1, mw - 1); /* For each point on box's edge ... */ for (int bx = sx, by = tbox; bx <= ex && bx < imap.GetLength(0); bx++) { /* If valid IMAP direction and test for removal is true ... */ if ((imap[bx, by] != INVALID_DIR) && (remove_dir(ref imap, bx, by, mw, mh, dir2rad))) { imap[bx, by] = INVALID_DIR; nremoved++; } } /* Return the number of directions removed on edge */ return (nremoved); }
/// <summary> /// Вычисляем карты для изображения (для Version 2 of the NIST LFS System) /// карта направлений (Direction Map) - матрица, целые значения, доминирующее направление /// карта контрастов (Low Contrast Map) - матрица, помечены блоки низкого контраста /// карта потоков (Low Flow Map) - нет выделенного направления /// карта кривизны (High Curve Map) - блоки высокой кривизны /// Изображение: произвольное, не квадратное /// </summary> /// <param name="odmap">карта направлений</param> /// <param name="olcmap">карта контраста</param> /// <param name="olfmap">карта потоков</param> /// <param name="ohcmap">карта кривизны</param> /// <param name="omw"> количество блоков по горизонтали</param> /// <param name="omh">количество блоков по вертикали</param> /// <param name="pdata">padded входное изображение</param> /// <param name="pw">padded ширина</param> /// <param name="ph">padded высота</param> /// <param name="dir2rad">Поисковая таблица преобразования целых направлений</param> /// <param name="dftwaves">структура для DFT сигналов (wave forms)</param> /// <param name="dftgrids">структура смещений для повёрнутых пикселей окна\грида (rotated pixel grid offsets)</param> internal static void gen_image_maps(ref int[,] odmap, ref int[,] olcmap, ref int[,] olfmap, ref int[,] ohcmap, ref int omw, ref int omh, int[,] pdata, int pw, int ph, DIR2RAD dir2rad, DFTWAVES dftwaves, ROTGRIDS dftgrids, string name) { int[,] direction_map = new int[omw, omh]; int[,] low_contrast_map = new int[omw, omh]; int[,] low_flow_map = new int[omw, omh]; int mw = 0, mh = 0; if (dftgrids.grid_w != dftgrids.grid_h) { throw new Exception("ERROR : gen_image_maps : DFT grids must be square\n"); } int iw = pw - (dftgrids.pad << 1); int ih = ph - (dftgrids.pad << 1); // int blkoffsCount = ((int)Math.Ceiling(iw / (double)lfsparms.blocksize)) * // (int)Math.Ceiling(ih / (double)lfsparms.blocksize); // blkoffsCount = blkoffs.GetLength(0) * blkoffs.GetLength(1) - проверено // считаем смещения для блоков // предполагая, что у нас +12 пикселей со всех сторон изображения со значениями 128 // чтобы нормально считать в окнах int[,] blkoffs = block_offsets(ref mw, ref mh, iw, ih, dftgrids.pad, lfsparms.blocksize); // PrintMap(blkoffs, "blkoffs"); gen_initial_maps(ref direction_map, ref low_contrast_map, ref low_flow_map, blkoffs, mw, mh, ref pdata, pw, ph, dftwaves, dftgrids); // печать // PrintMap(direction_map, "direction_map"); // PrintMap(low_contrast_map, "low_contrast_map"); // PrintMap(low_flow_map, "low_flow_map"); // direction_map не меняется, также low_flow = 0 // но low_contrast = 1 morph_TF_map(ref low_flow_map, mw, mh); remove_incon_dirs(ref direction_map, mw, mh, dir2rad); smooth_direction_map(ref direction_map, ref low_contrast_map, mw, mh, dir2rad); interpolate_direction_map(ref direction_map, ref low_contrast_map, mw, mh); remove_incon_dirs(ref direction_map, mw, mh, dir2rad); smooth_direction_map(ref direction_map, ref low_contrast_map, mw, mh, dir2rad); set_margin_blocks(ref direction_map, mw, mh, INVALID_DIR); int[,] high_curve_map = gen_high_curve_map(ref direction_map, mw, mh); odmap = direction_map; olcmap = low_contrast_map; olfmap = low_flow_map; ohcmap = high_curve_map; omw = mw; omh = mh; // печать //PrintMap(direction_map, name + "_direction_map"); //PrintMap(low_contrast_map, name + "_low_contrast_map"); //PrintMap(low_flow_map, name + "_low_flow_map"); //PrintMap(high_curve_map, name + "_high_curve_map"); }
internal static void remove_incon_dirs(ref int[,] imap, int mw, int mh, DIR2RAD dir2rad) { int nremoved; int lbox, rbox, tbox, bbox; /* Compute center coords of IMAP */ int cx = mw >> 1; int cy = mh >> 1; /* Do pass, while directions have been removed in a pass ... */ do { /* Reinitialize number of removed directions to 0 */ nremoved = 0; /* If valid IMAP direction and test for removal is true ... */ if ((imap[cx, cy] != INVALID_DIR) && remove_dir(ref imap, cx, cy, mw, mh, dir2rad)) { /* Set to INVALID */ imap[cx, cy] = INVALID_DIR; /* Bump number of removed IMAP directions */ nremoved++; } /* Initialize side indices of concentric boxes */ lbox = cx - 1; tbox = cy - 1; rbox = cx + 1; bbox = cy + 1; /* Grow concentric boxes, until ALL edges of imap are exceeded */ while ((lbox >= 0) || (rbox < mw) || (tbox >= 0) || (bbox < mh)) { /* test top edge of box */ if (tbox >= 0) nremoved += test_top_edge(lbox, tbox, rbox, bbox, ref imap, mw, mh, dir2rad); /* test right edge of box */ if (rbox < mw) nremoved += test_right_edge(lbox, tbox, rbox, bbox, ref imap, mw, mh, dir2rad); /* test bottom edge of box */ if (bbox < mh) nremoved += test_bottom_edge(lbox, tbox, rbox, bbox, ref imap, mw, mh, dir2rad); /* test left edge of box */ if (lbox >= 0) nremoved += test_left_edge(lbox, tbox, rbox, bbox, ref imap, mw, mh, dir2rad); /* Resize current box */ lbox--; tbox--; rbox++; bbox++; } } while (nremoved != 0); }