免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 20935 | 回复: 4
打印 上一主题 下一主题

[C] 读取PNG文件实现 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-01-28 19:52 |只看该作者 |倒序浏览
下面文章不是非原创,觉得不错,转进来。
========================================================================================================================================================
最近研究的如何读取和写入png文件。下面就把我的心得和大家分享一下了。
PNG是20世纪90年代中期开始开发的图像文件存储格式,其目的是企图替代GIF和TIFF文件格式,同时增加一些GIF文件格式所不具备的特性。流式网络图形格式(Portable Network Graphic Format,PNG)名称来源于非官方的“PNG's Not GIF”,是一种位图文件(bitmap file)存储格式,读成“ping”。PNG用来存储灰度图像时,灰度图像的深度可多到16位,存储彩色图像时,彩色图像的深度可多到48位,并且还可存储多到16位的α通道数据。PNG使用从LZ77派生的无损数据压缩算法。
PNG的格式具体是如何的。在此就不详细说明了。在网上搜一下就可以看到。
PNG图像格式文件(或者称为数据流)由一个8字节的PNG文件署名(PNG file signature)域和按照特定结构组织的3个以上的数据块(chunk)组成。 PNG定义了两种类型的数据块,一种是称为关键数据块(critical chunk),这是标准的数据块,另一种叫做辅助数据块(ancillary chunks),这是可选的数据块。关键数据块定义了4个标准数据块,每个PNG文件都必须包含它们,PNG读写软件也都必须要支持这些数据块。虽然PNG文件规范没有要求PNG编译码器对可选数据块进行编码和译码,但规范提倡支持可选数据块。
我们所关心的重点是,PNG的格式这么麻烦,我们如何才能在最短时间里完成老板的要求,实现PNG文件的读入和写入操作呢。我保证大家看完我的文章,不到半天就能把程序编好搞定,满意的给老板交差了。
好了,言归正传。我是如何读写PNG文件的?
libpng 是一套免费的、公开源代码的程序库,支持对 PNG 图形文件的创建、读写等操作。可以到www.libpng.org 上下载源代码。libpng 使用 zlib 程序库作为压缩引擎,zlib 也是著名的 gzip (GNU zip) 所采用的压缩引擎。zlib 是通用的压缩库,提供了一套 in-memory 压缩和解压函数,并能检测解压出来的数据的完整性(integrity)。zlib 也支持读写 gzip (.gz) 格式的文件这些都是免费和公开的哦。
在下载了pbglib,并且要下载zlib,如何才能加到代码中呢?以VC6.为例来说明。其他CBuild等编译器相信也一样的。
比如我们从网上下载了一个lpng1210.zip和zlib-1.2.3.tar.tar。首先把他们解压。在lpng的目录中寻找projects/visualc6/libpng.dsw。并在zlib中寻找zlib-1.2.3/projects/visualc6/zlib.dsw。
用vc打开libpng工程文件,把zlib的工程文件也添加进去。设置好在setting中设置好的zlib.lib和pbg.lib的输出路径,以及include路径(这个简单方法相信大家都明白吧)。运行后,就生成了两个静态库了。
lib做好以后,就可以将lib加到我们要做的工程中去。在link中设置好,并将png.h和zlib.h都加进去。这样就可以正式的使用libpng了!相关内容还可以参考一个帖子爱蓝天的网络日记。
下面将将到重点内容了。如何用pnglib实现对文件的读写?我们打开pnglib下面的一个example.c. 注释里面写的很清楚了。
/* Read a PNG file. You may want to return an error code if the read
* fails (depending upon the failure). There are two "prototypes" given
* here - one where we are given the filename, and we need to open the
* file, and the other where we are given an open file (possibly with
* some or all of the magic bytes read - see comments above).
*/
#ifdef open_file /* prototype 1 */
void read_png(char *file_name) /* We need to open the file */
{
定义两个很重要的pngwen文件指针。png_info里面放的是pngchuck和png的数据。
png_structp png_ptr;
png_infop info_ptr;
这些是读出图像需要的参数,图像高。宽、深度、图像类型、交错类型。
unsigned int sig_read = 0;
png_uint_32 width, height;
int bit_depth, color_type, interlace_type;
FILE *fp;
打开文件,把文件指针给png_prt
if ((fp = fopen(file_name, "rb") == NULL)
return (ERROR);
#else no_open_file /* prototype 2 */
void read_png(FILE *fp, unsigned int sig_read) /* file is already open */
{
png_structp png_ptr;
png_infop info_ptr;
png_uint_32 width, height;
int bit_depth, color_type, interlace_type;
#endif no_open_file /* only use one prototype! */

/* Create and initialize the png_struct with the desired error handler
* functions. If you want to use the default stderr and longjump method,
* you can supply NULL for the last three parameters. We also supply the
* the compiler header file version, so that we know if the application
* was compiled with a compatible version of the library. REQUIRED
*/
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
png_voidp user_error_ptr, user_error_fn, user_warning_fn);

if (png_ptr == NULL)
{
fclose(fp);
return (ERROR);
}

/* Allocate/initialize the memory for image information. REQUIRED. */
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL)
{
fclose(fp);
png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
return (ERROR);
}

/* Set error handling if you are using the setjmp/longjmp method (this is
* the normal method of doing things with libpng). REQUIRED unless you
* set up your own error handlers in the png_create_read_struct() earlier.
*/

if (setjmp(png_jmpbuf(png_ptr)))
{
/* Free all of the memory associated with the png_ptr and info_ptr */
png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
fclose(fp);
/* If we get here, we had a problem reading the file */
return (ERROR);
}

/* One of the following I/O initialization methods is REQUIRED */
#ifdef streams /* PNG file I/O method 1 */
/* Set up the input control if you are using standard C streams */
png_init_io(png_ptr, fp);

#else no_streams /* PNG file I/O method 2 */
/* If you are using replacement read functions, instead of calling
* png_init_io() here you would call:
*/
png_set_read_fn(png_ptr, (void *)user_io_ptr, user_read_fn);
/* where user_io_ptr is a structure you want available to the callbacks */
#endif no_streams /* Use only one I/O method! */

/* If we have already read some of the signature */
png_set_sig_bytes(png_ptr, sig_read);

#ifdef hilevel
/*
* If you have enough memory to read in the entire image at once,
* and you need to specify only transforms that can be controlled
* with one of the PNG_TRANSFORM_* bits (this presently excludes
* dithering, filling, setting background, and doing gamma
* adjustment), then you can read the entire image (including
* pixels) into the info structure with this call:
*/
png_read_png(png_ptr, info_ptr, png_transforms, png_voidp_NULL);
#else
/* OK, you're doing it the hard way, with the lower-level functions */

/* The call to png_read_info() gives us all of the information from the
* PNG file before the first IDAT (image data chunk). REQUIRED
*/
png_read_info(png_ptr, info_ptr);

png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
&interlace_type, int_p_NULL, int_p_NULL);

/* Set up the data transformations you want. Note that these are all
* optional. Only call them if you want/need them. Many of the
* transformations only work on specific types of images, and many
* are mutually exclusive.
*/

/* tell libpng to strip 16 bit/color files down to 8 bits/color */
png_set_strip_16(png_ptr);

/* Strip alpha bytes from the input data without combining with the
* background (not recommended).
*/
png_set_strip_alpha(png_ptr);

/* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
* byte into separate bytes (useful for paletted and grayscale images).
*/
png_set_packing(png_ptr);

/* Change the order of packed pixels to least significant bit first
* (not useful if you are using png_set_packing). */
png_set_packswap(png_ptr);

/* Expand paletted colors into true RGB triplets */
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_rgb(png_ptr);

/* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth <
png_set_gray_1_2_4_to_8(png_ptr);

/* Expand paletted or RGB images with transparency to full alpha channels
* so the data will be available as RGBA quartets.
*/
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(png_ptr);

/* Set the background color to draw transparent and alpha images over.
* It is possible to set the red, green, and blue components directly
* for paletted images instead of supplying a palette index. Note that
* even if the PNG file supplies a background, you are not required to
* use it - you should use the (solid) application background if it has one.
*/

png_color_16 my_background, *image_background;

if (png_get_bKGD(png_ptr, info_ptr, &image_background))
png_set_background(png_ptr, image_background,
PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
else
png_set_background(png_ptr, &my_background,
PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);

/* Some suggestions as to how to get a screen gamma value */

/* Note that screen gamma is the display_exponent, which includes
* the CRT_exponent and any correction for viewing conditions */
if (/* We have a user-defined screen gamma value */)
{
screen_gamma = user-defined screen_gamma;
}
/* This is one way that applications share the same screen gamma value */
else if ((gamma_str = getenv("SCREEN_GAMMA") != NULL)
{
screen_gamma = atof(gamma_str);
}
/* If we don't have another value */
else
{
screen_gamma = 2.2; /* A good guess for a PC monitors in a dimly
lit room */
screen_gamma = 1.7 or 1.0; /* A good guess for Mac systems */
}

/* Tell libpng to handle the gamma conversion for you. The final call
* is a good guess for PC generated images, but it should be configurable
* by the user at run time by the user. It is strongly suggested that
* your application support gamma correction.
*/

int intent;

if (png_get_sRGB(png_ptr, info_ptr, &intent))
png_set_gamma(png_ptr, screen_gamma, 0.45455);
else
{
double image_gamma;
if (png_get_gAMA(png_ptr, info_ptr, &image_gamma))
png_set_gamma(png_ptr, screen_gamma, image_gamma);
else
png_set_gamma(png_ptr, screen_gamma, 0.45455);
}

/* Dither RGB files down to 8 bit palette or reduce palettes
* to the number of colors available on your screen.
*/
if (color_type & PNG_COLOR_MASK_COLOR)
{
int num_palette;
png_colorp palette;

/* This reduces the image to the application supplied palette */
if (/* we have our own palette */)
{
/* An array of colors to which the image should be dithered */
png_color std_color_cube[MAX_SCREEN_COLORS];

png_set_dither(png_ptr, std_color_cube, MAX_SCREEN_COLORS,
MAX_SCREEN_COLORS, png_uint_16p_NULL, 0);
}
/* This reduces the image to the palette supplied in the file */
else if (png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette))
{
png_uint_16p histogram = NULL;

png_get_hIST(png_ptr, info_ptr, &histogram);

png_set_dither(png_ptr, palette, num_palette,
max_screen_colors, histogram, 0);
}
}

/* invert monochrome files to have 0 as white and 1 as black */
png_set_invert_mono(png_ptr);

/* If you want to shift the pixel values from the range [0,255] or
* [0,65535] to the original [0,7] or [0,31], or whatever range the
* colors were originally in:
*/
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT))
{
png_color_8p sig_bit;

png_get_sBIT(png_ptr, info_ptr, &sig_bit);
png_set_shift(png_ptr, sig_bit);
}

/* flip the RGB pixels to BGR (or RGBA to BGRA) */
if (color_type & PNG_COLOR_MASK_COLOR)
png_set_bgr(png_ptr);

/* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */
png_set_swap_alpha(png_ptr);

/* swap bytes of 16 bit files to least significant byte first */
png_set_swap(png_ptr);

/* Add filler (or alpha) byte (before/after each RGB triplet) */
png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);

/* Turn on interlace handling. REQUIRED if you are not using
* png_read_image(). To see how to handle interlacing passes,
* see the png_read_row() method below:
*/
number_passes = png_set_interlace_handling(png_ptr);

/* Optional call to gamma correct and add the background to the palette
* and update info structure. REQUIRED if you are expecting libpng to
* update the palette for you (ie you selected such a transform above).
*/
png_read_update_info(png_ptr, info_ptr);

/* Allocate the memory to hold the image using the fields of info_ptr. */

/* The easiest way to read the image: */
png_bytep row_pointers[height];

for (row = 0; row < height; row++)
{
row_pointers[row] = png_malloc(png_ptr, png_get_rowbytes(png_ptr,
info_ptr));
}

/* Now it's time to read the image. One of these methods is REQUIRED */
#ifdef entire /* Read the entire image in one go */
png_read_image(png_ptr, row_pointers);

#else no_entire /* Read the image one or more scanlines at a time */
/* The other way to read images - deal with interlacing: */

for (pass = 0; pass < number_passes; pass++)
{
#ifdef single /* Read the image a single row at a time */
for (y = 0; y < height; y++)
{
png_read_rows(png_ptr, &row_pointers[y], png_bytepp_NULL, 1);
}

#else no_single /* Read the image several rows at a time */
for (y = 0; y < height; y += number_of_rows)
{
#ifdef sparkle /* Read the image using the "sparkle" effect. */
png_read_rows(png_ptr, &row_pointers[y], png_bytepp_NULL,
number_of_rows);
#else no_sparkle /* Read the image using the "rectangle" effect */
png_read_rows(png_ptr, png_bytepp_NULL, &row_pointers[y],
number_of_rows);
#endif no_sparkle /* use only one of these two methods */
}

/* if you want to display the image after every pass, do
so here */
#endif no_single /* use only one of these two methods */
}
#endif no_entire /* use only one of these two methods */

/* read rest of file, and get additional chunks in info_ptr - REQUIRED */
png_read_end(png_ptr, info_ptr);
#endif hilevel

/* At this point you have read the entire image */

/* clean up after the read, and free any memory allocated - REQUIRED */
png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);

/* close the file */
fclose(fp);

/* that's it */
return (OK);
}

/* progressively read a file */

对于interplace的文件,要想顺利的读入,要用png updateinfo,这个是很关键的一点。
写文件也是类似的,可以参考example里面的相关函数。
另外采用pnglib读写文件,最好的demo是CXImage,大家可以找出来看看。

论坛徽章:
0
2 [报告]
发表于 2013-07-18 22:16 |只看该作者
你好,请问你使用png_read_image读出的数据和bmp文件的数据一致吗?
我写的一个demo,读出的数据恰恰相反

论坛徽章:
14
巨蟹座
日期:2013-11-19 14:09:4615-16赛季CBA联赛之青岛
日期:2016-07-05 12:36:0515-16赛季CBA联赛之广东
日期:2016-06-29 11:45:542015亚冠之全北现代
日期:2015-07-22 08:09:472015年辞旧岁徽章
日期:2015-03-03 16:54:15巨蟹座
日期:2014-12-29 08:22:29射手座
日期:2014-12-05 08:20:39狮子座
日期:2014-11-05 12:33:52寅虎
日期:2014-08-13 09:01:31巳蛇
日期:2014-06-16 16:29:52技术图书徽章
日期:2014-04-15 08:44:01天蝎座
日期:2014-03-11 13:06:45
3 [报告]
发表于 2013-07-19 08:49 |只看该作者
我用OpenCV读PNG文件,但不保存为PNG文件
因为OpenCV保存的PNG体积很大,我用 XnView、ACDSee、mspaint 等比较过,就OpenCV保存的PNG特大,改其质量参数也还是特别大。

论坛徽章:
0
4 [报告]
发表于 2013-07-19 13:23 |只看该作者
哦,是libpng,ImageMagick,CxImage,FreeImage等开源项目都调用它,接口封的不错。刚看到QQ下面也有libpng.dll。

论坛徽章:
0
5 [报告]
发表于 2013-07-19 15:08 |只看该作者
gdal读写png,img , tif,等也很好使
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP