You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 20:34:55