闽公网安备 35020302035485号

using Sdcb.LibRaw;
using RawContext r = RawContext.OpenFile(@"C:\a7r3\DSC02653.ARW");
r.Unpack();
r.DcrawProcess();
using ProcessedImage image = r.MakeDcrawMemoryImage();
using Bitmap bmp = ProcessedImageToBitmap(image);
Bitmap ProcessedImageToBitmap(ProcessedImage rgbImage)
{
// 堆代码 duidaima.com
rgbImage.SwapRGB();
using Bitmap bmp = new Bitmap(rgbImage.Width, rgbImage.Height, rgbImage.Width * 3, System.Drawing.Imaging.PixelFormat.Format24bppRgb, rgbImage.DataPointer);
return new Bitmap(bmp);
}
这段代码主要演示如何将RAW照片转换为Bitmap图像,有一点值得一提:LibRaw输出的像素格式和Bitmap有些许不同,具体体现在红蓝两色需要调换,代码中使用rgbImage.SwapRGB();用来调换红色和蓝色的顺序,也就是将RGB24转换成了BGR24。虽然这个示例基于.ARW照片,但实际上几乎所有RAW格式照片都是支持的,包括.CR2或者.DNG,可以通过RawContext.SupportedCameras获取支持的相机列表,截止当前版本它支持了1182款相机型号:Console.WriteLine("Sdcb.LibRaw supported cameras:");
foreach (string model in RawContext.SupportedCameras)
{
Console.WriteLine(model);
}
输出如下(有省略):Sdcb.LibRaw supported cameras: 1: Adobe Digital Negative (DNG) 2: AgfaPhoto DC-833m ... 1057: Sony ILCE-1 (A1) ... 1181: Zeiss ZX1 1182: Zenit M2. WPF和OpenCV示例
BitmapSource ProcessedImageToBitmapSource(ProcessedImage rgbImage)
{
return BitmapSource.Create(rgbImage.Width, rgbImage.Height,
96, 96,
PixelFormats.Rgb24,
null,
rgbImage.AsSpan<byte>().ToArray(),
rgbImage.Width * 3);
}
值得一提的是 WPF 的图片 BitmapSource 并不需要调换 R和 B 两个通道的颜色。如果你使用的是 OpencvSharp4 ,可以使用下面的代码将 ProcessedImage 转换为Mat:Mat ProcessedImageToMat(ProcessedImage rgbImage)
{
Mat mat = new Mat(rgbImage.Height, rgbImage.Width, MatType.CV_8UC3, rgbImage.AsSpan<byte>().ToArray());
Cv2.CvtColor(mat, mat, ColorConversionCodes.RGB2BGR);
return mat;
}
请注意上面3个示例中,我都使用了.AsSpan<byte>().ToArray()用来将内存复制一份。这样一来额外会复制会让性能略微降低,这是为了确保Bitmap、BitmapSource或Mat的生命周期由自己来管理,否则它们会共享使用ProcessedImage的内存,导致意外的情况。// 小心,代码直接使用了由ProcessedImage创建的内存
Mat ProcessedImageToMatZeroCopy(ProcessedImage rgbImage)
{
Mat mat = new Mat(rgbImage.Height, rgbImage.Width, MatType.CV_8UC3, rgbImage.DataPointer); // 换成rgbImage.DataPointer
Cv2.CvtColor(mat, mat, ColorConversionCodes.RGB2BGR);
return mat;
}
3. 图像后期r.DcrawProcess(c =>
{
c.HalfSize = false; // 图片只保留1/4大小
c.UseCameraWb = true; // 使用机内白平衡,false则会由UserMultipliers控制白平衡
c.Gamma[0] = 0.35; // 调整Gamma曲线指数
c.Gamma[1] = 3.5; // 调整Gamma曲线斜率
c.Brightness = 2.2f; // 亮度
c.Interpolation = true; // 是否执行反马赛克(demosaic)操作
c.OutputBps = 8; // 输出位数8位
c.OutputTiff = false; // 输出为tiff文件?false表示输出Bitmap
// c.Cropbox = new Rectangle(4000, 2000, 1500, 700); // 裁切
// 还有许多其它设置可以自行探索
});
原图:


using Sdcb.LibRaw; using RawContext r = RawContext.OpenFile(@"C:\a7r3\DSC02653.ARW"); using ProcessedImage image = r.ExportThumbnail(thumbnailIndex: 0); using Bitmap bmp = (Bitmap)Bitmap.FromStream(new MemoryStream(image.AsSpan<byte>().ToArray()));在上面的示例中我使用了r.ExportThumbnail(thumbnailIndex: 0);,它可以导出第0张缩略图,请注意这个是一个快捷函数,它内部会调用了下面2个函数:
r.UnpackThumbnail() r.MakeDcrawMemoryThumbnail()请注意它转换为Bitmap的方式有所不同,由于它的数据本质是JPEG格式,因此不再需要更换红色、蓝色的通道位置,同样也不需要关注它的宽度和高度,同样的道理如果使用OpenCV解码也应该使用Cv2.ImDecode。
using Sdcb.LibRaw; using RawContext r = RawContext.OpenFile(@"C:\a7r3\DSC02653.ARW"); // r.SaveRawImage() is a shortcut for r.Unpack() + r.DcrawProcess() + r.WriteDcrawPpmTiff(fileName) r.SaveRawImage(@"C:\test\test.tiff"); 同样地r.SaveRawImage(@"C:\test\test.tiff");也是Sdcb.LibRaw提供的一个快捷方式,它内部按顺序调用了下面3个函数: r.Unpack() r.DcrawProcess() r.WriteDcrawPpmTiff()6. 获取照片元数据信息
using RawContext r = RawContext.OpenFile(@"C:\a7r3\DSC02653.ARW");
LibRawImageParams imageParams = r.ImageParams;
LibRawImageOtherParams otherParams = r.ImageOtherParams;
LibRawLensInfo lensInfo = r.LensInfo;
Console.WriteLine($"相机: {imageParams.Model}");
Console.WriteLine($"版本号: {imageParams.Software}");
Console.WriteLine($"ISO: {otherParams.IsoSpeed}");
Console.WriteLine($"快门速度: 1/{1 / otherParams.Shutter:F0}s");
Console.WriteLine($"焦距: {otherParams.FocalLength}mm");
Console.WriteLine($"艺术家标签: {otherParams.Artist}");
Console.WriteLine($"拍摄日期: {new DateTime(1970, 1, 1, 8, 0, 0).AddSeconds(otherParams.Timestamp)}");
Console.WriteLine($"镜头名称: {lensInfo.Lens}");
在我的这个示例中,输出如下:相机: ILCE-7RM3 版本号: ILCE-7RM3 v3.10 ISO: 100 快门速度: 1/400s 焦距: 50mm 艺术家标签: Zhou Jie/sdcb 拍摄日期: 2023/1/26 12:54:01 镜头名称: FE 50mm F1.2 GM
var sw = Stopwatch.StartNew();
using RawContext r = RawContext.OpenFile(@"C:\Users\ZhouJie\Pictures\a7r3\11030126\DSC02653.ARW");
r.Unpack();
r.DcrawProcess();
Console.WriteLine($"耗时:{sw.ElapsedMilliseconds}ms");
输出如下:耗时:1627ms
Windows Imaging Component
之前的文章说过,可以使用系统自带的WIC进行RAW照片解码:
// 需要安装NuGet包:Vortices.Direct2D1
Stopwatch sw = Stopwatch.StartNew();
IWICImagingFactory2 wic = new IWICImagingFactory2();
using IWICBitmapDecoder decoder = wic.CreateDecoderFromFileName(@"C:a7r3\DSC02653.ARW");
using IWICFormatConverter converter = wic.CreateFormatConverter();
converter.Initialize(decoder.GetFrame(0), PixelFormat.Format24bppBGR);
var data = new byte[converter.Size.Width * 3 * converter.Size.Height];
converter.CopyPixels(converter.Size.Width * 3, data);
Console.WriteLine($"耗时:{sw.ElapsedMilliseconds}ms");
// 下面转Bitmap
// fixed (byte* pdata = data)
// {
// new System.Drawing.Bitmap(converter.Size.Width, converter.Size.Height, converter.Size.Width * 3, System.Drawing.Imaging.PixelFormat.Format24bppRgb, (IntPtr)pdata).DumpUnscaled();
// }
输出如下:耗时:2177ms这个方案的缺点是它无法对 RAW 照片做一些后处理。
Stopwatch sw = Stopwatch.StartNew();
using MagickImage image = new MagickImage(@"C:\a7r3\DSC02653.ARW");
Console.WriteLine($"耗时:{sw.ElapsedMilliseconds}ms");
输出如下:耗时:5496ms这个方案的缺点是它明显慢一些,且它的后处理都并非基于拜尔数据,因此后期空间有限。
#include <libraw\libraw.h>
#include <chrono>
int main()
{
auto start = std::chrono::high_resolution_clock::now();
libraw_data_t* data = libraw_init(0);
libraw_open_file(data, "C:\\a7r3\\DSC02653.ARW");
libraw_unpack(data);
libraw_dcraw_process(data);
auto end = std::chrono::high_resolution_clock::now();
printf("耗时: %lld ms\n", std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count());
libraw_recycle(data);
libraw_close(data);
}
输出如下:耗时: 1619 ms
| 方案名称 | 耗时(ms) | 说明 |
|---|---|---|
| Sdcb.LibRaw | 1627 | |
| Windows Imaging Component(WIC) | 2177 | 后期空间有限 |
| Magick.NET | 5496 | 后期空间有限 |
| 原生C代码 | 1619 |