/*! * kernelWriteStream() * * Input: stream * kel * Return: 0 if OK, 1 on error */ l_int32 kernelWriteStream(FILE *fp, L_KERNEL *kel) { l_int32 sx, sy, cx, cy, i, j; PROCNAME("kernelWriteStream"); if (!fp) return ERROR_INT("stream not defined", procName, 1); if (!kel) return ERROR_INT("kel not defined", procName, 1); kernelGetParameters(kel, &sy, &sx, &cy, &cx); fprintf(fp, " Kernel Version %d\n", KERNEL_VERSION_NUMBER); fprintf(fp, " sy = %d, sx = %d, cy = %d, cx = %d\n", sy, sx, cy, cx); for (i = 0; i < sy; i++) { for (j = 0; j < sx; j++) fprintf(fp, "%15.4f", kel->data[i][j]); fprintf(fp, "\n"); } fprintf(fp, "\n"); return 0; }
/*! * kernelNormalize() * * Input: kels (source kel, to be normalized) * normsum (desired sum of elements in keld) * Return: keld (normalized version of kels), or null on error * or if sum of elements is very close to 0) * * Notes: * (1) If the sum of kernel elements is close to 0, do not * try to calculate the normalized kernel. Instead, * return a copy of the input kernel, with a warning. */ L_KERNEL * kernelNormalize(L_KERNEL *kels, l_float32 normsum) { l_int32 i, j, sx, sy, cx, cy; l_float32 sum, factor; L_KERNEL *keld; PROCNAME("kernelNormalize"); if (!kels) return (L_KERNEL *)ERROR_PTR("kels not defined", procName, NULL); kernelGetSum(kels, &sum); if (L_ABS(sum) < 0.00001) { L_WARNING("null sum; not normalizing; returning a copy\n", procName); return kernelCopy(kels); } kernelGetParameters(kels, &sy, &sx, &cy, &cx); if ((keld = kernelCreate(sy, sx)) == NULL) return (L_KERNEL *)ERROR_PTR("keld not made", procName, NULL); keld->cy = cy; keld->cx = cx; factor = normsum / sum; for (i = 0; i < sy; i++) for (j = 0; j < sx; j++) keld->data[i][j] = factor * kels->data[i][j]; return keld; }
/*! * pixBilateralGrayExact() * * Input: pixs (8 bpp gray) * spatial_kel (gaussian kernel) * range_kel (<optional> 256 x 1, monotonically decreasing) * Return: pixd (8 bpp bilateral filtered image) * * Notes: * (1) See pixBilateralExact(). */ PIX * pixBilateralGrayExact(PIX *pixs, L_KERNEL *spatial_kel, L_KERNEL *range_kel) { l_int32 i, j, id, jd, k, m, w, h, d, sx, sy, cx, cy, wplt, wpld; l_int32 val, center_val; l_uint32 *datat, *datad, *linet, *lined; l_float32 sum, weight_sum, weight; L_KERNEL *keli; PIX *pixt, *pixd; PROCNAME("pixBilateralGrayExact"); if (!pixs) return (PIX *) ERROR_PTR("pixs not defined", procName, NULL); if (pixGetDepth(pixs) != 8) return (PIX *) ERROR_PTR("pixs must be gray", procName, NULL); pixGetDimensions(pixs, &w, &h, &d); if (!spatial_kel) return (PIX *) ERROR_PTR("spatial kel not defined", procName, NULL); if (!range_kel) return pixConvolve(pixs, spatial_kel, 8, 1); if (range_kel->sx != 256 || range_kel->sy != 1) return (PIX *) ERROR_PTR("range kel not {256 x 1", procName, NULL); keli = kernelInvert(spatial_kel); kernelGetParameters(keli, &sy, &sx, &cy, &cx); if ((pixt = pixAddMirroredBorder(pixs, cx, sx - cx, cy, sy - cy)) == NULL) return (PIX *) ERROR_PTR("pixt not made", procName, NULL); pixd = pixCreate(w, h, 8); datat = pixGetData(pixt); datad = pixGetData(pixd); wplt = pixGetWpl(pixt); wpld = pixGetWpl(pixd); for (i = 0, id = 0; id < h; i++, id++) { lined = datad + id * wpld; for (j = 0, jd = 0; jd < w; j++, jd++) { center_val = GET_DATA_BYTE(datat + (i + cy) * wplt, j + cx); weight_sum = 0.0; sum = 0.0; for (k = 0; k < sy; k++) { linet = datat + (i + k) * wplt; for (m = 0; m < sx; m++) { val = GET_DATA_BYTE(linet, j + m); weight = keli->data[k][m] * range_kel->data[0][L_ABS(center_val - val)]; weight_sum += weight; sum += val * weight; } } SET_DATA_BYTE(lined, jd, (l_int32)(sum / weight_sum + 0.5)); } } kernelDestroy(&keli); pixDestroy(&pixt); return pixd; }
/*! * kernelGetSum() * * Input: kernel * &sum (<return> sum of all kernel values) * Return: 0 if OK, 1 on error */ l_int32 kernelGetSum(L_KERNEL *kel, l_float32 *psum) { l_int32 sx, sy, i, j; PROCNAME("kernelGetSum"); if (!psum) return ERROR_INT("&sum not defined", procName, 1); *psum = 0.0; if (!kel) return ERROR_INT("kernel not defined", procName, 1); kernelGetParameters(kel, &sy, &sx, NULL, NULL); for (i = 0; i < sy; i++) { for (j = 0; j < sx; j++) { *psum += kel->data[i][j]; } } return 0; }
/*! * kernelCopy() * * Input: kels (source kernel) * Return: keld (copy of kels), or null on error */ L_KERNEL * kernelCopy(L_KERNEL *kels) { l_int32 i, j, sx, sy, cx, cy; L_KERNEL *keld; PROCNAME("kernelCopy"); if (!kels) return (L_KERNEL *)ERROR_PTR("kels not defined", procName, NULL); kernelGetParameters(kels, &sy, &sx, &cy, &cx); if ((keld = kernelCreate(sy, sx)) == NULL) return (L_KERNEL *)ERROR_PTR("keld not made", procName, NULL); keld->cy = cy; keld->cx = cx; for (i = 0; i < sy; i++) for (j = 0; j < sx; j++) keld->data[i][j] = kels->data[i][j]; return keld; }
/*! * kernelGetMinMax() * * Input: kernel * &min (<optional return> minimum value) * &max (<optional return> maximum value) * Return: 0 if OK, 1 on error */ l_int32 kernelGetMinMax(L_KERNEL *kel, l_float32 *pmin, l_float32 *pmax) { l_int32 sx, sy, i, j; l_float32 val, minval, maxval; PROCNAME("kernelGetMinmax"); if (!pmin && !pmax) return ERROR_INT("neither &min nor &max defined", procName, 1); if (pmin) *pmin = 0.0; if (pmax) *pmax = 0.0; if (!kel) return ERROR_INT("kernel not defined", procName, 1); kernelGetParameters(kel, &sy, &sx, NULL, NULL); minval = 10000000.0; maxval = -10000000.0; for (i = 0; i < sy; i++) { for (j = 0; j < sx; j++) { val = kel->data[i][j]; if (val < minval) minval = val; if (val > maxval) maxval = val; } } if (pmin) *pmin = minval; if (pmax) *pmax = maxval; return 0; }
/*! * kernelDisplayInPix() * * Input: kernel * size (of grid interiors; odd; either 1 or a minimum size * of 17 is enforced) * gthick (grid thickness; either 0 or a minimum size of 2 * is enforced) * Return: pix (display of kernel), or null on error * * Notes: * (1) This gives a visual representation of a kernel. * (2) There are two modes of display: * (a) Grid lines of minimum width 2, surrounding regions * representing kernel elements of minimum size 17, * with a "plus" mark at the kernel origin, or * (b) A pix without grid lines and using 1 pixel per kernel element. * (3) For both cases, the kernel absolute value is displayed, * normalized such that the maximum absolute value is 255. * (4) Large 2D separable kernels should be used for convolution * with two 1D kernels. However, for the bilateral filter, * the computation time is independent of the size of the * 2D content kernel. */ PIX * kernelDisplayInPix(L_KERNEL *kel, l_int32 size, l_int32 gthick) { l_int32 i, j, w, h, sx, sy, cx, cy, width, x0, y0; l_int32 normval; l_float32 minval, maxval, max, val, norm; PIX *pixd, *pixt0, *pixt1; PROCNAME("kernelDisplayInPix"); if (!kel) return (PIX *)ERROR_PTR("kernel not defined", procName, NULL); /* Normalize the max value to be 255 for display */ kernelGetParameters(kel, &sy, &sx, &cy, &cx); kernelGetMinMax(kel, &minval, &maxval); max = L_MAX(maxval, -minval); if (max == 0.0) return (PIX *)ERROR_PTR("kernel elements all 0.0", procName, NULL); norm = 255. / (l_float32)max; /* Handle the 1 element/pixel case; typically with large kernels */ if (size == 1 && gthick == 0) { pixd = pixCreate(sx, sy, 8); for (i = 0; i < sy; i++) { for (j = 0; j < sx; j++) { kernelGetElement(kel, i, j, &val); normval = (l_int32)(norm * L_ABS(val)); pixSetPixel(pixd, j, i, normval); } } return pixd; } /* Enforce the constraints for the grid line version */ if (size < 17) { L_WARNING("size < 17; setting to 17\n", procName); size = 17; } if (size % 2 == 0) size++; if (gthick < 2) { L_WARNING("grid thickness < 2; setting to 2\n", procName); gthick = 2; } w = size * sx + gthick * (sx + 1); h = size * sy + gthick * (sy + 1); pixd = pixCreate(w, h, 8); /* Generate grid lines */ for (i = 0; i <= sy; i++) pixRenderLine(pixd, 0, gthick / 2 + i * (size + gthick), w - 1, gthick / 2 + i * (size + gthick), gthick, L_SET_PIXELS); for (j = 0; j <= sx; j++) pixRenderLine(pixd, gthick / 2 + j * (size + gthick), 0, gthick / 2 + j * (size + gthick), h - 1, gthick, L_SET_PIXELS); /* Generate mask for each element */ pixt0 = pixCreate(size, size, 1); pixSetAll(pixt0); /* Generate crossed lines for origin pattern */ pixt1 = pixCreate(size, size, 1); width = size / 8; pixRenderLine(pixt1, size / 2, (l_int32)(0.12 * size), size / 2, (l_int32)(0.88 * size), width, L_SET_PIXELS); pixRenderLine(pixt1, (l_int32)(0.15 * size), size / 2, (l_int32)(0.85 * size), size / 2, width, L_FLIP_PIXELS); pixRasterop(pixt1, size / 2 - width, size / 2 - width, 2 * width, 2 * width, PIX_NOT(PIX_DST), NULL, 0, 0); /* Paste the patterns in */ y0 = gthick; for (i = 0; i < sy; i++) { x0 = gthick; for (j = 0; j < sx; j++) { kernelGetElement(kel, i, j, &val); normval = (l_int32)(norm * L_ABS(val)); pixSetMaskedGeneral(pixd, pixt0, normval, x0, y0); if (i == cy && j == cx) pixPaintThroughMask(pixd, pixt1, x0, y0, 255 - normval); x0 += size + gthick; } y0 += size + gthick; } pixDestroy(&pixt0); pixDestroy(&pixt1); return pixd; }