l_int32 pixCopyResolution(PIX *pixd, PIX *pixs) { PROCNAME("pixCopyResolution"); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (!pixd) return ERROR_INT("pixd not defined", procName, 1); if (pixs == pixd) return 0; /* no-op */ pixSetXRes(pixd, pixGetXRes(pixs)); pixSetYRes(pixd, pixGetYRes(pixs)); return 0; }
/*! * pixWriteStreamPng() * * Input: stream * pix * gamma (use 0.0 if gamma is not defined) * Return: 0 if OK; 1 on error * * Notes: * (1) If called from pixWriteStream(), the stream is positioned * at the beginning of the file. * (2) To do sequential writes of png format images to a stream, * use pixWriteStreamPng() directly. * (3) gamma is an optional png chunk. If no gamma value is to be * placed into the file, use gamma = 0.0. Otherwise, if * gamma > 0.0, its value is written into the header. * (4) The use of gamma in png is highly problematic. For an illuminating * discussion, see: http://hsivonen.iki.fi/png-gamma/ * (5) What is the effect/meaning of gamma in the png file? This * gamma, which we can call the 'source' gamma, is the * inverse of the gamma that was used in enhance.c to brighten * or darken images. The 'source' gamma is supposed to indicate * the intensity mapping that was done at the time the * image was captured. Display programs typically apply a * 'display' gamma of 2.2 to the output, which is intended * to linearize the intensity based on the response of * thermionic tubes (CRTs). Flat panel LCDs have typically * been designed to give a similar response as CRTs (call it * "backward compatibility"). The 'display' gamma is * in some sense the inverse of the 'source' gamma. * jpeg encoders attached to scanners and cameras will lighten * the pixels, applying a gamma corresponding to approximately * a square-root relation of output vs input: * output = input^(gamma) * where gamma is often set near 0.4545 (1/gamma is 2.2). * This is stored in the image file. Then if the display * program reads the gamma, it will apply a display gamma, * typically about 2.2; the product is 1.0, and the * display program produces a linear output. This works because * the dark colors were appropriately boosted by the scanner, * as described by the 'source' gamma, so they should not * be further boosted by the display program. * (6) As an example, with xv and display, if no gamma is stored, * the program acts as if gamma were 0.4545, multiplies this by 2.2, * and does a linear rendering. Taking this as a baseline * brightness, if the stored gamma is: * > 0.4545, the image is rendered lighter than baseline * < 0.4545, the image is rendered darker than baseline * In contrast, gqview seems to ignore the gamma chunk in png. * (7) The only valid pixel depths in leptonica are 1, 2, 4, 8, 16 * and 32. However, it is possible, and in some cases desirable, * to write out a png file using an rgb pix that has 24 bpp. * For example, the open source xpdf SplashBitmap class generates * 24 bpp rgb images. Consequently, we anble writing 24 bpp pix. * To generate such a pix, you can make a 24 bpp pix without data * and assign the data array to the pix; e.g., * pix = pixCreateHeader(w, h, 24); * pixSetData(pix, rgbdata); * See pixConvert32To24() for an example, where we get rgbdata * from the 32 bpp pix. Caution: do not call pixSetPadBits(), * because the alignment is wrong and you may erase part of the * last pixel on each line. */ l_int32 pixWriteStreamPng(FILE *fp, PIX *pix, l_float32 gamma) { char commentstring[] = "Comment"; l_int32 i, j, k; l_int32 wpl, d, cmflag; l_int32 ncolors; l_int32 *rmap, *gmap, *bmap; l_uint32 *data, *ppixel; png_byte bit_depth, color_type; png_uint_32 w, h; png_uint_32 xres, yres; png_bytep *row_pointers; png_bytep rowbuffer; png_structp png_ptr; png_infop info_ptr; png_colorp palette; PIX *pixt; PIXCMAP *cmap; char *text; PROCNAME("pixWriteStreamPng"); if (!fp) return ERROR_INT("stream not open", procName, 1); if (!pix) return ERROR_INT("pix not defined", procName, 1); /* Allocate the 2 data structures */ if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL)) == NULL) return ERROR_INT("png_ptr not made", procName, 1); if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return ERROR_INT("info_ptr not made", procName, 1); } /* Set up png setjmp error handling */ if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); return ERROR_INT("internal png error", procName, 1); } png_init_io(png_ptr, fp); /* With best zlib compression (9), get between 1 and 10% improvement * over default (5), but the compression is 3 to 10 times slower. * Our default compression is the zlib default (5). */ png_set_compression_level(png_ptr, var_ZLIB_COMPRESSION); w = pixGetWidth(pix); h = pixGetHeight(pix); d = pixGetDepth(pix); if ((cmap = pixGetColormap(pix))) cmflag = 1; else cmflag = 0; /* Set the color type and bit depth. */ if (d == 32 && var_PNG_WRITE_ALPHA == 1) { bit_depth = 8; color_type = PNG_COLOR_TYPE_RGBA; /* 6 */ cmflag = 0; /* ignore if it exists */ } else if (d == 24 || d == 32) { bit_depth = 8; color_type = PNG_COLOR_TYPE_RGB; /* 2 */ cmflag = 0; /* ignore if it exists */ } else { bit_depth = d; color_type = PNG_COLOR_TYPE_GRAY; /* 0 */ } if (cmflag) color_type = PNG_COLOR_TYPE_PALETTE; /* 3 */ #if DEBUG fprintf(stderr, "cmflag = %d, bit_depth = %d, color_type = %d\n", cmflag, bit_depth, color_type); #endif /* DEBUG */ png_set_IHDR(png_ptr, info_ptr, w, h, bit_depth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); /* Store resolution in ppm, if known */ xres = (png_uint_32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5); yres = (png_uint_32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5); if ((xres == 0) || (yres == 0)) png_set_pHYs(png_ptr, info_ptr, 0, 0, PNG_RESOLUTION_UNKNOWN); else png_set_pHYs(png_ptr, info_ptr, xres, yres, PNG_RESOLUTION_METER); if (cmflag) { pixcmapToArrays(cmap, &rmap, &gmap, &bmap); ncolors = pixcmapGetCount(cmap); /* Make and save the palette */ if ((palette = (png_colorp)(CALLOC(ncolors, sizeof(png_color)))) == NULL) return ERROR_INT("palette not made", procName, 1); for (i = 0; i < ncolors; i++) { palette[i].red = (png_byte)rmap[i]; palette[i].green = (png_byte)gmap[i]; palette[i].blue = (png_byte)bmap[i]; } png_set_PLTE(png_ptr, info_ptr, palette, (int)ncolors); FREE(rmap); FREE(gmap); FREE(bmap); } /* 0.4545 is treated as the default by some image * display programs (not gqview). A value > 0.4545 will * lighten an image as displayed by xv, display, etc. */ if (gamma > 0.0) png_set_gAMA(png_ptr, info_ptr, (l_float64)gamma); if ((text = pixGetText(pix))) { png_text text_chunk; text_chunk.compression = PNG_TEXT_COMPRESSION_NONE; text_chunk.key = commentstring; text_chunk.text = text; text_chunk.text_length = strlen(text); #ifdef PNG_ITXT_SUPPORTED text_chunk.itxt_length = 0; text_chunk.lang = NULL; text_chunk.lang_key = NULL; #endif png_set_text(png_ptr, info_ptr, &text_chunk, 1); } /* Write header and palette info */ png_write_info(png_ptr, info_ptr); if ((d != 32) && (d != 24)) { /* not rgb color */ /* Generate a temporary pix with bytes swapped. * For a binary image, there are two conditions in * which you must first invert the data for writing png: * (a) no colormap * (b) colormap with BLACK set to 0 * png writes binary with BLACK = 0, unless contradicted * by a colormap. If the colormap has BLACK = "1" * (typ. about 255), do not invert the data. If there * is no colormap, you must invert the data to store * in default BLACK = 0 state. */ if (d == 1 && (!cmap || (cmap && ((l_uint8 *)(cmap->array))[0] == 0x0))) { pixt = pixInvert(NULL, pix); pixEndianByteSwap(pixt); } else pixt = pixEndianByteSwapNew(pix); if (!pixt) { png_destroy_write_struct(&png_ptr, &info_ptr); return ERROR_INT("pixt not made", procName, 1); } /* Make and assign array of image row pointers */ if ((row_pointers = (png_bytep *)CALLOC(h, sizeof(png_bytep))) == NULL) return ERROR_INT("row-pointers not made", procName, 1); wpl = pixGetWpl(pixt); data = pixGetData(pixt); for (i = 0; i < h; i++) row_pointers[i] = (png_bytep)(data + i * wpl); png_set_rows(png_ptr, info_ptr, row_pointers); /* Transfer the data */ png_write_image(png_ptr, row_pointers); png_write_end(png_ptr, info_ptr); if (cmflag) FREE(palette); FREE(row_pointers); pixDestroy(&pixt); png_destroy_write_struct(&png_ptr, &info_ptr); return 0; } /* For rgb, compose and write a row at a time */ data = pixGetData(pix); wpl = pixGetWpl(pix); if (d == 24) { /* See note 7 above: special case of 24 bpp rgb */ for (i = 0; i < h; i++) { ppixel = data + i * wpl; png_write_rows(png_ptr, (png_bytepp)&ppixel, 1); } } else { /* 32 bpp rgb and rgba */ if ((rowbuffer = (png_bytep)CALLOC(w, 4)) == NULL) return ERROR_INT("rowbuffer not made", procName, 1); for (i = 0; i < h; i++) { ppixel = data + i * wpl; for (j = k = 0; j < w; j++) { rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED); rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN); rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE); if (var_PNG_WRITE_ALPHA == 1) rowbuffer[k++] = GET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL); ppixel++; } png_write_rows(png_ptr, &rowbuffer, 1); } FREE(rowbuffer); } png_write_end(png_ptr, info_ptr); if (cmflag) FREE(palette); png_destroy_write_struct(&png_ptr, &info_ptr); return 0; }
/*! * \brief pixWriteMemBmp() * * \param[out] pfdata data of bmp formatted image * \param[out] pfsize size of returned data * \param[in] pixs 1, 2, 4, 8, 16, 32 bpp * \return 0 if OK, 1 on error * * <pre> * Notes: * (1) 2 bpp bmp files are not valid in the spec, and are * written as 8 bpp. * (2) pix with depth <= 8 bpp are written with a colormap. * 16 bpp gray and 32 bpp rgb pix are written without a colormap. * (3) The transparency component in an rgb pix is ignored. * All 32 bpp pix have the bmp alpha component set to 255 (opaque). * (4) The bmp colormap entries, RGBA_QUAD, are the same as * the ones used for colormaps in leptonica. This allows * a simple memcpy for bmp output. * </pre> */ l_int32 pixWriteMemBmp(l_uint8 **pfdata, size_t *pfsize, PIX *pixs) { l_uint8 pel[4]; l_uint8 *cta = NULL; /* address of the bmp color table array */ l_uint8 *fdata, *data, *fmdata; l_int32 cmaplen; /* number of bytes in the bmp colormap */ l_int32 ncolors, val, stepsize; l_int32 w, h, d, fdepth, xres, yres; l_int32 pixWpl, pixBpl, extrabytes, fBpl, fWpl, i, j, k; l_int32 heapcm; /* extra copy of cta on the heap ? 1 : 0 */ l_uint32 offbytes, fimagebytes; l_uint32 *line, *pword; size_t fsize; BMP_FH *bmpfh; BMP_IH *bmpih; PIX *pix; PIXCMAP *cmap; RGBA_QUAD *pquad; PROCNAME("pixWriteMemBmp"); if (pfdata) *pfdata = NULL; if (pfsize) *pfsize = 0; if (!pfdata) return ERROR_INT("&fdata not defined", procName, 1 ); if (!pfsize) return ERROR_INT("&fsize not defined", procName, 1 ); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); pixGetDimensions(pixs, &w, &h, &d); if (d == 2) { L_WARNING("2 bpp files can't be read; converting to 8 bpp\n", procName); pix = pixConvert2To8(pixs, 0, 85, 170, 255, 1); d = 8; } else { pix = pixCopy(NULL, pixs); } fdepth = (d == 32) ? 24 : d; /* Resolution is given in pixels/meter */ xres = (l_int32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5); yres = (l_int32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5); pixWpl = pixGetWpl(pix); pixBpl = 4 * pixWpl; fWpl = (w * fdepth + 31) / 32; fBpl = 4 * fWpl; fimagebytes = h * fBpl; if (fimagebytes > 4LL * L_MAX_ALLOWED_PIXELS) { pixDestroy(&pix); return ERROR_INT("image data is too large", procName, 1); } /* If not rgb or 16 bpp, the bmp data is required to have a colormap */ heapcm = 0; if (d == 32 || d == 16) { /* 24 bpp rgb or 16 bpp: no colormap */ ncolors = 0; cmaplen = 0; } else if ((cmap = pixGetColormap(pix))) { /* existing colormap */ ncolors = pixcmapGetCount(cmap); cmaplen = ncolors * sizeof(RGBA_QUAD); cta = (l_uint8 *)cmap->array; } else { /* no existing colormap; d <= 8; make a binary or gray one */ if (d == 1) { cmaplen = sizeof(bwmap); ncolors = 2; cta = (l_uint8 *)bwmap; } else { /* d = 2,4,8; use a grayscale output colormap */ ncolors = 1 << fdepth; cmaplen = ncolors * sizeof(RGBA_QUAD); heapcm = 1; cta = (l_uint8 *)LEPT_CALLOC(cmaplen, 1); stepsize = 255 / (ncolors - 1); for (i = 0, val = 0, pquad = (RGBA_QUAD *)cta; i < ncolors; i++, val += stepsize, pquad++) { pquad->blue = pquad->green = pquad->red = val; pquad->alpha = 255; /* opaque */ } } } #if DEBUG {l_uint8 *pcmptr; pcmptr = (l_uint8 *)pixGetColormap(pix)->array; fprintf(stderr, "Pix colormap[0] = %c%c%c%d\n", pcmptr[0], pcmptr[1], pcmptr[2], pcmptr[3]); fprintf(stderr, "Pix colormap[1] = %c%c%c%d\n", pcmptr[4], pcmptr[5], pcmptr[6], pcmptr[7]); } #endif /* DEBUG */ offbytes = BMP_FHBYTES + BMP_IHBYTES + cmaplen; fsize = offbytes + fimagebytes; fdata = (l_uint8 *)LEPT_CALLOC(fsize, 1); *pfdata = fdata; *pfsize = fsize; /* Convert to little-endian and write the file header data */ bmpfh = (BMP_FH *)fdata; bmpfh->bfType = convertOnBigEnd16(BMP_ID); bmpfh->bfSize = convertOnBigEnd16(fsize & 0x0000ffff); bmpfh->bfFill1 = convertOnBigEnd16((fsize >> 16) & 0x0000ffff); bmpfh->bfOffBits = convertOnBigEnd16(offbytes & 0x0000ffff); bmpfh->bfFill2 = convertOnBigEnd16((offbytes >> 16) & 0x0000ffff); /* Convert to little-endian and write the info header data */ bmpih = (BMP_IH *)(fdata + BMP_FHBYTES); bmpih->biSize = convertOnBigEnd32(BMP_IHBYTES); bmpih->biWidth = convertOnBigEnd32(w); bmpih->biHeight = convertOnBigEnd32(h); bmpih->biPlanes = convertOnBigEnd16(1); bmpih->biBitCount = convertOnBigEnd16(fdepth); bmpih->biSizeImage = convertOnBigEnd32(fimagebytes); bmpih->biXPelsPerMeter = convertOnBigEnd32(xres); bmpih->biYPelsPerMeter = convertOnBigEnd32(yres); bmpih->biClrUsed = convertOnBigEnd32(ncolors); bmpih->biClrImportant = convertOnBigEnd32(ncolors); /* Copy the colormap data and free the cta if necessary */ if (ncolors > 0) { memcpy(fdata + BMP_FHBYTES + BMP_IHBYTES, cta, cmaplen); if (heapcm) LEPT_FREE(cta); } /* When you write a binary image with a colormap * that sets BLACK to 0, you must invert the data */ if (fdepth == 1 && cmap && ((l_uint8 *)(cmap->array))[0] == 0x0) { pixInvert(pix, pix); } /* An endian byte swap is also required */ pixEndianByteSwap(pix); /* Transfer the image data. Image origin for bmp is at lower right. */ fmdata = fdata + offbytes; if (fdepth != 24) { /* typ 1 or 8 bpp */ data = (l_uint8 *)pixGetData(pix) + pixBpl * (h - 1); for (i = 0; i < h; i++) { memcpy(fmdata, data, fBpl); data -= pixBpl; fmdata += fBpl; } } else { /* 32 bpp pix; 24 bpp file * See the comments in pixReadStreamBmp() to * understand the logic behind the pixel ordering below. * Note that we have again done an endian swap on * little endian machines before arriving here, so that * the bytes are ordered on both platforms as: Red Green Blue -- |-----------|------------|-----------|-----------| */ extrabytes = fBpl - 3 * w; line = pixGetData(pix) + pixWpl * (h - 1); for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { pword = line + j; pel[2] = *((l_uint8 *)pword + COLOR_RED); pel[1] = *((l_uint8 *)pword + COLOR_GREEN); pel[0] = *((l_uint8 *)pword + COLOR_BLUE); memcpy(fmdata, &pel, 3); fmdata += 3; } if (extrabytes) { for (k = 0; k < extrabytes; k++) { memcpy(fmdata, &pel, 1); fmdata++; } } line -= pixWpl; } } pixDestroy(&pix); return 0; }
/*! * \brief pixWriteStreamJpeg() * * \param[in] fp file stream * \param[in] pixs any depth; cmap is OK * \param[in] quality 1 - 100; 75 is default value; 0 is also default * \param[in] progressive 0 for baseline sequential; 1 for progressive * \return 0 if OK, 1 on error * * <pre> * Notes: * (1) Progressive encoding gives better compression, at the * expense of slower encoding and decoding. * (2) Standard chroma subsampling is 2x2 on both the U and V * channels. For highest quality, use no subsampling; this * option is set by pixSetChromaSampling(pix, 0). * (3) The only valid pixel depths in leptonica are 1, 2, 4, 8, 16 * and 32 bpp. However, it is possible, and in some cases desirable, * to write out a jpeg file using an rgb pix that has 24 bpp. * This can be created by appending the raster data for a 24 bpp * image (with proper scanline padding) directly to a 24 bpp * pix that was created without a data array. * (4) There are two compression paths in this function: * * Grayscale image, no colormap: compress as 8 bpp image. * * rgb full color image: copy each line into the color * line buffer, and compress as three 8 bpp images. * (5) Under the covers, the jpeg library transforms rgb to a * luminance-chromaticity triple, each component of which is * also 8 bits, and compresses that. It uses 2 Huffman tables, * a higher resolution one (with more quantization levels) * for luminosity and a lower resolution one for the chromas. * </pre> */ l_int32 pixWriteStreamJpeg(FILE *fp, PIX *pixs, l_int32 quality, l_int32 progressive) { l_int32 xres, yres; l_int32 i, j, k; l_int32 w, h, d, wpl, spp, colorflag, rowsamples; l_uint32 *ppixel, *line, *data; JSAMPROW rowbuffer; PIX *pix; struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; const char *text; jmp_buf jmpbuf; /* must be local to the function */ PROCNAME("pixWriteStreamJpeg"); if (!fp) return ERROR_INT("stream not open", procName, 1); if (!pixs) return ERROR_INT("pixs not defined", procName, 1); if (quality <= 0) quality = 75; /* default */ /* If necessary, convert the pix so that it can be jpeg compressed. * The colormap is removed based on the source, so if the colormap * has only gray colors, the image will be compressed with spp = 1. */ pixGetDimensions(pixs, &w, &h, &d); pix = NULL; if (pixGetColormap(pixs) != NULL) { L_INFO("removing colormap; may be better to compress losslessly\n", procName); pix = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); } else if (d >= 8 && d != 16) { /* normal case; no rewrite */ pix = pixClone(pixs); } else if (d < 8 || d == 16) { L_INFO("converting from %d to 8 bpp\n", procName, d); pix = pixConvertTo8(pixs, 0); /* 8 bpp, no cmap */ } else { L_ERROR("unknown pix type with d = %d and no cmap\n", procName, d); return 1; } if (!pix) return ERROR_INT("pix not made", procName, 1); rewind(fp); rowbuffer = NULL; /* Modify the jpeg error handling to catch fatal errors */ cinfo.err = jpeg_std_error(&jerr); cinfo.client_data = (void *)&jmpbuf; jerr.error_exit = jpeg_error_catch_all_1; if (setjmp(jmpbuf)) { LEPT_FREE(rowbuffer); pixDestroy(&pix); return ERROR_INT("internal jpeg error", procName, 1); } /* Initialize the jpeg structs for compression */ jpeg_create_compress(&cinfo); jpeg_stdio_dest(&cinfo, fp); cinfo.image_width = w; cinfo.image_height = h; /* Set the color space and number of components */ d = pixGetDepth(pix); if (d == 8) { colorflag = 0; /* 8 bpp grayscale; no cmap */ cinfo.input_components = 1; cinfo.in_color_space = JCS_GRAYSCALE; } else { /* d == 32 || d == 24 */ colorflag = 1; /* rgb */ cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; } jpeg_set_defaults(&cinfo); /* Setting optimize_coding to TRUE seems to improve compression * by approx 2-4 percent, and increases comp time by approx 20%. */ cinfo.optimize_coding = FALSE; /* Set resolution in pixels/in (density_unit: 1 = in, 2 = cm) */ xres = pixGetXRes(pix); yres = pixGetYRes(pix); if ((xres != 0) && (yres != 0)) { cinfo.density_unit = 1; /* designates pixels per inch */ cinfo.X_density = xres; cinfo.Y_density = yres; } /* Set the quality and progressive parameters */ jpeg_set_quality(&cinfo, quality, TRUE); if (progressive) jpeg_simple_progression(&cinfo); /* Set the chroma subsampling parameters. This is done in * YUV color space. The Y (intensity) channel is never subsampled. * The standard subsampling is 2x2 on both the U and V channels. * Notation on this is confusing. For a nice illustrations, see * http://en.wikipedia.org/wiki/Chroma_subsampling * The standard subsampling is written as 4:2:0. * We allow high quality where there is no subsampling on the * chroma channels: denoted as 4:4:4. */ if (pixs->special == L_NO_CHROMA_SAMPLING_JPEG) { cinfo.comp_info[0].h_samp_factor = 1; cinfo.comp_info[0].v_samp_factor = 1; cinfo.comp_info[1].h_samp_factor = 1; cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; cinfo.comp_info[2].v_samp_factor = 1; } jpeg_start_compress(&cinfo, TRUE); if ((text = pixGetText(pix))) jpeg_write_marker(&cinfo, JPEG_COM, (const JOCTET *)text, strlen(text)); /* Allocate row buffer */ spp = cinfo.input_components; rowsamples = spp * w; if ((rowbuffer = (JSAMPROW)LEPT_CALLOC(sizeof(JSAMPLE), rowsamples)) == NULL) { pixDestroy(&pix); return ERROR_INT("calloc fail for rowbuffer", procName, 1); } data = pixGetData(pix); wpl = pixGetWpl(pix); for (i = 0; i < h; i++) { line = data + i * wpl; if (colorflag == 0) { /* 8 bpp gray */ for (j = 0; j < w; j++) rowbuffer[j] = GET_DATA_BYTE(line, j); } else { /* colorflag == 1 */ if (d == 24) { /* See note 3 above; special case of 24 bpp rgb */ jpeg_write_scanlines(&cinfo, (JSAMPROW *)&line, 1); } else { /* standard 32 bpp rgb */ ppixel = line; for (j = k = 0; j < w; j++) { rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED); rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN); rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE); ppixel++; } } } if (d != 24) jpeg_write_scanlines(&cinfo, &rowbuffer, 1); } jpeg_finish_compress(&cinfo); pixDestroy(&pix); LEPT_FREE(rowbuffer); jpeg_destroy_compress(&cinfo); return 0; }
// Segment the page according to the current value of tessedit_pageseg_mode. // If the pix_binary_ member is not NULL, it is used as the source image, // and copied to image, otherwise it just uses image as the input. // On return the blocks list owns all the constructed page layout. int Tesseract::SegmentPage(const STRING* input_file, IMAGE* image, BLOCK_LIST* blocks) { int width = image->get_xsize(); int height = image->get_ysize(); int resolution = image->get_res(); #ifdef HAVE_LIBLEPT if (pix_binary_ != NULL) { width = pixGetWidth(pix_binary_); height = pixGetHeight(pix_binary_); resolution = pixGetXRes(pix_binary_); } #endif // Zero resolution messes up the algorithms, so make sure it is credible. if (resolution < kMinCredibleResolution) resolution = kDefaultResolution; // Get page segmentation mode. PageSegMode pageseg_mode = static_cast<PageSegMode>( static_cast<int>(tessedit_pageseg_mode)); // If a UNLV zone file can be found, use that instead of segmentation. if (pageseg_mode != tesseract::PSM_AUTO && input_file != NULL && input_file->length() > 0) { STRING name = *input_file; const char* lastdot = strrchr(name.string(), '.'); if (lastdot != NULL) name[lastdot - name.string()] = '\0'; read_unlv_file(name, width, height, blocks); } bool single_column = pageseg_mode > PSM_AUTO; if (blocks->empty()) { // No UNLV file present. Work according to the PageSegMode. // First make a single block covering the whole image. BLOCK_IT block_it(blocks); BLOCK* block = new BLOCK("", TRUE, 0, 0, 0, 0, width, height); block_it.add_to_end(block); } else { // UNLV file present. Use PSM_SINGLE_COLUMN. pageseg_mode = PSM_SINGLE_COLUMN; } TO_BLOCK_LIST land_blocks, port_blocks; TBOX page_box; if (pageseg_mode <= PSM_SINGLE_COLUMN) { if (AutoPageSeg(width, height, resolution, single_column, image, blocks, &port_blocks) < 0) { return -1; } // To create blobs from the image region bounds uncomment this line: // port_blocks.clear(); // Uncomment to go back to the old mode. } else { #if HAVE_LIBLEPT image->FromPix(pix_binary_); #endif deskew_ = FCOORD(1.0f, 0.0f); reskew_ = FCOORD(1.0f, 0.0f); } if (blocks->empty()) { tprintf("Empty page\n"); return 0; // AutoPageSeg found an empty page. } if (port_blocks.empty()) { // AutoPageSeg was not used, so we need to find_components first. find_components(blocks, &land_blocks, &port_blocks, &page_box); } else { // AutoPageSeg does not need to find_components as it did that already. page_box.set_left(0); page_box.set_bottom(0); page_box.set_right(width); page_box.set_top(height); // Filter_blobs sets up the TO_BLOCKs the same as find_components does. filter_blobs(page_box.topright(), &port_blocks, true); } TO_BLOCK_IT to_block_it(&port_blocks); ASSERT_HOST(!port_blocks.empty()); TO_BLOCK* to_block = to_block_it.data(); if (pageseg_mode <= PSM_SINGLE_BLOCK || to_block->line_size < 2) { // For now, AUTO, SINGLE_COLUMN and SINGLE_BLOCK all map to the old // textord. The difference is the number of blocks and how the are made. textord_page(page_box.topright(), blocks, &land_blocks, &port_blocks, this); } else { // SINGLE_LINE, SINGLE_WORD and SINGLE_CHAR all need a single row. float gradient = make_single_row(page_box.topright(), to_block, &port_blocks, this); if (pageseg_mode == PSM_SINGLE_LINE) { // SINGLE_LINE uses the old word maker on the single line. make_words(page_box.topright(), gradient, blocks, &land_blocks, &port_blocks, this); } else { // SINGLE_WORD and SINGLE_CHAR cram all the blobs into a // single word, and in SINGLE_CHAR mode, all the outlines // go in a single blob. make_single_word(pageseg_mode == PSM_SINGLE_CHAR, to_block->get_rows(), to_block->block->row_list()); } } return 0; }
main(int argc, char **argv) { char *text; l_int32 w, h, d, wpl, count, npages, color, format, bps, spp, iscmap; FILE *fp; PIX *pix; PIXCMAP *cmap; char *filein; static char mainName[] = "fileinfo"; if (argc != 2) exit(ERROR_INT(" Syntax: fileinfo filein", mainName, 1)); filein = argv[1]; l_pngSetStrip16To8(0); /* to preserve 16 bpp if format is png */ /* Read the full image */ if ((pix = pixRead(filein)) == NULL) exit(ERROR_INT("image not returned from file", mainName, 1)); format = pixGetInputFormat(pix); pixGetDimensions(pix, &w, &h, &d); wpl = pixGetWpl(pix); fprintf(stderr, "Reading the full image:\n"); fprintf(stderr, " Input image format type: %s\n", ImageFileFormatExtensions[format]); fprintf(stderr, " w = %d, h = %d, d = %d, wpl = %d\n", w, h, d, wpl); fprintf(stderr, " xres = %d, yres = %d\n", pixGetXRes(pix), pixGetYRes(pix)); text = pixGetText(pix); if (text) /* not null */ fprintf(stderr, " Text: %s\n", text); cmap = pixGetColormap(pix); if (cmap) { pixcmapHasColor(cmap, &color); if (color) fprintf(stderr, " Colormap exists and has color values:"); else fprintf(stderr, " Colormap exists and has only gray values:"); pixcmapWriteStream(stderr, pixGetColormap(pix)); } else fprintf(stderr, " Colormap does not exist.\n"); if (format == IFF_TIFF || format == IFF_TIFF_G4 || format == IFF_TIFF_G3 || format == IFF_TIFF_PACKBITS) { fprintf(stderr, " Tiff header information:\n"); fp = lept_fopen(filein, "rb"); tiffGetCount(fp, &npages); lept_fclose(fp); if (npages == 1) fprintf(stderr, " One page in file\n"); else fprintf(stderr, " %d pages in file\n", npages); fprintTiffInfo(stderr, filein); } if (d == 1) { pixCountPixels(pix, &count, NULL); fprintf(stderr, " 1 bpp: pixel ratio ON/OFF = %6.3f\n", (l_float32)count / (l_float32)(pixGetWidth(pix) * pixGetHeight(pix))); } pixDestroy(&pix); /* Test pixReadHeader() */ if (pixReadHeader(filein, &format, &w, &h, &bps, &spp, &iscmap)) { fprintf(stderr, "Failure to read header!\n"); return 1; } fprintf(stderr, "Reading just the header:\n"); fprintf(stderr, " Input image format type: %s\n", ImageFileFormatExtensions[format]); fprintf(stderr, " w = %d, h = %d, d = %d, bps = %d, spp = %d, iscmap = %d\n", w, h, d, bps, spp, iscmap); return 0; }
/*! * pixWriteStreamJpeg() * * Input: stream * pix (8 or 32 bpp) * quality (1 - 100; 75 is default value; 0 is also default) * progressive (0 for baseline sequential; 1 for progressive) * Return: 0 if OK, 1 on error * * Notes: * (1) Under the covers, the library transforms rgb to a * luminence-chromaticity triple, each component of which is * also 8 bits, and compresses that. It uses 2 Huffman tables, * a higher resolution one (with more quantization levels) * for luminosity and a lower resolution one for the chromas. * (2) Progressive encoding gives better compression, at the * expense of slower encoding and decoding. * (3) Standard chroma subsampling is 2x2 on both the U and V * channels. For highest quality, use no subsampling. This * option is set by l_jpegSetNoChromaSampling(1). * (4) There are three possibilities: * * Grayscale image, no colormap: compress as 8 bpp image. * * rgb full color image: copy each line into the color * line buffer, and compress as three 8 bpp images. * * 8 bpp colormapped image: convert each line to three * 8 bpp line images in the color line buffer, and * compress as three 8 bpp images. * (5) The only valid pixel depths in leptonica are 1, 2, 4, 8, 16 * and 32 bpp. However, it is possible, and in some cases desirable, * to write out a jpeg file using an rgb pix that has 24 bpp. * This can be created by appending the raster data for a 24 bpp * image (with proper scanline padding) directly to a 24 bpp * pix that was created without a data array. See note in * pixWriteStreamPng() for an example. */ l_int32 pixWriteStreamJpeg(FILE *fp, PIX *pix, l_int32 quality, l_int32 progressive) { l_uint8 byteval; l_int32 xres, yres; l_int32 i, j, k; l_int32 w, h, d, wpl, spp, colorflg, rowsamples; l_int32 *rmap, *gmap, *bmap; l_uint32 *ppixel, *line, *data; JSAMPROW rowbuffer; PIXCMAP *cmap; struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; const char *text; PROCNAME("pixWriteStreamJpeg"); if (!fp) return ERROR_INT("stream not open", procName, 1); if (!pix) return ERROR_INT("pix not defined", procName, 1); rewind(fp); if (setjmp(jpeg_jmpbuf)) { FREE(rowbuffer); if (colorflg == 1) { FREE(rmap); FREE(gmap); FREE(bmap); } return ERROR_INT("internal jpeg error", procName, 1); } rowbuffer = NULL; rmap = NULL; gmap = NULL; bmap = NULL; pixGetDimensions(pix, &w, &h, &d); if (d != 8 && d != 24 && d != 32) return ERROR_INT("bpp must be 8, 24 or 32", procName, 1); if (quality <= 0) quality = 75; /* default */ if (d == 32 || d == 24) colorflg = 2; /* rgb; no colormap */ else if ((cmap = pixGetColormap(pix)) == NULL) colorflg = 0; /* 8 bpp grayscale; no colormap */ else { colorflg = 1; /* 8 bpp; colormap */ pixcmapToArrays(cmap, &rmap, &gmap, &bmap); } cinfo.err = jpeg_std_error(&jerr); jerr.error_exit = jpeg_error_do_not_exit; /* catch error; do not exit! */ jpeg_create_compress(&cinfo); jpeg_stdio_dest(&cinfo, fp); cinfo.image_width = w; cinfo.image_height = h; if (colorflg == 0) { cinfo.input_components = 1; cinfo.in_color_space = JCS_GRAYSCALE; } else { /* colorflg == 1 or 2 */ cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; } jpeg_set_defaults(&cinfo); /* Setting optimize_coding to TRUE seems to improve compression * by approx 2-4 percent, and increases comp time by approx 20%. */ cinfo.optimize_coding = FALSE; xres = pixGetXRes(pix); yres = pixGetYRes(pix); if ((xres != 0) && (yres != 0)) { cinfo.density_unit = 1; /* designates pixels per inch */ cinfo.X_density = xres; cinfo.Y_density = yres; } /* Set the quality and progressive parameters */ jpeg_set_quality(&cinfo, quality, TRUE); if (progressive) { jpeg_simple_progression(&cinfo); } /* Set the chroma subsampling parameters. This is done in * YUV color space. The Y (intensity) channel is never subsampled. * The standard subsampling is 2x2 on both the U and V channels. * Notation on this is confusing. For a nice illustrations, see * http://en.wikipedia.org/wiki/Chroma_subsampling * The standard subsampling is written as 4:2:0. * We allow high quality where there is no subsampling on the * chroma channels: denoted as 4:4:4. */ if (var_JPEG_NO_CHROMA_SAMPLING == 1) { cinfo.comp_info[0].h_samp_factor = 1; cinfo.comp_info[0].v_samp_factor = 1; cinfo.comp_info[1].h_samp_factor = 1; cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; cinfo.comp_info[2].v_samp_factor = 1; } jpeg_start_compress(&cinfo, TRUE); if ((text = pixGetText(pix))) { jpeg_write_marker(&cinfo, JPEG_COM, (const JOCTET *)text, strlen(text)); } /* Allocate row buffer */ spp = cinfo.input_components; rowsamples = spp * w; if ((rowbuffer = (JSAMPROW)CALLOC(sizeof(JSAMPLE), rowsamples)) == NULL) return ERROR_INT("calloc fail for rowbuffer", procName, 1); data = pixGetData(pix); wpl = pixGetWpl(pix); for (i = 0; i < h; i++) { line = data + i * wpl; if (colorflg == 0) { /* 8 bpp gray */ for (j = 0; j < w; j++) rowbuffer[j] = GET_DATA_BYTE(line, j); } else if (colorflg == 1) { /* 8 bpp colormapped */ for (j = 0; j < w; j++) { byteval = GET_DATA_BYTE(line, j); rowbuffer[3 * j + COLOR_RED] = rmap[byteval]; rowbuffer[3 * j + COLOR_GREEN] = gmap[byteval]; rowbuffer[3 * j + COLOR_BLUE] = bmap[byteval]; } } else { /* colorflg == 2 */ if (d == 24) { /* See note 4 above; special case of 24 bpp rgb */ jpeg_write_scanlines(&cinfo, (JSAMPROW *)&line, 1); } else { /* standard 32 bpp rgb */ ppixel = line; for (j = k = 0; j < w; j++) { rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED); rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN); rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE); ppixel++; } } } if (d != 24) jpeg_write_scanlines(&cinfo, &rowbuffer, 1); } jpeg_finish_compress(&cinfo); FREE(rowbuffer); if (colorflg == 1) { FREE(rmap); FREE(gmap); FREE(bmap); } jpeg_destroy_compress(&cinfo); return 0; }
main(int argc, char **argv) { l_int32 w, h, d, wpl, count, i, format, xres, yres; FILE *fp; PIX *pix, *pixt1, *pixt2; PIXCMAP *cmap; char *filein; char *fileout = NULL; static char mainName[] = "iotest"; if (argc != 2 && argc != 3) exit(ERROR_INT(" Syntax: iotest filein [fileout]", mainName, 1)); filein = argv[1]; if (argc == 3) fileout = argv[2]; #if 1 if ((pix = pixRead(filein)) == NULL) exit(ERROR_INT("pix not made", mainName, 1)); #else if ((pix = pixReadJpeg(filein, 0, 4, NULL)) == NULL) exit(ERROR_INT("pix not made", mainName, 1)); #endif pixGetDimensions(pix, &w, &h, &d); wpl = pixGetWpl(pix); fprintf(stderr, "w = %d, h = %d, d = %d, wpl = %d\n", w, h, d, wpl); xres = pixGetXRes(pix); yres = pixGetXRes(pix); if (xres != 0 && yres != 0) fprintf(stderr, "xres = %d, yres = %d\n", xres, yres); if (pixGetColormap(pix)) { /* Write and read back the colormap */ pixcmapWriteStream(stderr, pixGetColormap(pix)); fp = lept_fopen("/tmp/junkcmap1", "wb"); pixcmapWriteStream(fp, pixGetColormap(pix)); lept_fclose(fp); fp = lept_fopen("/tmp/junkcmap1", "rb"); cmap = pixcmapReadStream(fp); lept_fclose(fp); fp = lept_fopen("/tmp/junkcmap2", "wb"); pixcmapWriteStream(fp, cmap); lept_fclose(fp); pixcmapDestroy(&cmap); /* Remove and regenerate colormap */ pixt1 = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); if (pixGetDepth(pixt1) == 8) { fprintf(stderr, "Colormap: represents grayscale image\n"); pixt2 = pixConvertGrayToColormap(pixt1); } else { /* 32 bpp */ fprintf(stderr, "Colormap: represents RGB image\n"); pixt2 = pixConvertRGBToColormap(pixt1, 1); } pixWrite("/tmp/junkpixt2.png", pixt2, IFF_PNG); pixDestroy(&pixt1); pixDestroy(&pixt2); } else { fprintf(stderr, "no colormap\n"); } format = pixGetInputFormat(pix); fprintf(stderr, "Input format extension: %s\n", ImageFileFormatExtensions[format]); if (format == IFF_JFIF_JPEG) fprintf(stderr, "Jpeg comment: %s\n", pixGetText(pix)); if (d == 1) { pixCountPixels(pix, &count, NULL); fprintf(stderr, "pixel ratio ON/OFF = %6.3f\n", (l_float32)count / (l_float32)(pixGetWidth(pix) * pixGetHeight(pix))); } if (argc == 3) { #if 1 d = pixGetDepth(pix); if (d == 16 || d < 8 || pixGetColormap(pix)) pixWrite(fileout, pix, IFF_PNG); else pixWriteJpeg(fileout, pix, 75, 0); #elif 0 pixWrite(fileout, pix, IFF_BMP); #elif 0 pixWrite(fileout, pix, IFF_PNG); #elif 0 pixWrite(fileout, pix, IFF_TIFF); fprintTiffInfo(stderr, fileout); #elif 0 pixWrite(fileout, pix, IFF_TIFF_PACKBITS); fprintTiffInfo(stderr, fileout); #elif 0 pixWrite(fileout, pix, IFF_TIFF_G3); fprintTiffInfo(stderr, fileout); #elif 0 pixWrite(fileout, pix, IFF_TIFF_G4); fprintTiffInfo(stderr, fileout); #elif 0 pixWrite(fileout, pix, IFF_JFIF_JPEG); #elif 0 pixWriteJpeg(fileout, pix, 75, 0); #elif 0 pixWrite(fileout, pix, IFF_PNM); #elif 0 pixWrite(fileout, pix, IFF_PS); #endif } pixDestroy(&pix); #if 0 /* test tiff header reader */ { l_int32 w, h, bps, spp, res, cmap; if (readHeaderTiff(filein, 0, &w, &h, &bps, &spp, &res, &cmap) == 0) fprintf(stderr, "w = %d, h = %d, bps = %d, spp = %d, res = %d, cmap = %d\n", w, h, bps, spp, res, cmap); } #endif return 0; }