/*! * pixRotateAM() * * Input: pixs (2, 4, 8 bpp gray or colormapped, or 32 bpp RGB) * angle (radians; clockwise is positive) * incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK) * Return: pixd, or null on error * * Notes: * (1) Rotates about image center. * (2) A positive angle gives a clockwise rotation. * (3) Brings in either black or white pixels from the boundary. */ PIX * pixRotateAM(PIX *pixs, l_float32 angle, l_int32 incolor) { l_int32 d; l_uint32 fillval; PIX *pixt1, *pixt2, *pixd; PROCNAME("pixRotateAM"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (pixGetDepth(pixs) == 1) return (PIX *)ERROR_PTR("pixs is 1 bpp", procName, NULL); if (L_ABS(angle) < MIN_ANGLE_TO_ROTATE) return pixClone(pixs); /* Remove cmap if it exists, and unpack to 8 bpp if necessary */ pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); d = pixGetDepth(pixt1); if (d < 8) pixt2 = pixConvertTo8(pixt1, FALSE); else pixt2 = pixClone(pixt1); d = pixGetDepth(pixt2); /* Compute actual incoming color */ fillval = 0; if (incolor == L_BRING_IN_WHITE) { if (d == 8) fillval = 255; else /* d == 32 */ fillval = 0xffffff00; } if (d == 8) pixd = pixRotateAMGray(pixt2, angle, fillval); else /* d == 32 */ pixd = pixRotateAMColor(pixt2, angle, fillval); pixDestroy(&pixt1); pixDestroy(&pixt2); return pixd; }
/*! * pixRotateAMColor() * * Input: pixs (32 bpp) * angle (radians; clockwise is positive) * colorval (e.g., 0 to bring in BLACK, 0xffffff00 for WHITE) * Return: pixd, or null on error * * Notes: * (1) Rotates about image center. * (2) A positive angle gives a clockwise rotation. * (3) Specify the color to be brought in from outside the image. */ PIX * pixRotateAMColor(PIX *pixs, l_float32 angle, l_uint32 colorval) { l_int32 w, h, wpls, wpld; l_uint32 *datas, *datad; PIX *pix1, *pix2, *pixd; PROCNAME("pixRotateAMColor"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); if (pixGetDepth(pixs) != 32) return (PIX *)ERROR_PTR("pixs must be 32 bpp", procName, NULL); if (L_ABS(angle) < MIN_ANGLE_TO_ROTATE) return pixClone(pixs); pixGetDimensions(pixs, &w, &h, NULL); datas = pixGetData(pixs); wpls = pixGetWpl(pixs); pixd = pixCreateTemplate(pixs); datad = pixGetData(pixd); wpld = pixGetWpl(pixd); rotateAMColorLow(datad, w, h, wpld, datas, wpls, angle, colorval); if (pixGetSpp(pixs) == 4) { pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL); pix2 = pixRotateAMGray(pix1, angle, 255); /* bring in opaque */ pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL); pixDestroy(&pix1); pixDestroy(&pix2); } return pixd; }
int main(int argc, char **argv) { char *filein; l_float32 angle, conf, deg2rad; PIX *pixs, *pix1, *pix2, *pix3, *pix4, *pix5; PIX *pix6, *pix7, *pix8, *pix9; static char mainName[] = "lineremoval"; if (argc != 2) return ERROR_INT(" Syntax: lineremoval filein", mainName, 1); filein = argv[1]; deg2rad = 3.14159 / 180.; if ((pixs = pixRead(filein)) == NULL) return ERROR_INT("pix not made", mainName, 1); /* threshold to binary, extracting much of the lines */ pix1 = pixThresholdToBinary(pixs, 170); pixWrite("/tmp/dave-proc1.png", pix1, IFF_PNG); pixDisplayWrite(pix1, 1); /* find the skew angle and deskew using an interpolated * rotator for anti-aliasing (to avoid jaggies) */ pixFindSkew(pix1, &angle, &conf); pix2 = pixRotateAMGray(pixs, deg2rad * angle, 255); pixWrite("/tmp/dave-proc2.png", pix2, IFF_PNG); pixDisplayWrite(pix2, 1); /* extract the lines to be removed */ pix3 = pixCloseGray(pix2, 51, 1); pixWrite("/tmp/dave-proc3.png", pix3, IFF_PNG); pixDisplayWrite(pix3, 1); /* solidify the lines to be removed */ pix4 = pixErodeGray(pix3, 1, 5); pixWrite("/tmp/dave-proc4.png", pix4, IFF_PNG); pixDisplayWrite(pix4, 1); /* clean the background of those lines */ pix5 = pixThresholdToValue(NULL, pix4, 210, 255); pixWrite("/tmp/dave-proc5.png", pix5, IFF_PNG); pixDisplayWrite(pix5, 1); pix6 = pixThresholdToValue(NULL, pix5, 200, 0); pixWrite("/tmp/dave-proc6.png", pix6, IFF_PNG); pixDisplayWrite(pix6, 1); /* get paint-through mask for changed pixels */ pix7 = pixThresholdToBinary(pix6, 210); pixWrite("/tmp/dave-proc7.png", pix7, IFF_PNG); pixDisplayWrite(pix7, 1); /* add the inverted, cleaned lines to orig. Because * the background was cleaned, the inversion is 0, * so when you add, it doesn't lighten those pixels. * It only lightens (to white) the pixels in the lines! */ pixInvert(pix6, pix6); pix8 = pixAddGray(NULL, pix2, pix6); pixWrite("/tmp/dave-proc8.png", pix8, IFF_PNG); pixDisplayWrite(pix8, 1); pix9 = pixOpenGray(pix8, 1, 9); pixWrite("/tmp/dave-proc9.png", pix9, IFF_PNG); pixDisplayWrite(pix9, 1); pixCombineMasked(pix8, pix9, pix7); pixWrite("/tmp/dave-result.png", pix8, IFF_PNG); pixDisplayWrite(pix8, 1); pixDisplayMultiple("/tmp/display/file*"); return 0; }
/*! * pixRotate() * * Input: pixs (1, 2, 4, 8, 32 bpp rgb) * angle (radians; clockwise is positive) * type (L_ROTATE_AREA_MAP, L_ROTATE_SHEAR, L_ROTATE_SAMPLING) * incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK) * width (original width; use 0 to avoid embedding) * height (original height; use 0 to avoid embedding) * Return: pixd, or null on error * * Notes: * (1) This is a high-level, simple interface for rotating images * about their center. * (2) For very small rotations, just return a clone. * (3) Rotation brings either white or black pixels in * from outside the image. * (4) The rotation type is adjusted if necessary for the image * depth and size of rotation angle. For 1 bpp images, we * rotate either by shear or sampling. * (5) Colormaps are removed for rotation by area mapping. * (6) The dest can be expanded so that no image pixels * are lost. To invoke expansion, input the original * width and height. For repeated rotation, use of the * original width and height allows the expansion to * stop at the maximum required size, which is a square * with side = sqrt(w*w + h*h). * * *** Warning: implicit assumption about RGB component ordering *** */ PIX * pixRotate(PIX *pixs, l_float32 angle, l_int32 type, l_int32 incolor, l_int32 width, l_int32 height) { l_int32 w, h, d; l_uint32 fillval; PIX *pixt1, *pixt2, *pixt3, *pixd; PIXCMAP *cmap; PROCNAME("pixRotate"); if (!pixs) return (PIX *) ERROR_PTR("pixs not defined", procName, NULL); if (type != L_ROTATE_SHEAR && type != L_ROTATE_AREA_MAP && type != L_ROTATE_SAMPLING) return (PIX *) ERROR_PTR("invalid type", procName, NULL); if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) return (PIX *) ERROR_PTR("invalid incolor", procName, NULL); if (L_ABS(angle) < MIN_ANGLE_TO_ROTATE) return pixClone(pixs); /* Adjust rotation type if necessary: * - If d == 1 bpp and the angle is more than about 6 degrees, * rotate by sampling; otherwise rotate by shear. * - If d > 1, only allow shear rotation up to about 20 degrees; * beyond that, default a shear request to sampling. */ if (pixGetDepth(pixs) == 1) { if (L_ABS(angle) > MAX_1BPP_SHEAR_ANGLE) { if (type != L_ROTATE_SAMPLING) L_INFO("1 bpp, large angle; rotate by sampling\n", procName); type = L_ROTATE_SAMPLING; } else if (type != L_ROTATE_SHEAR) { L_INFO("1 bpp; rotate by shear\n", procName); type = L_ROTATE_SHEAR; } } else if (L_ABS(angle) > LIMIT_SHEAR_ANGLE && type == L_ROTATE_SHEAR) { L_INFO("large angle; rotate by sampling\n", procName); type = L_ROTATE_SAMPLING; } /* Remove colormap if we rotate by area mapping. */ cmap = pixGetColormap(pixs); if (cmap && type == L_ROTATE_AREA_MAP) pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); else pixt1 = pixClone(pixs); cmap = pixGetColormap(pixt1); /* Otherwise, if there is a colormap and we're not embedding, * add white color if it doesn't exist. */ if (cmap && width == 0) { /* no embedding; generate @incolor */ if (incolor == L_BRING_IN_BLACK) pixcmapAddBlackOrWhite(cmap, 0, NULL); else /* L_BRING_IN_WHITE */ pixcmapAddBlackOrWhite(cmap, 1, NULL); } /* Request to embed in a larger image; do if necessary */ pixt2 = pixEmbedForRotation(pixt1, angle, incolor, width, height); /* Area mapping requires 8 or 32 bpp. If less than 8 bpp and * area map rotation is requested, convert to 8 bpp. */ d = pixGetDepth(pixt2); if (type == L_ROTATE_AREA_MAP && d < 8) pixt3 = pixConvertTo8(pixt2, FALSE); else pixt3 = pixClone(pixt2); /* Do the rotation: shear, sampling or area mapping */ pixGetDimensions(pixt3, &w, &h, &d); if (type == L_ROTATE_SHEAR) { pixd = pixRotateShearCenter(pixt3, angle, incolor); } else if (type == L_ROTATE_SAMPLING) { pixd = pixRotateBySampling(pixt3, w / 2, h / 2, angle, incolor); } else { /* rotate by area mapping */ fillval = 0; if (incolor == L_BRING_IN_WHITE) { if (d == 8) fillval = 255; else /* d == 32 */ fillval = 0xffffff00; } if (d == 8) pixd = pixRotateAMGray(pixt3, angle, fillval); else /* d == 32 */ pixd = pixRotateAMColor(pixt3, angle, fillval); } pixDestroy(&pixt1); pixDestroy(&pixt2); pixDestroy(&pixt3); return pixd; }
int main_line_removal() { PIX* pixs_source = pixRead("dave-start.png"); if (!pixs_source) { printf("Error opening file"); return 1; } double deg2rad = 3.1415926535 / 180.; l_float32 angle, conf, score; PIX *pix1, *pix2, *pix3, *pix4, *pix5, *pix6, *pix7, *pix8, *pix9; pix1 = pixThresholdToBinary(pixs_source, 160); printf("Create line removal image\n"); pixWrite("line-removal/result.line-removal-1.tif", pix1, IFF_TIFF_G4); pixFindSkew(pix1, &angle, &conf); pix2 = pixRotateAMGray(pixs_source, deg2rad * angle, 160); printf("Create line removal image\n"); pixWrite("line-removal/result.line-removal-2.tif", pix2, IFF_TIFF_G4); l_int32 HORIZ = 1; l_int32 VERT = 3; pix3 = pixCloseGray(pix2, 51, HORIZ); //k?p?c 51? printf("Create line removal image\n"); pixWrite("line-removal/result.line-removal-3.tif", pix3, IFF_TIFF_G4); pix4 = pixErodeGray(pix3, 5, VERT); //k?p?c 5? printf("Create line removal image\n"); pixWrite("line-removal/result.line-removal-4.tif", pix4, IFF_TIFF_G4); pix5 = pix4; pix5 = pixThresholdToValue(pix5, pix4, 230, 255); printf("Create line removal image\n"); pixWrite("line-removal/result.line-removal-5.tif", pix5, IFF_TIFF_G4); pix6 = pix5; pix6 = pixThresholdToValue(pix5, pix5, 210, 0); printf("Create line removal image\n"); pixWrite("line-removal/result.line-removal-6.tif", pix6, IFF_TIFF_G4); pix7 = pixThresholdToBinary(pix6, 230); printf("Create line removal image\n"); pixWrite("line-removal/result.line-removal-7.tif", pix7, IFF_TIFF_G4); pixInvert(pix6, pix6); pix8 = pixAddGray(NULL, pix2, pix6); printf("Create line removal image\n"); pixWrite("line-removal/result.line-removal-8.tif", pix8, IFF_TIFF_G4); VERT = 7; pix9 = pixOpenGray(pix8, 3, VERT); printf("Create line removal image\n"); pixWrite("line-removal/result.line-removal-9.tif", pix9, IFF_TIFF_G4); if (pixCombineMasked(pix8, pix9, pix7)) { printf("!!!Error while combining pixs!!!\n"); } printf("Create line removal image\n"); pixWrite("line-removal/result.line-removal-final.tif", pix8, IFF_TIFF_G4); printf("\n---\nEnd\n"); getchar(); return 0; }