Leptonica 1.68
C Image Processing Library
|
00001 /*====================================================================* 00002 - Copyright (C) 2001 Leptonica. All rights reserved. 00003 - This software is distributed in the hope that it will be 00004 - useful, but with NO WARRANTY OF ANY KIND. 00005 - No author or distributor accepts responsibility to anyone for the 00006 - consequences of using this software, or for whether it serves any 00007 - particular purpose or works at all, unless he or she says so in 00008 - writing. Everyone is granted permission to copy, modify and 00009 - redistribute this source code, for commercial or non-commercial 00010 - purposes, with the following restrictions: (1) the origin of this 00011 - source code must not be misrepresented; (2) modified versions must 00012 - be plainly marked as such; and (3) this notice may not be removed 00013 - or altered from any source or modified source distribution. 00014 - Author: krish@google.com (krish Chaudhury) 00015 *====================================================================*/ 00016 00017 /* 00018 * webpio.c 00019 * 00020 * Read WebP from file 00021 * PIX *pixReadStreamWebP() 00022 * l_int32 readHeaderWebP() 00023 * 00024 * Write WebP to file 00025 * l_int32 pixWriteWebP() [ special top level ] 00026 * l_int32 pixWriteStreamWebP() 00027 * 00028 * Write WebP to file with target psnr 00029 * l_int32 pixWriteWebPwithTargetPSNR 00030 * 00031 */ 00032 00033 #include <math.h> 00034 #include "allheaders.h" 00035 00036 #ifdef HAVE_CONFIG_H 00037 #include "config_auto.h" 00038 #endif /* HAVE_CONFIG_H */ 00039 00040 /* --------------------------------------------*/ 00041 #if HAVE_LIBWEBP /* defined in environ.h */ 00042 /* --------------------------------------------*/ 00043 #include "webp/decode.h" 00044 #include "webp/encode.h" 00045 00046 /*---------------------------------------------------------------------* 00047 * Reading WebP * 00048 *---------------------------------------------------------------------*/ 00049 00050 /*! 00051 * pixReadStreamWebP() 00052 * 00053 * Input: stream corresponding to WebP image 00054 * Return: pix (32 bpp), or null on error 00055 * 00056 * Notes: 00057 * (1) Use 'free', and not leptonica's 'FREE', for all heap data 00058 * that is returned from the WebP library. 00059 */ 00060 PIX * 00061 pixReadStreamWebP(FILE *fp) 00062 { 00063 l_uint8 *filedata; 00064 l_uint8 *out = NULL; 00065 l_int32 w, h, wpl, stride; 00066 l_uint32 *data; 00067 size_t nbytes, sz, out_size; 00068 PIX *pix; 00069 00070 PROCNAME("pixReadStreamWebP"); 00071 00072 if (!fp) 00073 return (PIX *)ERROR_PTR("fp not defined", procName, NULL); 00074 00075 /* Read data from file and decode into Y,U,V arrays */ 00076 rewind(fp); 00077 if ((filedata = l_binaryReadStream(fp, &nbytes)) == NULL) 00078 return (PIX *)ERROR_PTR("filedata not read", procName, NULL); 00079 00080 sz = WebPGetInfo(filedata, nbytes, &w, &h); 00081 if (sz == 0) { 00082 FREE(filedata); 00083 return (PIX *)ERROR_PTR("Bad WebP: Can't find size", procName, NULL); 00084 } 00085 00086 /* Write from Y,U,V arrays to pix data */ 00087 pix = pixCreate(w, h, 32); 00088 data = pixGetData(pix); 00089 wpl = pixGetWpl(pix); 00090 stride = wpl * 4; 00091 out_size = stride * h; 00092 out = WebPDecodeRGBAInto(filedata, nbytes, (uint8_t *)data, out_size, 00093 stride); 00094 FREE(filedata); 00095 if (out == NULL) { 00096 pixDestroy(&pix); 00097 return (PIX *)ERROR_PTR("WebP decode failed", procName, NULL); 00098 } 00099 pixEndianByteSwap(pix); 00100 00101 return pix; 00102 } 00103 00104 00105 /*! 00106 * readHeaderWebP() 00107 * 00108 * Input: filename 00109 * &width (<return>) 00110 * &height (<return>) 00111 * Return: 0 if OK, 1 on error 00112 */ 00113 l_int32 00114 readHeaderWebP(const char *filename, 00115 l_int32 *pwidth, 00116 l_int32 *pheight) 00117 { 00118 l_uint8 data[10]; 00119 l_int32 sz; 00120 FILE *fp; 00121 00122 PROCNAME("readHeaderWebP"); 00123 00124 if (!filename) 00125 return ERROR_INT("filename not defined", procName, 1); 00126 if (!pwidth || !pheight) 00127 return ERROR_INT("input ptr(s) not defined", procName, 1); 00128 if ((fp = fopenReadStream(filename)) == NULL) 00129 return ERROR_INT("image file not found", procName, 1); 00130 if (fread((char *)data, 1, 10, fp) != 10) 00131 return ERROR_INT("failed to read 10 bytes of file", procName, 1); 00132 00133 sz = WebPGetInfo(data, 10, pwidth, pheight); 00134 if (sz == 0) 00135 return ERROR_INT("Bad WebP: Can't find size", procName, 1); 00136 00137 fclose(fp); 00138 return 0; 00139 } 00140 00141 00142 /*---------------------------------------------------------------------* 00143 * Writing WebP * 00144 *---------------------------------------------------------------------*/ 00145 /*! 00146 * pixWriteWebP() 00147 * 00148 * Input: filename 00149 * pixs 00150 * quality (1 - 100; 75 is default) 00151 * Return: 0 if OK, 1 on error 00152 */ 00153 l_int32 00154 pixWriteWebP(const char *filename, 00155 PIX *pixs, 00156 l_int32 quality) 00157 { 00158 FILE *fp; 00159 00160 PROCNAME("pixWriteWebP"); 00161 00162 if (!pixs) 00163 return ERROR_INT("pixs not defined", procName, 1); 00164 if (!filename) 00165 return ERROR_INT("filename not defined", procName, 1); 00166 00167 if ((fp = fopenWriteStream(filename, "wb+")) == NULL) 00168 return ERROR_INT("stream not opened", procName, 1); 00169 00170 if (pixWriteStreamWebP(fp, pixs, quality) != 0) { 00171 fclose(fp); 00172 return ERROR_INT("pixs not written to stream", procName, 1); 00173 } 00174 00175 fclose(fp); 00176 return 0; 00177 } 00178 00179 00180 /*! 00181 * pixWriteStreampWebP() 00182 * 00183 * Input: stream 00184 * pix (all depths) 00185 * quality (1 - 100; 75 is default) 00186 * Return: 0 if OK, 1 on error 00187 * 00188 * Notes: 00189 * (1) webp only encodes rgb images, so the input image is converted to rgb 00190 * if necessary. 00191 */ 00192 l_int32 00193 pixWriteStreamWebP(FILE *fp, 00194 PIX *pixs, 00195 l_int32 quality) 00196 { 00197 l_int32 w, h, d, wpl, stride, ret; 00198 l_uint8 *filedata = NULL; 00199 l_uint32 *data; 00200 size_t nbytes; 00201 PIX *pix, *pixt, *pix32; 00202 00203 PROCNAME("pixWriteStreamWebP"); 00204 00205 if (!fp) 00206 return ERROR_INT("stream not open", procName, 1); 00207 if (!pixs) 00208 return ERROR_INT("pixs not defined", procName, 1); 00209 00210 if (quality < 1) 00211 quality = 1; 00212 if (quality > 100) 00213 quality = 100; 00214 00215 if ((pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR)) == NULL) 00216 return ERROR_INT("failure to remove color map", procName, 1); 00217 pix = pixEndianByteSwapNew(pixt); 00218 pixDestroy(&pixt); 00219 pixGetDimensions(pix, &w, &h, &d); 00220 00221 /* Convert to rgb if not 32 bpp */ 00222 if (d != 32) { 00223 if ((pix32 = pixConvertTo32(pix)) != NULL) { 00224 pixDestroy(&pix); 00225 pix = pix32; 00226 d = pixGetDepth(pix); 00227 } 00228 } 00229 00230 wpl = pixGetWpl(pix); 00231 data = pixGetData(pix); 00232 if (d != 32 || w <= 0 || h <= 0 || wpl <= 0 || !data) { 00233 pixDestroy(&pix); 00234 return ERROR_INT("bad or empty input pix", procName, 1); 00235 } 00236 00237 stride = wpl * 4; 00238 nbytes = WebPEncodeRGBA((uint8_t *)data, w, h, stride, quality, &filedata); 00239 00240 if (nbytes == 0) { 00241 if (filedata) free(filedata); 00242 pixDestroy(&pix); 00243 return ERROR_INT("WebPEncode failed", procName, 1); 00244 } 00245 00246 rewind(fp); 00247 00248 ret = (fwrite(filedata, 1, nbytes, fp) != nbytes); 00249 free(filedata); 00250 pixDestroy(&pix); 00251 if (ret) 00252 return ERROR_INT("Write error", procName, 1); 00253 00254 return 0; 00255 } 00256 00257 00258 /*! 00259 * pixWriteWebPwithTargetPSNR() 00260 * 00261 * Input: filename 00262 * pix (all depths) 00263 * target_psnr (target psnr to control the quality [1 ... 100]) 00264 * pquality (<optional return> final quality value used to obtain 00265 * the target_psnr; can be null) 00266 * Return: 0 if OK, 1 on error 00267 * 00268 * Notes: 00269 * (1) The parameter to control quality while encoding WebP is quality. 00270 * This function does a line search over the quality values between 00271 * MIN_QUALITY and MAX_QUALITY to achieve the target PSNR as close as 00272 * possible. 00273 */ 00274 l_int32 00275 pixWriteWebPwithTargetPSNR(const char *filename, 00276 PIX *pixs, 00277 l_float64 target_psnr, 00278 l_int32 *pquality) 00279 { 00280 l_uint8 *filedata = NULL; 00281 l_uint8 *tmp_filedata = NULL; 00282 l_int32 MIN_QUALITY = 1; /* min allowed value of quality */ 00283 l_int32 MAX_QUALITY = 100; /* max allowed value of quality */ 00284 l_int32 w, h, d, wpl, stride, ret; 00285 l_int32 quality, delta_quality, quality_test, accept; 00286 l_uint32 *data; 00287 l_float64 psnr, psnr_test; 00288 size_t nbytes, tmp_nbytes = 0; 00289 FILE *fp; 00290 PIX *pix, *pix32; 00291 00292 PROCNAME("pixWriteWebPwithTargetPSNR"); 00293 00294 if (!filename) 00295 return ERROR_INT("filename not defined", procName, 1); 00296 if (!pixs) 00297 return ERROR_INT("pixs not defined", procName, 1); 00298 if (target_psnr <= 0 || target_psnr >= 100) 00299 return ERROR_INT("Target psnr out of range", procName, 1); 00300 00301 if ((pix = pixRemoveColormap(pixs, REMOVE_CMAP_TO_FULL_COLOR)) == NULL) 00302 return ERROR_INT("cannot remove color map", procName, 1); 00303 pixGetDimensions(pix, &w, &h, &d); 00304 00305 /* Convert to rgb if not 32 bpp */ 00306 if (d != 32) { 00307 if ((pix32 = pixConvertTo32(pix)) != NULL) { 00308 pixDestroy(&pix); 00309 pix = pix32; 00310 d = pixGetDepth(pix); 00311 } 00312 } 00313 00314 wpl = pixGetWpl(pix); 00315 data = pixGetData(pix); 00316 if (d != 32 || w <= 0 || h <= 0 || wpl <= 0 || !data) { 00317 pixDestroy(&pix); 00318 return ERROR_INT("bad or empty input pix", procName, 1); 00319 } 00320 00321 /* Set the initial value of the Quality parameter. In each iteration 00322 * it will then increase or decrease the Quality value, based on 00323 * whether the achieved psnr is higher or lower than the target_psnr */ 00324 quality = 75; 00325 stride = wpl * 4; 00326 00327 nbytes = WebPEncodeRGBA((uint8_t *)data, w, h, stride, quality, &filedata); 00328 00329 if (nbytes == 0) { 00330 if (filedata) free(filedata); 00331 pixDestroy(&pix); 00332 return ERROR_INT("WebPEncode failed", procName, 1); 00333 } 00334 00335 /* Rationale about the delta_quality being limited: we expect optimal 00336 * quality to be not too far from target quality in practice. 00337 * So instead of a full dichotomy for the whole range we cap 00338 * |delta_quality| to only explore quickly around the starting value 00339 * and maximize the return in investment. */ 00340 delta_quality = (psnr > target_psnr) ? 00341 L_MAX((MAX_QUALITY - quality) / 4, 1) : 00342 L_MIN((MIN_QUALITY - quality) / 4, -1); 00343 00344 while (delta_quality != 0) { 00345 /* Advance quality and clip to valid range */ 00346 quality_test = L_MIN(L_MAX(quality + delta_quality, MIN_QUALITY), 00347 MAX_QUALITY); 00348 /* Re-adjust delta value after Quality-clipping. */ 00349 delta_quality = quality_test - quality; 00350 00351 tmp_nbytes = WebPEncodeRGBA((uint8_t *)data, w, h, stride, quality_test, 00352 &tmp_filedata); 00353 if (tmp_nbytes == 0) { 00354 free(filedata); 00355 if (tmp_filedata) free(tmp_filedata); 00356 pixDestroy(&pix); 00357 return ERROR_INT("WebPEncode failed", procName, 1); 00358 } 00359 00360 /* Accept or reject new settings */ 00361 accept = (psnr_test > target_psnr) ^ (delta_quality < 0); 00362 if (accept) { 00363 free(filedata); 00364 filedata = tmp_filedata; 00365 nbytes = tmp_nbytes; 00366 quality = quality_test; 00367 psnr = psnr_test; 00368 } 00369 else { 00370 delta_quality /= 2; 00371 free(tmp_filedata); 00372 } 00373 } 00374 if (pquality) *pquality = quality; 00375 00376 if ((fp = fopenWriteStream(filename, "wb+")) == NULL) { 00377 free(filedata); 00378 pixDestroy(&pix); 00379 return ERROR_INT("stream not opened", procName, 1); 00380 } 00381 00382 ret = (fwrite(filedata, 1, nbytes, fp) != nbytes); 00383 fclose(fp); 00384 free(filedata); 00385 pixDestroy(&pix); 00386 if (ret) 00387 return ERROR_INT("Write error", procName, 1); 00388 00389 return 0; 00390 } 00391 00392 /* --------------------------------------------*/ 00393 #endif /* HAVE_LIBWEBP */ 00394 /* --------------------------------------------*/