为什么 read_JPEG_file() 无法在 C 中写入图像 (libjpeg/jpeg-turbo)?
Why read_JPEG_file() fails to write image (libjpeg/jpeg-turbo) in C?
它是来自 jpeg-trubo 的 example.c 的稍微修改的代码(该代码包含错误)。
当我尝试将图像数据写入文件时失败 cinfo.next_scanline==9
它在 jpeg_write_scanlines 线上崩溃。
错误:SigSegv 错误。 cinfo.image_height 设置为 404。
阅读循环 看起来 很好。你能帮忙解决问题吗?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libjpeg-turbo\libjpeg-turbo-gcc\include\jerror.h"
#include "libjpeg-turbo\libjpeg-turbo-gcc\include\jpeglib.h"
#include <setjmp.h> // optional error recovery mechanism
//# include "conversions.h" // color conversion functions
extern JSAMPLE * image_buffer; // Points to large array of R,G,B-order data
int image_height; // Number of rows
int image_width; // Number of columns
int read_JPEG_file(char * filename, unsigned char * image_buffer);
void write_JPEG_file(char * filename, unsigned char * image_buffer, int quality);
int main()
{
char * filename1 = "source.jpg";
char * filename2 = "target.jpg";
unsigned char * image_buffer; // Final image
read_JPEG_file(filename1, image_buffer);
write_JPEG_file(filename2, image_buffer, 95);
return 0;
}
struct my_error_mgr {
struct jpeg_error_mgr pub; /* "public" fields */
jmp_buf setjmp_buffer; /* for return to caller */
};
typedef struct my_error_mgr * my_error_ptr;
METHODDEF(void) my_error_exit (j_common_ptr cinfo)
{
my_error_ptr myerr = (my_error_ptr) cinfo->err;
(*cinfo->err->output_message) (cinfo);
longjmp(myerr->setjmp_buffer, 1);
}
GLOBAL(int)
read_JPEG_file (char * filename, unsigned char * image_buffer)
{
struct jpeg_decompress_struct cinfo;
struct my_error_mgr jerr;
FILE * infile; // source file
JSAMPARRAY rows_buffer; // Output row rows_buffer
int row_stride_len; // physical row width in output rows_buffer
if ((infile = fopen(filename, "rb")) == NULL) {
fprintf(stderr, "can't open %s\n", filename);
return 0;
}
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;
if (setjmp(jerr.setjmp_buffer)) {
jpeg_destroy_decompress(&cinfo);
fclose(infile); // close the input file and return
return 0;
}
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, infile);
(void) jpeg_read_header(&cinfo, TRUE);
(void) jpeg_start_decompress(&cinfo); // no errors possible
row_stride_len = cinfo.output_width * cinfo.output_components;
rows_buffer = (*cinfo.mem->alloc_sarray)
((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride_len, 1);
size_t counter=0;
size_t raw_size = row_stride_len;
image_buffer = (unsigned char*) malloc( row_stride_len*cinfo.output_height );
while (cinfo.output_scanline < cinfo.output_height) {
(void) jpeg_read_scanlines(&cinfo, rows_buffer, 1);
memcpy(image_buffer+counter, rows_buffer[0], raw_size);
counter += row_stride_len;
}
// Save global values for later use
image_width=cinfo.image_width;
image_height=cinfo.image_height;
(void) jpeg_finish_decompress(&cinfo); // Finish decompression, no errors possible
jpeg_destroy_decompress(&cinfo); // 8) Release JPEG decompression object
fclose(infile);
return 1;
}
GLOBAL(void)
write_JPEG_file(char * filename, unsigned char * image_buffer, int quality)
{
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE * outfile; // target file
JSAMPROW rows_buffer[1]; // pointer to JSAMPLE row[s]
int row_stride_len; // physical row width in image rows_buffer
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo); // initialize the JPEG compression object
if ((outfile = fopen(filename, "wb")) == NULL) {
fprintf(stderr, "can't open %s\n", filename);
exit(1);
}
jpeg_stdio_dest(&cinfo, outfile);
cinfo.image_width = image_width;
cinfo.image_height = image_height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo); // set default compression parameters
jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
jpeg_start_compress(&cinfo, TRUE); // TRUE - write a complete interchange-JPEG file.
row_stride_len = cinfo.image_width * 3; /* JSAMPLEs per row in image_buffer */
// pass array of pointers to scan lines
while (cinfo.next_scanline < cinfo.image_height) {
rows_buffer[0] = & image_buffer[cinfo.next_scanline * row_stride_len];
(void) jpeg_write_scanlines(&cinfo, rows_buffer, 1);
}
jpeg_finish_compress(&cinfo);
fclose(outfile);
jpeg_destroy_compress(&cinfo);
}
您为 read_JPEG_file
中的图像缓冲区分配 space 并将指向它的指针存储在该函数的 image_buffer
参数中,但是当函数 returns. main
中的 image_buffer
变量是一个不同的变量,不会因调用 read_JPEG_file
而更改。由于 main
中的 image_buffer
变量从未被初始化或赋值,因此您将未初始化的值而不是有效指针传递给 write_JPEG_file
。
全局变量 image_buffer
也是一个不同的变量,声明为指向示例开头附近的 JSAMPLE
的指针。您的示例程序完全没有使用此变量。
由于您已经在使用全局变量在函数 read_JPEG_file
和 write_JPEG_file
之间传递图像的宽度和高度,因此解决您的问题的简单方法(但不一定是最好的方法)也是使用全局变量作为指向图像数据的指针。将全局变量 image_buffer
的类型从 JSAMPLE *
更改为 unsigned char *
,从 main
中删除 image_buffer
的定义,并从中删除参数 image_buffer
函数 read_JPEG_file
和 write_JPEG_file
。这将导致全局变量 image_buffer
用于存储指向图像数据的指针。
例如:
...
unsigned char * image_buffer; // Points to large array of R,G,B-order data
int image_height; // Number of rows
int image_width; // Number of columns
int read_JPEG_file(char * filename);
void write_JPEG_file(char * filename, int quality);
int main()
{
char * filename1 = "source.jpg";
char * filename2 = "target.jpg";
read_JPEG_file(filename1);
write_JPEG_file(filename2, 95);
return 0;
}
...
GLOBAL(int)
read_JPEG_file (char * filename)
{
...
}
GLOBAL(void)
write_JPEG_file(char * filename, int quality)
{
...
}
它是来自 jpeg-trubo 的 example.c 的稍微修改的代码(该代码包含错误)。
当我尝试将图像数据写入文件时失败 cinfo.next_scanline==9 它在 jpeg_write_scanlines 线上崩溃。 错误:SigSegv 错误。 cinfo.image_height 设置为 404。 阅读循环 看起来 很好。你能帮忙解决问题吗?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libjpeg-turbo\libjpeg-turbo-gcc\include\jerror.h"
#include "libjpeg-turbo\libjpeg-turbo-gcc\include\jpeglib.h"
#include <setjmp.h> // optional error recovery mechanism
//# include "conversions.h" // color conversion functions
extern JSAMPLE * image_buffer; // Points to large array of R,G,B-order data
int image_height; // Number of rows
int image_width; // Number of columns
int read_JPEG_file(char * filename, unsigned char * image_buffer);
void write_JPEG_file(char * filename, unsigned char * image_buffer, int quality);
int main()
{
char * filename1 = "source.jpg";
char * filename2 = "target.jpg";
unsigned char * image_buffer; // Final image
read_JPEG_file(filename1, image_buffer);
write_JPEG_file(filename2, image_buffer, 95);
return 0;
}
struct my_error_mgr {
struct jpeg_error_mgr pub; /* "public" fields */
jmp_buf setjmp_buffer; /* for return to caller */
};
typedef struct my_error_mgr * my_error_ptr;
METHODDEF(void) my_error_exit (j_common_ptr cinfo)
{
my_error_ptr myerr = (my_error_ptr) cinfo->err;
(*cinfo->err->output_message) (cinfo);
longjmp(myerr->setjmp_buffer, 1);
}
GLOBAL(int)
read_JPEG_file (char * filename, unsigned char * image_buffer)
{
struct jpeg_decompress_struct cinfo;
struct my_error_mgr jerr;
FILE * infile; // source file
JSAMPARRAY rows_buffer; // Output row rows_buffer
int row_stride_len; // physical row width in output rows_buffer
if ((infile = fopen(filename, "rb")) == NULL) {
fprintf(stderr, "can't open %s\n", filename);
return 0;
}
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;
if (setjmp(jerr.setjmp_buffer)) {
jpeg_destroy_decompress(&cinfo);
fclose(infile); // close the input file and return
return 0;
}
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, infile);
(void) jpeg_read_header(&cinfo, TRUE);
(void) jpeg_start_decompress(&cinfo); // no errors possible
row_stride_len = cinfo.output_width * cinfo.output_components;
rows_buffer = (*cinfo.mem->alloc_sarray)
((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride_len, 1);
size_t counter=0;
size_t raw_size = row_stride_len;
image_buffer = (unsigned char*) malloc( row_stride_len*cinfo.output_height );
while (cinfo.output_scanline < cinfo.output_height) {
(void) jpeg_read_scanlines(&cinfo, rows_buffer, 1);
memcpy(image_buffer+counter, rows_buffer[0], raw_size);
counter += row_stride_len;
}
// Save global values for later use
image_width=cinfo.image_width;
image_height=cinfo.image_height;
(void) jpeg_finish_decompress(&cinfo); // Finish decompression, no errors possible
jpeg_destroy_decompress(&cinfo); // 8) Release JPEG decompression object
fclose(infile);
return 1;
}
GLOBAL(void)
write_JPEG_file(char * filename, unsigned char * image_buffer, int quality)
{
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE * outfile; // target file
JSAMPROW rows_buffer[1]; // pointer to JSAMPLE row[s]
int row_stride_len; // physical row width in image rows_buffer
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo); // initialize the JPEG compression object
if ((outfile = fopen(filename, "wb")) == NULL) {
fprintf(stderr, "can't open %s\n", filename);
exit(1);
}
jpeg_stdio_dest(&cinfo, outfile);
cinfo.image_width = image_width;
cinfo.image_height = image_height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo); // set default compression parameters
jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
jpeg_start_compress(&cinfo, TRUE); // TRUE - write a complete interchange-JPEG file.
row_stride_len = cinfo.image_width * 3; /* JSAMPLEs per row in image_buffer */
// pass array of pointers to scan lines
while (cinfo.next_scanline < cinfo.image_height) {
rows_buffer[0] = & image_buffer[cinfo.next_scanline * row_stride_len];
(void) jpeg_write_scanlines(&cinfo, rows_buffer, 1);
}
jpeg_finish_compress(&cinfo);
fclose(outfile);
jpeg_destroy_compress(&cinfo);
}
您为 read_JPEG_file
中的图像缓冲区分配 space 并将指向它的指针存储在该函数的 image_buffer
参数中,但是当函数 returns. main
中的 image_buffer
变量是一个不同的变量,不会因调用 read_JPEG_file
而更改。由于 main
中的 image_buffer
变量从未被初始化或赋值,因此您将未初始化的值而不是有效指针传递给 write_JPEG_file
。
全局变量 image_buffer
也是一个不同的变量,声明为指向示例开头附近的 JSAMPLE
的指针。您的示例程序完全没有使用此变量。
由于您已经在使用全局变量在函数 read_JPEG_file
和 write_JPEG_file
之间传递图像的宽度和高度,因此解决您的问题的简单方法(但不一定是最好的方法)也是使用全局变量作为指向图像数据的指针。将全局变量 image_buffer
的类型从 JSAMPLE *
更改为 unsigned char *
,从 main
中删除 image_buffer
的定义,并从中删除参数 image_buffer
函数 read_JPEG_file
和 write_JPEG_file
。这将导致全局变量 image_buffer
用于存储指向图像数据的指针。
例如:
...
unsigned char * image_buffer; // Points to large array of R,G,B-order data
int image_height; // Number of rows
int image_width; // Number of columns
int read_JPEG_file(char * filename);
void write_JPEG_file(char * filename, int quality);
int main()
{
char * filename1 = "source.jpg";
char * filename2 = "target.jpg";
read_JPEG_file(filename1);
write_JPEG_file(filename2, 95);
return 0;
}
...
GLOBAL(int)
read_JPEG_file (char * filename)
{
...
}
GLOBAL(void)
write_JPEG_file(char * filename, int quality)
{
...
}