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 int[,] binarize_image_V2(ref int bw, ref int bh, int[,] pdata, int pw, int ph, int[,] direction_map, int mw, int mh, int blocksize, ROTGRIDS dirbingrids) { int bx, by, mapval; bw = pw - (dirbingrids.pad << 1); bh = ph - (dirbingrids.pad << 1); int[,] bdata = new int[bw, bh]; int bptrX = 0, bptrY = 0; //bdata int sptrX = dirbingrids.pad, sptrY = dirbingrids.pad; // pdata int pptrX = 0, pptrY = 0; //pdata for (int iy = 0; iy < bh; iy++) { /* Set pixel pointer to start of next row in grid. */ pptrX = sptrX; pptrY = sptrY; for (int ix = 0; ix < bw; ix++) { /* Compute which block the current pixel is in. */ bx = ix / blocksize; by = iy / blocksize; /* Get corresponding value in Direction Map. */ mapval = direction_map[bx, by]; /* If current block has has INVALID direction ... */ if (mapval == INVALID_DIR) { /* Set binary pixel to white (255). */ bdata[bptrX, bptrY] = WHITE_PIXEL; } /* Otherwise, if block has a valid direction ... */ else { /*if(mapval >= 0)*/ /* Use directional binarization based on block's direction. */ bdata[bptrX, bptrY] = dirbinarize(pdata, pptrX, pptrY, mapval, dirbingrids); } /* Bump input and output pixel pointers. */ // IncrementPointer(ref pptrX, ref pptrY, pdata.GetLength(0), pdata.GetLength(1), 1); pptrX++; IncrementPointer(ref bptrX, ref bptrY, bdata.GetLength(0), bdata.GetLength(1), 1); } /* Bump pointer to the next row in padded input image. */ sptrY++; } return bdata; }
internal static int[,] binarize_image_V2(ref int bw, ref int bh, int[,] pdata, int pw, int ph, int[,] direction_map, int mw, int mh, int blocksize, ROTGRIDS dirbingrids) { int bx, by, mapval; bw = pw - (dirbingrids.pad << 1); bh = ph - (dirbingrids.pad << 1); int[,] bdata = new int[bw, bh]; int bptrX = 0, bptrY = 0; //bdata int sptrX = dirbingrids.pad, sptrY = dirbingrids.pad; // pdata int pptrX = 0, pptrY = 0; //pdata for (int iy = 0; iy < bh; iy++) { /* Set pixel pointer to start of next row in grid. */ pptrX = sptrX; pptrY = sptrY; for (int ix = 0; ix < bw; ix++) { /* Compute which block the current pixel is in. */ bx = ix / blocksize; by = iy / blocksize; /* Get corresponding value in Direction Map. */ mapval = direction_map[bx, by]; /* If current block has has INVALID direction ... */ if (mapval == INVALID_DIR) { /* Set binary pixel to white (255). */ bdata[bptrX, bptrY] = WHITE_PIXEL; } /* Otherwise, if block has a valid direction ... */ else { /*if(mapval >= 0)*/ /* Use directional binarization based on block's direction. */ bdata[bptrX, bptrY] = dirbinarize(pdata, pptrX, pptrY, mapval, dirbingrids); } /* Bump input and output pixel pointers. */ // IncrementPointer(ref pptrX, ref pptrY, pdata.GetLength(0), pdata.GetLength(1), 1); pptrX++; IncrementPointer(ref bptrX, ref bptrY, bdata.GetLength(0), bdata.GetLength(1), 1); } /* Bump pointer to the next row in padded input image. */ sptrY++; } return(bdata); }
internal static int dirbinarize(int[,] pdata, int pptrX, int pptrY, int idir, ROTGRIDS dirbingrids) { int gx, gy; int rsum, csum = 0; /* Assign nickname pointer. */ List<int> grid = dirbingrids.grids[idir]; /* Calculate center (0-oriented) row in grid. */ double dcy = (dirbingrids.grid_h - 1) / (double)2.0; /* Need to truncate precision so that answers are consistent */ /* on different computer architectures when rounding doubles. */ dcy = trunc_dbl_precision(dcy, TRUNC_SCALE); int cy = Sround(dcy); /* Initialize grid's pixel offset index to zero. */ int gi = 0; /* Initialize grid's pixel accumulator to zero */ int gsum = 0; /* Foreach row in grid ... */ for (gy = 0; gy < dirbingrids.grid_h; gy++) { /* Initialize row pixel sum to zero. */ rsum = 0; /* Foreach column in grid ... */ for (gx = 0; gx < dirbingrids.grid_w; gx++) { /* Accumulate next pixel along rotated row in grid. */ //rsum += *(pptr+grid[gi]); rsum += GetValueOfArray(pdata, pptrX, pptrY, grid[gi]); /* Bump grid's pixel offset index. */ gi++; } /* Accumulate row sum into grid pixel sum. */ gsum += rsum; /* If current row is center row, then save row sum separately. */ if (gy == cy) { csum = rsum; } } /* If the center row sum treated as an average is less than the */ /* total pixel sum in the rotated grid ... */ return csum * dirbingrids.grid_h < gsum ? BLACK_PIXEL : WHITE_PIXEL; }
internal static int[,] binarize_V2(int[,] pdata, int pw, int ph, int[,] direction_map, int mw, int mh, ROTGRIDS dirbingrids) { int[,] bdata = new int[pdata.GetLength(0), pdata.GetLength(1)]; int bw = 0, bh = 0; /* 1. Binarize the padded input image using directional block info. */ bdata = binarize_image_V2(ref bw, ref bh, pdata, pw, ph, direction_map, mw, mh, lfsparms.blocksize, dirbingrids); /* 2. Fill black and white holes in binary image. */ /* LFS scans the binary image, filling holes, 3 times. */ for (int i = 0; i < lfsparms.num_fill_holes; i++) { fill_holes(ref bdata, bw, bh); } return bdata; }
/// <summary> /// Проводит DFT анализ для блока. /// Блок отбирается по ряду направлений и по нескольким видам сигнала (волн) /// различной частоты, применённой для каждого направления. /// Для каждого направления пиксели суммируются по строкам и получается /// вектор сумм. Каждый DFT вид сигнала затем применяется индивидуально этому вектору. /// Значение мощности DFT вычисляется для каждого типа сигнала. /// (frequency) на каждом направлении блока. /// Более того итоговый результат - вектор N сигналов * M направлений /// Используется для определения доминирующего направления. /// </summary> /// <param name="powers"></param> /// <param name="pdata"></param> /// <param name="blkoffset"></param> /// <param name="blkoffsetX"></param> /// <param name="blkoffsetY"></param> /// <param name="pw"></param> /// <param name="ph"></param> /// <param name="dftwaves"></param> /// <param name="dftgrids"></param> internal static void dft_dir_powers(ref double[,] powers, ref int[,] pdata, int blkoffset, int blkoffsetX, int blkoffsetY, int pw, int ph, DFTWAVES dftwaves, ROTGRIDS dftgrids) { if (dftgrids.grid_w != dftgrids.grid_h) { throw new Exception("ERROR : dft_dir_powers : DFT grids must be square\n"); } int[] rowsums = new int[dftgrids.grid_w]; // для кажого направления for (int dir = 0; dir < dftgrids.ngrids; dir++) { sum_rot_block_rows(ref rowsums, pdata, blkoffsetX, blkoffsetY, dftgrids.grids[dir], dftgrids.grid_w); // для каждого варианта сигнала\волны for (int w = 0; w < dftwaves.nwaves; w++) { powers[w, dir] = dft_power(rowsums, dftwaves.waves[w], dftwaves.wavelen); } } }
/// <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"); }
/// <summary> /// Создаёт начальную карту направлений (Direction Map) /// Важно, чтобы были те самые padding - чтобы не вылезти за границы /// Странные направления сгладятся и уйдут после основанного на DFT анализа /// Валидные значения >=0, невалидные -1 /// Также возвращаются две карты: /// карта контрастов (Low Contrast Map) /// - помечает блоки с низким контрастом (у них невалидное значение - направление) /// карта потоков(Low Flow Map) /// - помечает блоки, в которых DFT анализ не нашёл направления (также невалидное направление) /// </summary> /// <param name="odmap">карта направлений</param> /// <param name="olcmap">карта контрастов</param> /// <param name="olfmap">карта потоков</param> /// <param name="blkoffs">смещения блоков</param> /// <param name="mw">количество блоков по горизонтали в padded изображении</param> /// <param name="mh">количество блоков по вертикали в padded изображении</param> /// <param name="pdata">padded входное изображение</param> /// <param name="pw">ширина padded изображения</param> /// <param name="ph">высота padded изображения</param> /// <param name="dftwaves">структура для DFT сигналов (wave forms)</param> /// <param name="dftgrids">структура смещений для повёрнутых пикселей окна\грида (rotated pixel grid offsets)</param> internal static void gen_initial_maps(ref int[,] odmap, ref int[,] olcmap, ref int[,] olfmap, int[,] blkoffs, int mw, int mh, ref int[,] pdata, int pw, int ph, DFTWAVES dftwaves, ROTGRIDS dftgrids) { int nstats = dftwaves.nwaves - 1; int[,] direction_map = new int[mw, mh]; int[,] low_contrast_map = new int[mw, mh]; int[,] low_flow_map = new int[mw, mh]; double[,] powers = new double[dftwaves.nwaves, dftgrids.ngrids]; int[] wis = new int[nstats]; double[] powmaxs = new double[nstats]; int[] powmax_dirs = new int[nstats]; double[] pownorms = new double[nstats]; int xminlimit = dftgrids.pad; int xmaxlimit = pw - dftgrids.pad - lfsparms.windowsize - 1; int yminlimit = dftgrids.pad; int ymaxlimit = ph - dftgrids.pad - lfsparms.windowsize - 1; int win_x, win_y, low_contrast_offset; int blkdir; int dft_offset; for (int jndex = 0; jndex < mh; jndex++) { for (int index = 0; index < mw; index++) { // инициализируем карты // direction_map -1 // low_contrast_map 0 // low_flow_map 0 direction_map[index, jndex] = INVALID_DIR; low_contrast_map[index, jndex] = 0; low_flow_map[index, jndex] = 0; } } for (int jndex = 0; jndex < mh; jndex++) { for (int index = 0; index < mw; index++) { // dft_offset = blkoffs[bi] - (lfsparms->windowoffset * pw) - lfsparms->windowoffset; dft_offset = blkoffs[index, jndex] - (lfsparms.windowoffset * pw) - lfsparms.windowoffset; win_x = dft_offset % pw; win_y = (int)(dft_offset / pw); // проверка на попадание в padding (т.е. рамку) win_x = Max(xminlimit, win_x); win_x = Min(xmaxlimit, win_x); win_y = Max(yminlimit, win_y); win_y = Min(ymaxlimit, win_y); low_contrast_offset = (win_y * pw) + win_x; // просто передаём координаты if (low_contrast_block(low_contrast_offset, win_x, win_y, lfsparms.windowsize, pdata, pw, ph)) { /* block is low contrast ... */ low_contrast_map[index, jndex] = TRUE; continue; } // вычислить dft веса // blkoffs[bi] ? low_contrast_offset, win_x, win_y dft_dir_powers(ref powers, ref pdata, low_contrast_offset, win_x, win_y, pw, ph, dftwaves, dftgrids); // вычислить статистики для dft весов (?) dft_power_stats(ref wis, ref powmaxs, ref powmax_dirs, ref pownorms, powers, 1, dftwaves.nwaves, dftgrids.ngrids); // Проведение _первичного_ теста для определения направления blkdir = primary_dir_test(ref powers, wis, powmaxs, powmax_dirs, pownorms, nstats); if (blkdir != INVALID_DIR) { direction_map[index, jndex] = blkdir; continue; } // Проведение _вторичного_ теста для определения направления (вилка) blkdir = secondary_fork_test(ref powers, wis, powmaxs, powmax_dirs, pownorms, nstats); if (blkdir != INVALID_DIR) { direction_map[index, jndex] = blkdir; continue; } low_flow_map[index, jndex] = TRUE; } } odmap = direction_map; olcmap = low_contrast_map; olfmap = low_flow_map; }
internal static int dirbinarize(int[,] pdata, int pptrX, int pptrY, int idir, ROTGRIDS dirbingrids) { int gx, gy; int rsum, csum = 0; /* Assign nickname pointer. */ List <int> grid = dirbingrids.grids[idir]; /* Calculate center (0-oriented) row in grid. */ double dcy = (dirbingrids.grid_h - 1) / (double)2.0; /* Need to truncate precision so that answers are consistent */ /* on different computer architectures when rounding doubles. */ dcy = trunc_dbl_precision(dcy, TRUNC_SCALE); int cy = Sround(dcy); /* Initialize grid's pixel offset index to zero. */ int gi = 0; /* Initialize grid's pixel accumulator to zero */ int gsum = 0; /* Foreach row in grid ... */ for (gy = 0; gy < dirbingrids.grid_h; gy++) { /* Initialize row pixel sum to zero. */ rsum = 0; /* Foreach column in grid ... */ for (gx = 0; gx < dirbingrids.grid_w; gx++) { /* Accumulate next pixel along rotated row in grid. */ //rsum += *(pptr+grid[gi]); rsum += GetValueOfArray(pdata, pptrX, pptrY, grid[gi]); /* Bump grid's pixel offset index. */ gi++; } /* Accumulate row sum into grid pixel sum. */ gsum += rsum; /* If current row is center row, then save row sum separately. */ if (gy == cy) { csum = rsum; } } /* If the center row sum treated as an average is less than the */ /* total pixel sum in the rotated grid ... */ return(csum * dirbingrids.grid_h < gsum ? BLACK_PIXEL : WHITE_PIXEL); }
internal static int[,] binarize_V2(int[,] pdata, int pw, int ph, int[,] direction_map, int mw, int mh, ROTGRIDS dirbingrids) { int[,] bdata = new int[pdata.GetLength(0), pdata.GetLength(1)]; int bw = 0, bh = 0; /* 1. Binarize the padded input image using directional block info. */ bdata = binarize_image_V2(ref bw, ref bh, pdata, pw, ph, direction_map, mw, mh, lfsparms.blocksize, dirbingrids); /* 2. Fill black and white holes in binary image. */ /* LFS scans the binary image, filling holes, 3 times. */ for (int i = 0; i < lfsparms.num_fill_holes; i++) { fill_holes(ref bdata, bw, bh); } return(bdata); }