使用libraw与mozjpeg转换RAF为渐进式JPEG报错求助
问题
我有一台富士X-T10相机,会生成RAF格式的原始文件,希望将RAF文件转换为渐进式JPEG。使用以下命令可以成功生成目标文件:
dcraw -v -o 0 -c DSCF7257.RAF | cjpeg -quality 92 -progressive > DSCF7257.JPG
但自行编写的C程序实现相同功能时,返回错误:Empty JPEG image (DNL not supported),代码如下:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <libraw/libraw.h> #include <jpeglib.h> int main(int argc, char **argv) { if (argc != 2) { fprintf(stderr, "usage: %s <filename>.RAF\n", argv[0]); exit(EXIT_FAILURE); } char *src_name = argv[1]; char *src_name_2 = strdup(src_name); const char *delim = "/"; char *tok = NULL; char *dst_name = NULL; tok = strtok(src_name_2, delim); while ((tok = strtok(NULL, delim)) != NULL) dst_name = tok; dst_name = strcat(strtok(dst_name, "."), ".JPG"); FILE *src_file = fopen(src_name, "rb"); fseek(src_file, 0, SEEK_END); long src_len = ftell(src_file); fseek(src_file, 0, SEEK_SET); size_t chunk = 1000 * 1000 * 5; size_t nmembs = src_len / chunk; uint8_t *raw_buf = (uint8_t *) malloc(src_len * sizeof(uint8_t)); fread(raw_buf, chunk, nmembs, src_file); libraw_data_t *raw_dec = libraw_init(0); libraw_open_buffer(raw_dec, raw_buf, sizeof(*raw_buf)); libraw_unpack(raw_dec); libraw_dcraw_process(raw_dec); libraw_processed_image_t *raw_img = libraw_dcraw_make_mem_image(raw_dec, NULL); struct jpeg_compress_struct jpeg_enc; struct jpeg_error_mgr jpeg_err; JSAMPROW row_pointer[1]; int row_stride; jpeg_enc.err = jpeg_std_error(&jpeg_err); jpeg_create_compress(&jpeg_enc); FILE *dst_file = fopen(dst_name, "wb"); jpeg_stdio_dest(&jpeg_enc, dst_file); jpeg_enc.image_width = raw_img->width; jpeg_enc.image_height = raw_img->height; jpeg_enc.input_components = 3; jpeg_set_defaults(&jpeg_enc); jpeg_set_colorspace(&jpeg_enc, JCS_YCbCr); jpeg_set_quality(&jpeg_enc, 92, FALSE); jpeg_simple_progression(&jpeg_enc); jpeg_start_compress(&jpeg_enc, FALSE); row_stride = jpeg_enc.image_width * jpeg_enc.input_components; while (jpeg_enc.next_scanline < jpeg_enc.image_height) { row_pointer[0] = &raw_img->data[jpeg_enc.next_scanline * row_stride]; jpeg_write_scanlines(&jpeg_enc, row_pointer, 1); } jpeg_finish_compress(&jpeg_enc); jpeg_destroy_compress(&jpeg_enc); fclose(src_file); fclose(dst_file); free(raw_buf); free(src_name_2); libraw_dcraw_clear_mem(raw_img); libraw_recycle(raw_dec); libraw_close(raw_dec); exit(EXIT_SUCCESS); }
解决建议
错误核心是libraw未正确读取RAF文件数据,导致后续生成的图像数据无效,JPEG压缩时无有效像素从而报错。以下是具体问题及修复方案:
1. RAW文件读取逻辑错误
原代码fread参数使用错误,若文件大小不是5MB整数倍,会遗漏最后一部分数据:
// 错误写法 fread(raw_buf, chunk, nmembs, src_file);
应按1字节单位读取整个文件,并添加读取校验:
// 正确写法 size_t read_bytes = fread(raw_buf, 1, src_len, src_file); if (read_bytes != src_len) { fprintf(stderr, "Failed to read entire RAW file\n"); exit(EXIT_FAILURE); }
2. libraw_open_buffer参数错误
原代码传递的文件大小参数为sizeof(*raw_buf)(仅1字节),导致libraw无法识别RAF文件:
// 错误写法 libraw_open_buffer(raw_dec, raw_buf, sizeof(*raw_buf));
需替换为实际文件大小src_len,并添加错误检查:
// 正确写法 int ret = libraw_open_buffer(raw_dec, raw_buf, src_len); if (ret != LIBRAW_SUCCESS) { fprintf(stderr, "libraw_open_buffer failed: %s\n", libraw_strerror(ret)); exit(EXIT_FAILURE); }
3. 目标文件名处理存在内存越界风险
原代码直接在strtok返回的内存上strcat,可能超出原内存容量:
// 错误写法 dst_name = strcat(strtok(dst_name, "."), ".JPG");
应重新分配内存处理文件名:
// 正确写法 char *base_name = strtok(dst_name, "."); if (!base_name) { fprintf(stderr, "Invalid filename format\n"); exit(EXIT_FAILURE); } dst_name = malloc(strlen(base_name) + 5); // 预留".JPG"和结束符空间 if (!dst_name) { fprintf(stderr, "malloc failed: %s\n", strerror(errno)); exit(EXIT_FAILURE); } strcpy(dst_name, base_name); strcat(dst_name, ".JPG");
4. JPEG压缩参数优化
原代码jpeg_start_compress第二个参数设为FALSE,对于渐进式JPEG,建议设为TRUE以保证兼容性:
// 正确写法 jpeg_start_compress(&jpeg_enc, TRUE);
修复后的完整代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <libraw/libraw.h> #include <jpeglib.h> int main(int argc, char **argv) { if (argc != 2) { fprintf(stderr, "usage: %s <filename>.RAF\n", argv[0]); exit(EXIT_FAILURE); } char *src_name = argv[1]; char *src_name_2 = strdup(src_name); if (!src_name_2) { fprintf(stderr, "strdup failed: %s\n", strerror(errno)); exit(EXIT_FAILURE); } const char *delim = "/"; char *tok = NULL; char *dst_name = NULL; tok = strtok(src_name_2, delim); while ((tok = strtok(NULL, delim)) != NULL) dst_name = tok; // 修复文件名处理 char *base_name = strtok(dst_name, "."); if (!base_name) { fprintf(stderr, "Invalid filename format\n"); free(src_name_2); exit(EXIT_FAILURE); } dst_name = malloc(strlen(base_name) + 5); if (!dst_name) { fprintf(stderr, "malloc failed: %s\n", strerror(errno)); free(src_name_2); exit(EXIT_FAILURE); } strcpy(dst_name, base_name); strcat(dst_name, ".JPG"); FILE *src_file = fopen(src_name, "rb"); if (!src_file) { fprintf(stderr, "Failed to open source file: %s\n", strerror(errno)); free(src_name_2); free(dst_name); exit(EXIT_FAILURE); } fseek(src_file, 0, SEEK_END); long src_len = ftell(src_file); if (src_len == -1) { fprintf(stderr, "ftell failed: %s\n", strerror(errno)); fclose(src_file); free(src_name_2); free(dst_name); exit(EXIT_FAILURE); } fseek(src_file, 0, SEEK_SET); uint8_t *raw_buf = (uint8_t *) malloc(src_len * sizeof(uint8_t)); if (!raw_buf) { fprintf(stderr, "malloc failed: %s\n", strerror(errno)); fclose(src_file); free(src_name_2); free(dst_name); exit(EXIT_FAILURE); } // 修复文件读取 size_t read_bytes = fread(raw_buf, 1, src_len, src_file); if (read_bytes != src_len) { fprintf(stderr, "Failed to read entire RAW file: read %zu bytes, expected %ld\n", read_bytes, src_len); fclose(src_file); free(raw_buf); free(src_name_2); free(dst_name); exit(EXIT_FAILURE); } libraw_data_t *raw_dec = libraw_init(0); if (!raw_dec) { fprintf(stderr, "libraw_init failed\n"); fclose(src_file); free(raw_buf); free(src_name_2); free(dst_name); exit(EXIT_FAILURE); } // 修复libraw_open_buffer参数并添加错误检查 int ret = libraw_open_buffer(raw_dec, raw_buf, src_len); if (ret != LIBRAW_SUCCESS) { fprintf(stderr, "libraw_open_buffer failed: %s\n", libraw_strerror(ret)); libraw_close(raw_dec); fclose(src_file); free(raw_buf); free(src_name_2); free(dst_name); exit(EXIT_FAILURE); } ret = libraw_unpack(raw_dec); if (ret != LIBRAW_SUCCESS) { fprintf(stderr, "libraw_unpack failed: %s\n", libraw_strerror(ret)); libraw_close(raw_dec); fclose(src_file); free(raw_buf); free(src_name_2); free(dst_name); exit(EXIT_FAILURE); } ret = libraw_dcraw_process(raw_dec); if (ret != LIBRAW_SUCCESS) { fprintf(stderr, "libraw_dcraw_process failed: %s\n", libraw_strerror(ret)); libraw_close(raw_dec); fclose(src_file); free(raw_buf); free(src_name_2); free(dst_name); exit(EXIT_FAILURE); } libraw_processed_image_t *raw_img = libraw_dcraw_make_mem_image(raw_dec, &ret); if (!raw_img || ret != LIBRAW_SUCCESS) { fprintf(stderr, "libraw_dcraw_make_mem_image failed: %s\n", libraw_strerror(ret)); libraw_close(raw_dec); fclose(src_file); free(raw_buf); free(src_name_2); free(dst_name); exit(EXIT_FAILURE); } struct jpeg_compress_struct jpeg_enc; struct jpeg_error_mgr jpeg_err; JSAMPROW row_pointer[1]; int row_stride; jpeg_enc.err = jpeg_std_error(&jpeg_err); jpeg_create_compress(&jpeg_enc); FILE *dst_file = fopen(dst_name, "wb"); if (!dst_file) { fprintf(stderr, "Failed to open destination file: %s\n", strerror(errno)); libraw_dcraw_clear_mem(raw_img); libraw_close(raw_dec); fclose(src_file); free(raw_buf); free(src_name_2); free(dst_name); exit(EXIT_FAILURE); } jpeg_stdio_dest(&jpeg_enc, dst_file); jpeg_enc.image_width = raw_img->width; jpeg_enc.image_height = raw_img->height; jpeg_enc.input_components = 3; jpeg_set_defaults(&jpeg_enc); jpeg_set_colorspace(&jpeg_enc, JCS_YCbCr); jpeg_set_quality(&jpeg_enc, 92, FALSE); jpeg_simple_progression(&jpeg_enc); // 修复jpeg_start_compress参数 jpeg_start_compress(&jpeg_enc, TRUE); row_stride = jpeg_enc.image_width * jpeg_enc.input_components; while (jpeg_enc.next_scanline < jpeg_enc.image_height) { row_pointer[0] = &raw_img->data[jpeg_enc.next_scanline * row_stride]; jpeg_write_scanlines(&jpeg_enc, row_pointer, 1); } jpeg_finish_compress(&jpeg_enc); jpeg_destroy_compress(&jpeg_enc); fclose(src_file); fclose(dst_file); free(raw_buf); free(src_name_2); free(dst_name); libraw_dcraw_clear_mem(raw_img); libraw_recycle(raw_dec); libraw_close(raw_dec); exit(EXIT_SUCCESS); }
内容的提问来源于stack exchange,提问作者aculnaig
相关产品推荐
相关产品推荐

