Libtiff:从像素数组生成TIFF/EP Profile 2原始图像

5
我正在尝试从任意像素数组生成一个TIFF/EP Profile 2原始(.dng)图像。这个像素阵列表示Bayer模式(CFA)。
我研究了TIFF/EP文件规范,并使用libtiff包含了我认为生成完整的.dng文件所必需的所有标签。但是,我无法用dcraw转换创建的文件(dcraw显示无法解码文件)。
一个问题可能由两个标签引起,在TIFF/EP规范中被声明为强制性的,但似乎没有在libtiff中实现:SensingMethod和TIFF/EPStandardID标签。我真的需要包括它们吗(我看到有忽略这些标签的示例代码,但仍然报告正常工作),如果是这样,如何手动将它们添加到libtiff中?此外,设置SubIFD标签会产生错误消息“Assertion failed: *pa<=0xFFFFFFFFUL, file tif_dirwrite.c, line 1869”。
总的来说,我认为我的问题不仅仅是由这三个标签引起的,我相信存在一些根本性的问题。也许你们中的某些人可以查看我的代码并给出一些线索?我必须说,libtiff的文档相当差,因此我的代码受到其中极少数示例代码之一的启发:elphel_dng.c
非常感谢!Fabian
附言:我在Dropbox上上传了生成的文件。
C++
#include "tiffio.h"
#include <iostream>
using namespace std;

int main(void)
{
TIFF *tif = TIFFOpen("8bitRaw.dng", "w");

const int sampleperpixel = 1;
const int width = 4;
const int height = 4;

static const short bayerPatternDimensions[] = { 2,2 };
static const double cam_xyz[] = { 2.0413690, -0.5649464, -0.3446944, -0.9692660, 1.8760108, 0.0415560,0.0134474, -0.1183897, 1.0154096 }; //AdobeRGB
static const double neutral[] = { 1.0, 1.0, 1.0 };
long max_white = 255;
long sub_offset = 0;
int row, i;
float gamma = 80;

//Arbitrary Bayer pixel values:
unsigned char image[height][width*sampleperpixel] = {};
image[0][0] = 5;    image[0][1] = 165;   image[0][2] = 0;   image[0][3] = 255;
image[1][0] = 0;    image[1][1] = 21;    image[1][2] = 0;   image[1][3] = 10;
image[2][0] = 0;    image[2][1] = 0;     image[2][2] = 30;  image[2][3] = 5;
image[3][0] = 21;   image[3][1] = 120;   image[3][2] = 1;   image[3][3] = 254;

//Black Thumbnail pixel values:
unsigned char Thumbnail_RGB_Array[] = { 0,0,0,0,0,0,0,0,0,0,0,0 };

//Linearization Table:
unsigned short curve[256];
for (i = 0; i < 256; i++)
    curve[i] = 255 * pow(i / 255.0, 100.0 / gamma) + 0.5;


TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 1);
TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, 4);
TIFFSetField(tif, TIFFTAG_IMAGELENGTH, 4);
TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
TIFFSetField(tif, TIFFTAG_XRESOLUTION, 75.0);
TIFFSetField(tif, TIFFTAG_YRESOLUTION, 75.0);
TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);

TIFFSetField(tif, TIFFTAG_MAKE, "DummyMake");
TIFFSetField(tif, TIFFTAG_MODEL, "DummyModel");
TIFFSetField(tif, TIFFTAG_SOFTWARE, "DummySoftware");
TIFFSetField(tif, TIFFTAG_ORIGINALRAWFILENAME, 1, "DummyName.dng");
TIFFSetField(tif, TIFFTAG_UNIQUECAMERAMODEL, "DummyUniqueModel");
TIFFSetField(tif, TIFFTAG_IMAGEDESCRIPTION, "DummyImageDescription");
TIFFSetField(tif, TIFFTAG_COPYRIGHT, "DummyCopyright");
TIFFSetField(tif, TIFFTAG_DATETIME, "2016:06:30 11:11:15");
TIFFSetField(tif, TIFFTAG_DNGVERSION, "\01\01\00\00");
TIFFSetField(tif, TIFFTAG_DNGBACKWARDVERSION, "\01\00\00\00");
TIFFSetField(tif, TIFFTAG_COLORMATRIX1, 9, cam_xyz);
TIFFSetField(tif, TIFFTAG_ASSHOTNEUTRAL, 3, neutral);
TIFFSetField(tif, TIFFTAG_CALIBRATIONILLUMINANT1, 21);
//SensingMethodTag and TIFF/EPStandardID tag: Libtiff doesn't seem to know these:
//TIFFSetField(tif, 37399, 2);
//TIFFSetField(tif, 37398, "\01\01\00\00");
//Yields an error:
//TIFFSetField(tif, TIFFTAG_SUBIFD, 1, &sub_offset);

//Write a black 4x4 pixel dummy thumbnail:
for (row = 0; row < 4 ; row++) 
    TIFFWriteScanline(tif, Thumbnail_RGB_Array, row, 0);
TIFFWriteDirectory(tif);


//Now write main raw image:
TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 0);
TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width);
TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height);
TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CFA);
TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);
TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
TIFFSetField(tif, TIFFTAG_CFAREPEATPATTERNDIM, bayerPatternDimensions);
TIFFSetField(tif, TIFFTAG_CFAPATTERN, "\01\00\02\01");
TIFFSetField(tif, TIFFTAG_CFAPLANECOLOR, 3, "\00\01\02");
TIFFSetField(tif, TIFFTAG_LINEARIZATIONTABLE, 256, curve);
TIFFSetField(tif, TIFFTAG_WHITELEVEL, 1, &max_white);

TIFFWriteScanline(tif, image[0], 0, 0);
TIFFWriteScanline(tif, image[1], 1, 0);
TIFFWriteScanline(tif, image[2], 2, 0);
TIFFWriteScanline(tif, image[3], 3, 0);
TIFFClose(tif);
return 0;
}

那是一个DNG文件,而不是TIF文件。 - Mark Setchell
你确定 dcraw 能读取你的“有趣的 TIF 文件”吗?我认为 dcraw 应该用于读取 Adobe DNG 文件,因为它是数字底片,并且用于读取高端相机生成的 Adobe DNG。如果你将文件扩展名更改为 TIF,ImageMagick 就可以很好地读取它们了。 - Mark Setchell
真的吗?谢谢您提供的信息,我会认真研究一下。实际上,TIFF/EP Profile 2格式用于存储原始数据,即像素强度、Bayer模式布局等。而且,既然在dcraw网站上有文件规范的链接,我想dcraw应该能够处理它... 另外,Adobe的DNG基于TIFF/EP。@MarkSetchell - Fabian K
你实际上想要使用什么dcraw命令?你想得到什么结果? - Mark Setchell
@MarkSetchell:我通过在物镜上追踪光线到相机传感器来模拟相机。问题归结为以下几点:我有一个像素强度数组(我的相机传感器上的图像)和 Bayer 模式的布局。有了这两个信息,我想生成一个原始图像(就像真实相机生成的一样)。dcraw(或任何其他原始转换器)应该能够打开该图像文件,以便我可以应用去马赛克算法等等... 我通过“dcraw64.exe imagefile.dng”(我还尝试过 imagefile.tiff)在 Windows 上调用 dcraw。感谢您一直以来的帮助 :) - Fabian K
显示剩余4条评论
1个回答

3

我有类似的需求,从你离开的地方继续。这里是一些代码,生成一个128x128的原始Bayer 10位DNG图像,与dng_validate、dcraw、lightroom和resolve兼容。

我已经剥离了几乎所有内容,只保留了最少量的内容以保持dng_validate的兼容性。

#include <tiffio.h>
#include <math.h>

int main (void)
{
   const int width = 128;
   const int height = 128;

   static const short bayerPatternDimensions[] = { 2, 2 };
   static const float ColorMatrix1[] =
   {
      1.0, 0.0, 0.0,
      0.0, 1.0, 0.0,
      0.0, 0.0, 1.0,
   };

   static const float AsShotNeutral[] =
   {
      1.0, 1.0, 1.0,
   };

   int row, i;
   float gamma = 80;

   #define GR 0x80, 0x20, 0x08, 0x02, 0x00,
   #define WH 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
   #define BL 0x00, 0x00, 0x00, 0x00, 0x00,
   #define BLK(N) N N N N N N N N

   // Arbitrary Bayer pixel values:
   unsigned char image[] =
   {
      BLK(WH)
      BLK(GR)
      BLK(BL)
      BLK(WH)
      BLK(GR)
      BLK(BL)
      BLK(WH)
      BLK(GR)
      BLK(BL)
   };

   TIFF* tif = TIFFOpen ("out.dng", "w");
   TIFFSetField (tif, TIFFTAG_DNGVERSION, "\01\01\00\00");
   TIFFSetField (tif, TIFFTAG_SUBFILETYPE, 0);
   TIFFSetField (tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
   TIFFSetField (tif, TIFFTAG_IMAGEWIDTH, width);
   TIFFSetField (tif, TIFFTAG_IMAGELENGTH, height);
   TIFFSetField (tif, TIFFTAG_BITSPERSAMPLE, 10);
   TIFFSetField (tif, TIFFTAG_ROWSPERSTRIP, 1);
   TIFFSetField (tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
   TIFFSetField (tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CFA);
   TIFFSetField (tif, TIFFTAG_SAMPLESPERPIXEL, 1);
   TIFFSetField (tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
   TIFFSetField (tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
   TIFFSetField (tif, TIFFTAG_CFAREPEATPATTERNDIM, bayerPatternDimensions);
   TIFFSetField (tif, TIFFTAG_CFAPATTERN, "\00\01\01\02");
   TIFFSetField (tif, TIFFTAG_MAKE, "DNG");
   TIFFSetField (tif, TIFFTAG_UNIQUECAMERAMODEL, "DNG");
   TIFFSetField (tif, TIFFTAG_COLORMATRIX1, 9, ColorMatrix1);
   TIFFSetField (tif, TIFFTAG_ASSHOTNEUTRAL, 3, AsShotNeutral);
   TIFFSetField (tif, TIFFTAG_CFALAYOUT, 1);
   TIFFSetField (tif, TIFFTAG_CFAPLANECOLOR, 3, "\00\01\02");

   unsigned char* cur = image;

   for (row = 0; row < height;)
   {
      for (i = 0; i < 32; ++i, ++row)
         TIFFWriteScanline (tif, cur, row, 0);
      cur += 40;
   }

   TIFFClose (tif);
   return 0;
}

dcraw的使用相当棘手,我必须获取其源代码并在gdb中跟踪以找到错误条件:

if (!load_raw || height < 22 || width < 22 ||
  tiff_bps > 16 || tiff_samples > 6 || colors > 4)
  is_raw = 0;

因此,您第一次尝试创建4x4图像时会自动失败,因为出现了这种情况。


注意:此代码无法运行。生成的文件在使用dcraw -i显示时甚至没有正确的大小。 - user1418330
1
如果你将for循环条件中的i < 40改为i < 32,它将会写入正确数量的行以生成一个128x128的图像。感谢您提供这个代码,我用它编写了一个Python库来创建dng文件。 - Amanda_Panda
2
对于现代的libtiff版本,您必须为CFAPattern提供大小。 例如:TIFFSetField (tif,TIFFTAG_CFAPATTERN,4,"\ 00 \ 01 \ 01 \ 02"); - Shootfast
如果CFAPATTERN行出现段错误,那是因为字符串前面缺少4, ,就像Shootfast所说的那样。这种情况发生在libtiff5版本4.4.0中。如果我没记错的话,API的更改是在版本4.2.0中进行的。 - user2394284

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接