LibRaw库(C++)基本介绍与相机Raw数据处理

Jun 5,2024   9534 words   35 min

Tags: SLAM

在之前的这篇笔记中,我们介绍过利用RawPy工具基于Python语言实现对于相机Raw数据的解析。在这篇笔记中,还是完成同样的目标,但利用LibRaw这个库、以C++语言实现。

1.LibRaw简介

在前面,我们提到了RawPy这个Python库,如果你看过它项目的主页便会知道,它其实就是LibRaw库的Python版本接口。我们这篇则重点关注LibRaw,官网地址是这里,官方GitHub地址是这里。根据官网介绍,LibRaw可以让Raw相关的开发更加专注于算法和对数据的处理,不用担心很多繁杂、底层的问题,例如元数据的不同格式、不同压缩方式、Bayer Pattern读取等。因此,LibRaw具有丰富的扩展性,可以有效应对各种Raw数据相关任务。

2.LibRaw的安装

LibRaw官方并非是一个标准的CMake项目,因此不能按照之前经典步骤进行安装。但总体也非常简单。首先,在LibRaw官网下载源码,然后解压,进入源码目录,打开终端,输入如下内容:

./configure
make -j
sudo make install

编译、安装完成的界面如下。 至此,就完成了LibRaw的安装。

3.LibRaw的使用

LibRaw这个库的使用相对特殊一些。一般情况下,在CMake项目中使用其它依赖都是先通过find_package命令查找,include_directories命令把相关头文件路径包含进来,最后target_link_libraries命令将对应的动态链接库关联到程序中。但对于LibRaw,在安装完成后使用并不需要查找包、也不用include目录,只需要在最后link一下它的链接库即可。而link的库也不是常见的${XXXX_LIBS}这种格式,如果这样的话在我电脑上会提醒找不到。所以直接指定完整路径即可,/usr/local/lib/libraw.so

在这里我们简单模拟一个任务:利用相机拍摄了一张Raw相片,然后利用LibRaw进行解析,最后保存成jpg文件。这里面主要涉及OpenCV和LibRaw两个库。

3.1 CMakeLists文件

CMakeLists.txt文件如下:

cmake_minimum_required(VERSION 3.2)
project(libRaw_demo)

set(CMAKE_CXX_STANDARD 11)

find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})

add_executable(libRaw_demo main.cpp)
target_link_libraries(libRaw_demo
        ${OpenCV_LIBS}
        /usr/local/lib/libraw.so
        )

可以看到,我们不需要在CMakeLists.txt里做任何查找的步骤,直接link一下库,然后在代码文件中包含头文件即可使用。

3.2 代码文件
#include <iostream>

#include "opencv2/opencv.hpp"
#include "libraw/libraw.h"

using namespace std;
using namespace cv;

int main() {

    string raw_path = "../test/test.dng";

    // step1. create image processor
    LibRaw myRawProcessor;

    // step2. open raw image file
    if (myRawProcessor.open_file(raw_path.c_str()) != LIBRAW_SUCCESS) {
        std::cout << "Open file fail" << std::endl;
    }

    // step3. unpack the raw data
    myRawProcessor.unpack();

    // step4. convert imgdata.rawdata to imgdata.image
    myRawProcessor.raw2image();

    // step5. get bayer pattern image
    cv::Mat bayer_image(myRawProcessor.imgdata.sizes.iheight,
                        myRawProcessor.imgdata.sizes.iwidth, CV_16UC1,
                        myRawProcessor.imgdata.rawdata.raw_image);

    // step6. convert to grayscale
    cv::Mat color_image, gray_image;
    cvtColor(bayer_image, color_image, cv::COLOR_BayerGB2BGR);
    cvtColor(color_image, gray_image, cv::COLOR_BGR2GRAY);

    // step7. output 
    imwrite("color_image.jpg", color_image);
    imwrite("gray_image.jpg", gray_image);

    // step8. release
    myRawProcessor.recycle();
    return 0;
}

可以看到,代码整理还是比较清晰的。核心主要就是第三、第四步。第三步的作用是将Raw文件中存储的二进制数据解包出来,变成可读的数据。然后第四步将这些数据转换成可显示的图片。然后,将获取到的Bayer Pattern数据转换到Mat格式,并利用OpenCV的函数转换到RGB彩色影像。转换好的影像如下图所示。 可以看到,这里出现了经典的偏绿的情况。这是因为我们只是“读取”并可视化了Raw数据,没有做任何的校正和处理。但利用LibRaw进行Raw数据处理的关键步骤就是这样。上面完整的项目也上传到了GitHub,欢迎查看

4.LibRaw中主要数据结构与类型

在上面,我们只是读取了Raw数据,没有对Raw数据做进一步处理。本部分主要参考这个官方文档,对部分我觉得重要的数据类型与变量进行介绍。完整版参阅上面的文档。 在LibRaw中,libraw_data_t类型的imgdata变量是为提供的用户数据访问接口类,访问方式为LibRaw::imgdata(class_instance.imgdata)。具体又包含以下主要字段:

  • idata: libraw_iparams_t类型,记录从Raw文件读取到的主要参数,详细包含哪些内容见后文。
  • sizes: libraw_image_sizes_t类型,记录影像的几何相关参数,详细包含哪些内容见后文。
  • color: libraw_colordata_t类型,描述从文件中读取的颜色信息,详细包含哪些内容见后文。
  • other: libraw_imgother_t类型,描述影像的感光度、快门、光圈、焦距等信息,详细包含哪些内容见后文。
  • rawdata: libraw_rawdata_t类型,提供一个指向raw-data缓冲区的指针,用于访问和修改读取的数据,详细使用见后文。
  • thumbnail: libraw_thumbnail_t类型,在LibRaw成功打开文件后,会自动生成预览文件,详细使用见后文。
  • lens: libraw_lensinfo_t类型,描述用于拍摄时所用的镜头信息,详细包含哪些内容见后文。
  • *image: 长度为4的ushort数组类型,提供指向影像像素数据的指针,其内容在调用raw2image()dcraw_process()后被填充。
4.1 libraw_iparams_t类型的idata变量

主要描述影像的一些通用信息,进一步包含如下主要字段:

  • make: 长度为64的char类型的数组,储存设备的生产商信息。
  • model: 长度为64的char类型的数组,储存设备的模组信息。
  • software: 长度为64的char类型的数组,储存Raw数据拍摄时的软件版本信息。
  • raw_count: unsigned类型的数字,表示文件中包含多少个Raw影像(0代表没有成功识别)。
  • dng_version: unsigned类型的数字,表示Raw数据dng格式的版本信息。
  • colors: int类型的数字,表示Raw数据包含颜色的个数。
  • cdesc: char类型长度为5的数组,表示RGBG, RGBE, GMCY, GBTG颜色模式中的一种,索引从0到3的颜色。
4.2 libraw_image_sizes_t类型的sizes变量

主要描述影像的几何尺寸信息,进一步包含如下主要字段:

  • raw_height: ushort类型的数字,表示Raw影像在高度上的全部大小(包含frame)。
  • raw_width: ushort类型的数字,表示Raw影像在宽度上的全部大小(包含frame)。
  • height: ushort类型的数字,表示影像可见部分(有意义部分)的像素高度(没有frame)。
  • width: ushort类型的数字,表示影像可见部分(有意义部分)的像素宽度(没有frame)。
  • top_margin: ushort类型的数字,frame的左上角点的高度方向坐标。
  • left_margin: ushort类型的数字,frame的左上角点的宽度方向坐标。
  • iheight: ushort类型的数字,表示输出影像的像素高度。
  • iwidth: ushort类型的数字,表示输出影像的像素宽度。
  • raw_pitch: unsigned类型的数字,表示以byte计算的每一行Raw数据所占据的大小。
  • pixel_aspect: double类型的数字,表示像素的width和height的比值。
  • flip: int类型的数字,表示影响的方向(0-无需旋转,3-180度旋转,5-90度逆时针旋转,6-90度顺时针旋转)。
4.3 libraw_colordata_t类型的color变量

主要描述影像的颜色信息,进一步包含如下主要字段:

  • curve: ushort类型长度为65536的数组,表示相机的色调曲线(tone curve)。
  • black: unsigned类型的black level数值,它可能为0(这意味着在Raw数据解包阶段已经进行了black level的消除或者相机输出数据时已经完成)。
  • cblack: unsigned类型长度为4102的数组,表示每个颜色通道的black level数值。
  • data_maximum: unsigned类型的数字,表示当前Raw文件中最大的像素值(必须要在调用raw2image()dcraw_process()后才会被填充)。
  • maximum: unsigned类型的数字,表示相机可以记录的最大的像素值。
  • linear_max: 长度为4的unsigned类型的数组,表示从Raw文件元数据中读取到的每个颜色通道的线性最大数值。
  • fmaximum: float类型的数字,表示相机可以记录的最大的像素值(以float类型表示)。
  • fnorm: float类型的数字,表示将float类型的Raw数据转换到int类型时的归一化系数。
  • cam_xyz: 4×3的float类型的数组,表示从相机RGB颜色空间到XYZ颜色空间的转换矩阵。
  • cam_mul: 长度为4的float类型的数组,表示拍摄时的白平衡参数。
  • pre_mul: 长度为4的float类型的数组,表示白天的白平衡参数。
  • cmatrix: 3×4的float类型的数组,表示从Raw文件中读取的相机颜色信息。
  • rgb_cam: 3×4的float类型的数组,表示从相机颜色空间到sRGB颜色空间的转换矩阵。
  • ccm: 3×4的float类型的数组,表示从文件元数据中读取的颜色校正矩阵。
  • flash_used: float类型的数字,表示是否使用闪光灯。
4.4 libraw_imgother_t类型的other变量

主要描述影像的其它信息,进一步包含如下主要字段:

  • iso_speed: float类型的数字,表示感光度ISO的大小。
  • shutter: float类型的数字,表示快门速度。
  • aperture: float类型的数字,表示光圈大小。
  • focal_len: float类型的数字,表示焦距。
  • timestamp: time_t类型的数字,表示拍摄时的时间戳。
  • gpsdata: 长度为32的unsigned类型的数组,表示拍摄的位置信息。
  • parsed_gps: libraw_gps_info_t类型的对象,包含解析好的经度、纬度和高程信息。
  • desc: 长度为512的char类型的数组,表示影像的描述信息。
  • artist: 长度为64的char类型的数组,表示影像的拍摄者信息。
4.5 libraw_rawdata_t类型的rawdata变量

主要描述没有解析的影像Raw数据,进一步包含如下主要字段:

  • *raw_image: unsigned short类型的指针,指向原始Bayer数据。
  • *color3_image: unsigned short数组类型的指针,指向包含3个颜色分量的像素。
  • *color4_image: unsigned short数组类型的指针,指向包含4个颜色分量的像素。
  • *float_image: float类型的指针,指向数据类型为float的Byer数据。
  • *float3_image: float数组类型的指针,指向数据类型为float包含3个颜色分量的像素。
  • *float4_image: float数组类型的指针,指向数据类型为float包含4个颜色分量的像素。

在官方文档中明确说了:在调用unpack()函数以后,这些字段只有一个是非空的,其它所有字段外部用户应该尽力避免访问,避免指针问题。

4.6 libraw_thumbnail_t类型的thumbnail变量

主要描述保存在Raw文件中的预览图片的信息,进一步包含如下主要字段:

  • tformat: LibRaw_thumbnail_formats类型(枚举类型),具体包括:LIBRAW_THUMBNAIL_UNKNOWN、LIBRAW_THUMBNAIL_JPEG、LIBRAW_THUMBNAIL_BITMAP、LIBRAW_THUMBNAIL_BITMAP16、LIBRAW_THUMBNAIL_LAYER、LIBRAW_THUMBNAIL_ROLLEI、LIBRAW_THUMBNAIL_H265。
  • twidth: ushort类型数字,表示预览图片的像素宽度。
  • theight: ushort类型数字,表示预览图片的像素高度。
  • tlength: unsigned类型数字,表示预览图片一样byte计算的大小。
  • tcolors: int类型数字,表示预览图片中包含的颜色个数。
  • *thumb: char类型的指针,指向从Raw文件中读取的预览图片数据。
4.7 libraw_lensinfo_t类型的lens变量

主要描述保存在Raw文件中的镜头相关信息,由于这一部分和相机和镜头硬件结合比较紧密,且各个厂家都有不同的定义,因此,没有相对统一的接口包含libraw_makernotes_lens_tlibraw_nikonlens_tlibraw_dnglens_tlibraw_lensinfo_t等内容。感兴趣可以参考官方文档里列举的对应部分,获取数据。

5.LibRaw中主要函数

这里我们简单列举、介绍LibRaw中相关的函数。本部分主要参考这个官方文档。根据文档介绍,有以下需要注意的几点:

    1. 整个Raw数据的处理流程都通过LibRaw这个类的实例化对象来完成。
    1. 一个实例化对象同时只能处理一张影像,可以实例化多个对象,从而同时处理多个影像。
    1. 如果要多个同时处理,需要关注内存的占用,可能会比较大。
5.1 打开数据

在LibRaw中提供了如下函数用于打开不同类型的数据:

  • LibRaw::open_datastream(): 打开Raw数据流
  • LibRaw::open_file(): 打开Raw数据文件
  • LibRaw::open_buffer(): 打开Raw数据缓冲区数据
  • LibRaw::open_bayer(): 打开Bayer模式的Raw数据
  • LibRaw::unpack(): 打开Raw数据,并计算Black Level,相关结果保存在imgdata.image
  • LibRaw::unpack_thumb(): 打开Raw数据并生产缩略图,相关结果保存在imgdata.thumbnail.thumb
  • LibRaw::unpack_thumb_ex(): 打开第i个缩略图
5.2 辅助函数

LibRaw中也提供了一些辅助函数可供使用,部分列举如下:

  • LibRaw::version(): 返回char类型的版本字符串
  • LibRaw::cameraCount(): 返回int类型的支持的相机Raw数据格式的个数
  • LibRaw::cameraList(): 返回char类型的list,包含具体支持的Raw数据格式的信息
  • LibRaw::COLOR(): 返回Bayer模式数据中指定行列位置的像素颜色,对于4分量的Bayer数据,范围为0-3,对于3分量数据,范围为0-2
  • LibRaw::subtract_black(): 执行Black Level消除,colordata.data_maximum、colordata.maximum、black level data (colordata.black和colordata.cblack)都会进行相应调整
  • LibRaw::is_floating_point(): 判断是否为浮点型的Raw数据,返回为1表示包含浮点数据
  • LibRaw::convertFloatToInt(): 将浮点型Raw数据转换为int型数据
5.3 Raw数据后处理函数

LibRaw中也提供了一些后处理函数可供使用。需要注意的是,这些后处理函数应该在open_file()unpack()函数之后调用。部分列举如下:

  • LibRaw::raw2image(): 将Raw数据转化为可以处理的缓冲区数据
  • LibRaw::free_image: 将由raw2image()申请的缓冲区imgdata.image数据释放
  • LibRaw::dcraw_process(): 在unpack()函数之后调用,模拟Raw数据的后处理过程
5.4 Raw数据输出函数

LibRaw提供了数据处理后输出的函数,主要如下:

  • LibRaw::dcraw_ppm_tiff_writer(): 将处理结果输出为无损的PPM/PGM/TIFF格式(通过imgdata.params.output_tiff属性设置)
  • LibRaw::dcraw_thumb_writer(): 将缩略图输出为PPM或JPEG格式图片

6.官方示例程序

LibRaw官方提供了示例程序以供学习,源码放在了LibRaw根目录下的samples文件夹内,编译LibRaw成功以后,生成的可执行文件在LibRaw根目录下的bin文件夹内。包含如下示例:

  • raw-identify: 展示了LibRaw打开读取Raw文件的基本用法(open_file()函数)。
  • simple_dcraw: 简单展示了解码Raw数据的流程。首先用open_file()函数打开文件,然后用unpack()函数解析数据。一方面,调用unpack_thumb_ex()函数和unpack_thumb()函数获取需要的缩略图,并通过dcraw_thumb_writer()函数保存。另一方面,调用dcraw_process()函数进行Raw数据的后处理,并将处理结果通过dcraw_ppm_tiff_writer()函数无损保存。
  • dcraw_half: 简单展示了解码Raw数据的流程(C语言版本)。
  • dcraw_emu: 较为完整的Raw数据解析示例,支持诸多参数。首先通过open()mmap()函数将Raw文件读取,并映射到内存中指定的区域,然后调用open_buffer()函数实现对该数据的解析(当然,代码也提供了open_file()函数的实现)。然后是常规的用unpack()函数解析数据,再用dcraw_process()函数进行Raw数据的后处理,最后将处理结果通过dcraw_ppm_tiff_writer()函数无损保存。
  • half_mt: 还是Raw数据的读取、解析和输出过程,但是尺寸是缩小为原图的一半。
  • mem_image: 使用dcraw_make_mem_image()和dcraw_make_mem_thumb()函数,实现Raw数据的读取、解析和输出。
  • unprocessed_raw: 展示了读取和解析Raw数据的流程并进行了逐像素的Gamma校正,主要利用open_file()函数和unpack()函数完成。
  • 4channels: 将Raw数据分解为16bit量化的4个波段的影像,核心还是基于open_file()函数和unpack()函数完成,感兴趣可以参考源码实现自己想要的功能。
  • multirender_test: 展示了一次打开文件,然后多次对其进行解析渲染的示例。
  • postprocessing_benchmark: 展示Raw数据处理各个步骤的耗时。
  • openbayer_sample: 展示了利用open_bayer()函数打开Raw数据的示例。

以上便是LibRaw库的相关笔记内容,可以看到,其实它并不像OpenCV那样全能,没有提供Raw数据的处理算法相关的函数,更多只是作为一个Raw数据读取和输出的角色存在。所以,如果要实现对Raw数据的处理,利用LibRaw读取数据以后如何做才是关键。

7.参考资料

  • [1] https://pypi.org/project/rawpy/
  • [2] https://www.libraw.org/
  • [3] https://github.com/LibRaw/LibRaw
  • [4] https://blog.csdn.net/zenglongjian/article/details/129225919
  • [5] https://www.libraw.org/docs/API-overview.html
  • [6] https://www.libraw.org/docs/API-datastruct-eng.html
  • [7] https://www.libraw.org/docs/API-CXX.html
  • [8] https://www.libraw.org/docs/API-datastruct.html
  • [9] https://www.libraw.org/docs/Samples-LibRaw.html

本文作者原创,未经许可不得转载,谢谢配合

返回顶部