Libtiff: Генерация TIFF/EP Profile 2 Raw изображения из массива пикселей

Я пытаюсь создать необработанное изображение TIFF/EP Profile 2 (.dng) из произвольного массива пикселей. Этот массив пикселей представляет шаблон Байера (CFA).

Я изучил спецификации файлов TIFF/EP и с помощью libtiff включил все теги, которые, по моему мнению, необходимы для создания полного файла .dng. Однако я не могу преобразовать созданный файл с помощью dcraw (dcraw показывает, что не может декодировать файл).

Одну проблему могут вызвать два тега, которые объявлены обязательными в спецификации TIFF/EP, но не реализованы в libtiff: теги SensingMethod и TIFF/EPStandardID. Мне действительно нужно включать их (я видел пример кода, который игнорирует эти теги, но все же сообщается, что он работает правильно), и если да, то как я могу вручную добавить их в libtiff? Кроме того, установка тега SubIFD приводит к сообщению об ошибке «Ошибка утверждения: *pa‹=0xFFFFFFFFUL, файл tif_dirwrite.c, строка 1869».

В общем, я не думаю, что мои проблемы связаны только с этими тремя тегами, и я считаю, что что-то в корне неправильное. Может быть, кто-то из вас может взглянуть на мой код и дать некоторые подсказки? Я должен сказать, что документация libtiff довольно бедна, поэтому мой код вдохновлен одним из очень немногих примеров кода: elphel_dng.c.

Большое тебе спасибо! Фабиан

PS. Я загрузил сгенерированный файл в 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;
}

person Fabian K    schedule 04.07.2016    source источник
comment
Можете ли вы предоставить ссылку на файл TIF, который вы создаете?   -  person Mark Setchell    schedule 04.07.2016
comment
@mark Да, конечно. Здесь   -  person Fabian K    schedule 04.07.2016
comment
Это DNG, а не TIF.   -  person Mark Setchell    schedule 04.07.2016
comment
Это образ TIFF/EP Profile 2 (по крайней мере, я надеюсь, что libtiff создал такой формат файла). В файле [спецификации] (standardsproposals.bsigroup.com/Home/getPDF/567 )), он говорит: расширение файла .dng должно указывать файл профиля 2. Не путать с Adobe DNG. @МаркСетчелл   -  person Fabian K    schedule 04.07.2016
comment
Вы уверены, что dcraw может читать ваши забавные TIF? Я думал, что dcraw будет использоваться для чтения файлов Adobe DNG, поскольку они являются цифровыми негативами и используются для чтения Adobe DNG, созданных камерами высокого класса. Если вы измените расширение файла на TIF, ImageMagick сможет их нормально прочитать.   -  person Mark Setchell    schedule 04.07.2016
comment
Действительно, может? Спасибо за информацию, буду разбираться поближе. На самом деле, формат TIFF/EP Profile 2 предназначен для хранения необработанных данных, т. е. интенсивности пикселей, шаблона Байера и т. д. И поскольку на веб-сайте dcraw есть ссылка на спецификации файла, я думаю, dcraw способен с этим справиться. .. Между прочим, Adobe DNG основан на TIFF/EP. @МаркСетчелл   -  person Fabian K    schedule 04.07.2016
comment
Какую команду dcraw вы на самом деле пытаетесь использовать? И что вы, собственно, хотите получить в результате?   -  person Mark Setchell    schedule 05.07.2016
comment
@MarkSetchell: я имитирую камеру, отслеживая лучи через объектив на датчик камеры. Проблема сводится к следующему: у меня есть массив пикселей интенсивности (изображение на датчике моей камеры) и макет шаблона Байера. С помощью этих двух данных я хочу сгенерировать необработанное изображение (так же, как если бы реальная камера генерировала его). dcraw (или любой другой конвертер необработанных данных) сможет открыть этот файл изображения, чтобы я мог применить алгоритмы демозаики и т. д. Я вызываю dcraw в Windows через dcraw64.exe imagefile.dng (я также пробовал imagefile.tiff). Спасибо за постоянную помощь :)   -  person Fabian K    schedule 06.07.2016
comment
Боюсь, вы вышли за пределы моих знаний. Я знаю, что Эрик (@emcconville) очень хорошо разбирается в файлах TIFF, так что, возможно, у него будет возможность взглянуть на них сейчас, когда он предупрежден.   -  person Mark Setchell    schedule 06.07.2016


Ответы (2)


У меня почти такое же требование, и я продолжил с того места, где вы остановились. Вот небольшой код, который генерирует необработанное 10-битное DNG-изображение Bayer размером 128x128, совместимое с 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 автоматически потерпит неудачу из-за этого.

person Vanista    schedule 03.10.2016
comment
Примечание: этот код не работает. Сгенерированный файл даже не имеет правильного размера при отображении с помощью dcraw -i. - person ; 21.04.2017
comment
Если вы измените i < 40 на i < 32 в условии цикла for, он запишет правильное количество строк для создания изображения 128x128. Спасибо за это, я использовал его для написания библиотеки Python для создания файлов dng. - person Amanda_Panda; 06.09.2018

Это не ответ, но он слишком велик для комментария и является конструктивным вкладом в поиск решения... поэтому, пожалуйста, не голосуйте против.

Если вы переименуете файл так, чтобы он заканчивался на .TIF, а затем запустите команду identify ImageMagick для двух вложенных изображений, вы получите следующее:

identify -verbose a.tif[0]

Вывод

Image: a.tif
  Base filename: a.tif
  Format: TIFF (Tagged Image File Format)
  Mime type: image/tiff
  Class: DirectClass
  Geometry: 4x4+0+0
  Resolution: 75x75
  Print size: 0.0533333x0.0533333
  Units: PixelsPerInch
  Type: Bilevel
  Base type: TrueColor
  Endianess: LSB
  Colorspace: sRGB
  Depth: 8/1-bit
  Channel depth:
    gray: 1-bit
  Channel statistics:
    Pixels: 16
    Gray:
      min: 0 (0)
      max: 0 (0)
      mean: 0 (0)
      standard deviation: 0 (0)
      kurtosis: 0
      skewness: 0
      entropy: nan
  Colors: 1
  Histogram:
        16: (  0,  0,  0) #000000 gray(0)
  Rendering intent: Perceptual
  Gamma: 0.454545
  Chromaticity:
    red primary: (0.64,0.33)
    green primary: (0.3,0.6)
    blue primary: (0.15,0.06)
    white point: (0.3127,0.329)
  Background color: gray(255)
  Border color: gray(223)
  Matte color: gray(189)
  Transparent color: gray(0)
  Interlace: None
  Intensity: Undefined
  Compose: Over
  Page geometry: 4x4+0+0
  Dispose: Undefined
  Iterations: 0
  Compression: None
  Orientation: TopLeft
  Properties:
    comment: DummyImageDescription
    date:create: 2016-07-04T13:20:26+01:00
    date:modify: 2016-07-04T13:20:26+01:00
    signature: 17b0761f87b081d5cf10757ccc89f12be355c70e2e29df288b65b30710dcbcd1
    tiff:alpha: unspecified
    tiff:copyright: DummyCopyright
    tiff:endian: lsb
    tiff:make: DummyMake
    tiff:model: DummyModel
    tiff:photometric: RGB
    tiff:rows-per-strip: 682
    tiff:software: DummySoftware
    tiff:subfiletype: REDUCEDIMAGE
    tiff:timestamp: 2016:06:30 11:11:15
  Artifacts:
    filename: a.tif[0]
    verbose: true
  Tainted: False
  Filesize: 1.33KB
  Number pixels: 16
  Pixels per second: 16PB
  User time: 0.000u
  Elapsed time: 0:01.000
  Version: ImageMagick 6.9.5-0 Q16 x86_64 2016-07-04 http://www.imagemagick.org

И для второго подизображения:

identify -verbose a.tif[1]

Вывод

  Image: a.tif
  Base filename: a.tif
  Format: TIFF (Tagged Image File Format)
  Mime type: image/tiff
  Class: DirectClass
  Geometry: 4x4+0+0
  Units: PixelsPerInch
  Type: Palette
  Base type: TrueColor
  Endianess: LSB
  Colorspace: sRGB
  Depth: 8-bit
  Channel depth:
    red: 8-bit
    green: 8-bit
    blue: 8-bit
  Channel statistics:
    Pixels: 16
    Red:
      min: 0 (0)
      max: 255 (1)
      mean: 70.0625 (0.274755)
      standard deviation: 88.1841 (0.34582)
      kurtosis: -0.160699
      skewness: 1.06679
      entropy: 0.80381
    Green:
      min: 0 (0)
      max: 255 (1)
      mean: 59.8125 (0.234559)
      standard deviation: 83.8438 (0.328799)
      kurtosis: 0.828861
      skewness: 1.43159
      entropy: 0.884626
    Blue:
      min: 0 (0)
      max: 255 (1)
      mean: 67.6875 (0.265441)
      standard deviation: 95.9861 (0.376416)
      kurtosis: -0.141547
      skewness: 1.19724
      entropy: 0.811278
  Image statistics:
    Overall:
      min: 0 (0)
      max: 255 (1)
      mean: 65.8542 (0.258252)
      standard deviation: 89.4791 (0.350899)
      kurtosis: 0.150059
      skewness: 1.23551
      entropy: 0.833238
  Colors: 7
  Histogram:
         6: (  0,  0,  0) #000000 black
         2: (  0,  1,  0) #000100 srgb(0,1,0)
         1: ( 54,128,255) #3680FF srgb(54,128,255)
         3: ( 90, 84, 85) #5A5455 srgb(90,84,85)
         1: ( 96, 32,  0) #602000 srgb(96,32,0)
         1: (191, 33, 63) #BF213F srgb(191,33,63)
         2: (255,255,255) #FFFFFF white
  Rendering intent: Perceptual
  Gamma: 0.454545
  Chromaticity:
    red primary: (0.64,0.33)
    green primary: (0.3,0.6)
    blue primary: (0.15,0.06)
    white point: (0.3127,0.329)
  Background color: white
  Border color: srgb(223,223,223)
  Matte color: grey74
  Transparent color: black
  Interlace: None
  Intensity: Undefined
  Compose: Over
  Page geometry: 4x4+0+0
  Dispose: Undefined
  Iterations: 0
  Scene: 1
  Compression: None
  Orientation: TopLeft
  Properties:
    date:create: 2016-07-04T14:19:59+01:00
    date:modify: 2016-07-04T14:19:59+01:00
    signature: 19310563e3b3b718a9c7e4a49743acffb6fefbdbeb64a6742589e7efd0beb36a
    tiff:alpha: unspecified
    tiff:endian: lsb
    tiff:photometric: unknown
    tiff:rows-per-strip: 2048
  Artifacts:
    filename: a.tif[1]
    verbose: true
  Tainted: False
  Filesize: 0B
  Number pixels: 16
  Pixels per second: 16PB
  User time: 0.000u
  Elapsed time: 0:01.000
  Version: ImageMagick 6.9.5-0 Q16 x86_64 2016-07-04 http://www.imagemagick.org

Вы также можете использовать tiffdump следующим образом:

tiffdump YourFile

Вывод

Magic: 0x4949 <little-endian> Version: 0x2a <ClassicTIFF>
Directory 0: offset 56 (0x38) next 632 (0x278)
SubFileType (254) LONG (4) 1<1>
ImageWidth (256) SHORT (3) 1<4>
ImageLength (257) SHORT (3) 1<4>
BitsPerSample (258) SHORT (3) 3<8 8 8>
Compression (259) SHORT (3) 1<1>
Photometric (262) SHORT (3) 1<2>
ImageDescription (270) ASCII (2) 22<DummyImageDescription\0>
Make (271) ASCII (2) 10<DummyMake\0>
Model (272) ASCII (2) 11<DummyModel\0>
StripOffsets (273) LONG (4) 1<8>
Orientation (274) SHORT (3) 1<1>
SamplesPerPixel (277) SHORT (3) 1<3>
StripByteCounts (279) LONG (4) 1<48>
XResolution (282) RATIONAL (5) 1<75>
YResolution (283) RATIONAL (5) 1<75>
PlanarConfig (284) SHORT (3) 1<1>
ResolutionUnit (296) SHORT (3) 1<2>
Software (305) ASCII (2) 14<DummySoftware\0>
DateTime (306) ASCII (2) 20<2016:06:30 11:11:15\0>
Copyright (33432) ASCII (2) 15<DummyCopyright\0>
50706 (0xc612) BYTE (1) 4<0x1 0x1 00 00>
50707 (0xc613) BYTE (1) 4<0x1 00 00 00>
50708 (0xc614) ASCII (2) 17<DummyUniqueModel\0>
50721 (0xc621) SRATIONAL (10) 9<2824.73 2.00517 Nan (2147483647/0) -1.76624 0 -1.67235 0 -1.86732 Nan (-2147483647/0)>
50728 (0xc628) RATIONAL (5) 3<0 1.875 0>
50778 (0xc65a) SHORT (3) 1<21>
50827 (0xc68b) BYTE (1) 1<0x44>

Directory 1: offset 632 (0x278) next 0 (0)
SubFileType (254) LONG (4) 1<0>
ImageWidth (256) SHORT (3) 1<4>
ImageLength (257) SHORT (3) 1<4>
BitsPerSample (258) SHORT (3) 1<8>
Compression (259) SHORT (3) 1<1>
Photometric (262) SHORT (3) 1<32803>
StripOffsets (273) LONG (4) 1<616>
SamplesPerPixel (277) SHORT (3) 1<1>
StripByteCounts (279) LONG (4) 1<16>
PlanarConfig (284) SHORT (3) 1<1>
33421 (0x828d) SHORT (3) 2<2 2>
33422 (0x828e) BYTE (1) 4<0x1 00 0x2 0x1>
50710 (0xc616) BYTE (1) 3<00 0x1 0x2>
50712 (0xc618) SHORT (3) 256<0 0 1 1 1 2 2 3 3 4 4 5 6 6 7 7 8 9 9 10 11 11 12 13 ...>
50717 (0xc61d) LONG (4) 1<255>
person Mark Setchell    schedule 04.07.2016
comment
tiffdump был немного полезен, я понял, что значения, сохраненные в тегах 50721 и 50728 (ColorMatrix1 и AsShotNeutral), неверны. Я мог бы исправить это, изменив тип данных матриц со static const double на const float. Однако до сих пор нет улучшений... - person Fabian K; 05.07.2016