naivecv的设计与实现(2): 读写gray和rgb图像

图像读写并不是图像处理的核心,仅仅作为调试工具, 是一种手段而非目的。

图像文件格式的选择

正因如此,对gray和rgb图像的读写,存在多种方法。 最常见的三种图像文件格式:

  • bmp
  • png
  • jpg

实际上有更简单的方式:pgm(存储gray图像),ppm(存储gray图像)。
选择 pgm 和 ppm 图像文件格式, 纯粹是因为编解码更为简单, 实现起来更容易。

获取 .ppm 和 .pgm 样例图像文件

https://graphics.stanford.edu/~jowens/223b/examples.html

提供了 lena.ppm, lena.pgm 等图像。

读写 .ppm 和 .pgm 图像文件

代码源自网络,稍加整理。放在 test.cpp 里。

/// @brief Save GRAY image to .pgm file
static void savePGM(const char* filename, int width, int height, uint8_t* image)
{
    FILE* fout = fopen(filename, "wb");
    fprintf(fout, "P5\n%d %d\n255\n", width, height);
    fwrite(image, width * height, 1, fout);
    fclose(fout);
}

/// @brief Load GRAY image from .pgm file
/// The caller should release the memory
static uint8_t* loadPGM(const char* filename, int* out_width, int* out_height)
{
    FILE* fin = fopen(filename, "rb");
    char magic[3];
    int width, height;
    int nscan = fscanf(fin, "%2s\n%d %d\n255\n", magic, &width, &height);
    *out_width = width;
    *out_height = height;

    uint8_t* image = nullptr;
    if (nscan == 3 && magic[0] == 'P' && magic[1] == '5')
    {
        image = (uint8_t*) malloc(width * height);
        fread(image, width * height, 1, fin);
    }

    fclose(fin);
    return image;
}


/// @brief Save RGB image to .ppm file
static void savePPM(const char* filename, int width, int height, unsigned char* data)
{
    FILE* fp;
    char header[20];
 
    fp = fopen(filename, "wb");
 
    // 写图片格式、宽高、最大像素值
    fprintf(fp,"P6\n%d %d\n255\n", width, height);
 
    // 写RGB数据
    fwrite(data, width*height*3, 1, fp);
 
    fclose(fp);
}

/// @brief Load RGB image from .ppm file
/// The caller should release the memory
static uint8_t* loadPPM(const char* filename, int* out_width, int* out_height)
{
    char header[1024];
    FILE* fp = NULL;
    int line = 0;
 
    fp = fopen(filename, "rb");
 
    // 读取图片格式(例如:"P6")
    // 高宽在第二行非注释数据
    while(line < 2){    
        fgets(header, 1024, fp);
        if(header[0] != '#'){
            ++line;
        }
    }
    
    int width, height;
    sscanf(header,"%d %d\n", &width, &height);
    *out_width = width;
    *out_height = height;
 
    // 获取最大像素值
    fgets(header, 20, fp);

    size_t buf_size = height * width * 3;
    uint8_t* data = (uint8_t*) malloc(buf_size);

    // get rgb data
    fread(data, buf_size, 1, fp);
 
    fclose(fp);

    return data;
}

投入使用

调试是一种手段。 上述 ppm/pgm 图像的读写, 是用于 naivecv 第一个图像处理函数:rgb 转 gray。

NCV_Status ncvConvertColorRGBtoGRAY(const NCV_Image* srcImg, NCV_Image* dstImg);

api 设计

enum {
    NCV_STATUS_OK = 0,
    NCV_STATUS_INVALID_PARAM = 1
};
typedef int NCV_Status;

typedef enum NCV_PixelFormat {
    NCV_PIXFMT_GRAY,
    NCV_PIXFMT_RGB,
} NCV_PixelFormat;

typedef struct NCV_Image {
    NCV_PixelFormat format;
    int width;
    int height;
    uint8_t* plane[4];
    int pitch[4];
} NCV_Image;

具体实现

具体实现是细节, 这里忽略。

测试

这里贴出测试代码:

int main()
{
    const char* filename = "/Users/zz/data/lena.ppm";

    int width, height;
    uint8_t* ppm_data = loadPPM(filename, &width, &height);

    // 使用读取到的图像数据进行后续处理
    NCV_Image rgbImg;
    rgbImg.format = NCV_PIXFMT_RGB;
    rgbImg.height = height;
    rgbImg.width = width;
    rgbImg.pitch[0] = width * 3;
    rgbImg.plane[0] = ppm_data;

    NCV_Image grayImg;
    grayImg.format = NCV_PIXFMT_GRAY;
    grayImg.height = height;
    grayImg.width = width;
    grayImg.pitch[0] = width;
    grayImg.plane[0] = (uint8_t*) malloc(height * width);

    ncvConvertColorRGBtoGRAY(&rgbImg, &grayImg);
    savePGM("lena.pgm", width, height, grayImg.plane[0]);
    savePPM("lena.ppm", width, height, rgbImg.plane[0]);

    // free memory
    free(ppm_data);
    free(grayImg.plane[0]);

    return 0;
}

查看 lena.pgm, 符合预期

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值