I've been struggling for the last couple of days trying to find a way of reading TIF image files which contain non-standard depth (12-bit per pixel) channels coming from high-speed cameras. So far, I've tested OpenCV, libTIFF, and TinyTIFF without success (throw same error: only 8,16,24, or 32 bit TIFFs are supported).
I guess at this point, either I'll have to somehow read the file as a binary and work from there (no idea how) or use imagemagick's convert utility to set the channel's depth to 16-bit. I'd really like to avoid the latter, since I want to make my code as light-weight and self-contained as possible. I'm processing hundreds of thousands of images, thus reading them two times (one to convert, one to post-process) seems quite counter-productive. Any Ideas?
As an example, to reproduce the error (use opencv, and libtiff):
TIFF* tif = TIFFOpen(imageName.c_str(), "r");
// Create a matrix to hold the tif image in
Mat image;
// check the tif is open
if (tif) {
do {
unsigned int width, height;
uint32* raster;
// get the size of the tiff
TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
uint npixels = width * height; // get the total number of pixels
raster = (uint32*)_TIFFmalloc(npixels * sizeof(uint32)); // allocate temp memory (must use the tiff library malloc)
if (raster == NULL) // check the raster's memory was allocaed
{
TIFFClose(tif);
throw bad_alloc();
}
// Check the tif read to the raster correctly
if (!TIFFReadRGBAImage(tif, width, height, raster, 0))
{
TIFFClose(tif);
throw runtime_error("Could not read the TIF appropriately");
}
image = Mat(width, height, CV_8UC4); // create a new matrix of w x h with 8 bits per channel and 4 channels (RGBA)
// iterate over all the pixels of the tif
for (uint x = 0; x < width; x++)
for (uint y = 0; y < height; y++)
{
uint32& TiffPixel = raster[y * width + x]; // read the current pixel of the TIF
Vec4b& pixel = image.at<Vec4b>(Point(y, x)); // read the current pixel of the matrix
pixel[0] = TIFFGetB(TiffPixel); // Set the pixel values as BGRA
pixel[1] = TIFFGetG(TiffPixel);
pixel[2] = TIFFGetR(TiffPixel);
pixel[3] = TIFFGetA(TiffPixel);
}
_TIFFfree(raster); // release temp memory
// Rotate the image 90 degrees couter clockwise
image = image.t();
flip(image, image, 0);
imshow("TIF Image", image); // show the image
waitKey(0); // wait for anykey before displaying next
} while (TIFFReadDirectory(tif)); // get the next tif
TIFFClose(tif); // close the tif file
}
And the input image is the following:
https://drive.google.com/file/d/15TR2mnczo0i6dRzmT1jzPIMoNH61DJi1/view?usp=sharing
EDIT 1
The camera model is the following:
https://photron.com/wp-content/uploads/2022/01/NOVA_4models_Rev.2022.01.11.pdf
All photographs come from the same Camera, with same bit-depth and appear to be uncompressed.
** TIFFINFO ** on a randomly selected tif outputs the following:
=== TIFF directory 0 ===
TIFF Directory at offset 0x22f5e (143198)
Image Width: 258 Image Length: 370
Bits/Sample: 12
Compression Scheme: None
Photometric Interpretation: min-is-black
Orientation: row 0 top, col 0 lhs
Samples/Pixel: 1
Rows/Strip: 21
Planar Configuration: single image plane
EDIT 2
I further tried ImageMagick's API, assuming that 'convert' can read TIFFs with arbitrary bitdepths:
// Create base image
Image image;
Mat cvImage;
try{
image.read(imageName);
// Set the image type to TrueColor DirectClass representation.
image.type(GrayscaleType);
// Ensure that there is only one reference to underlying image
// If this is not done, then image pixels will not be modified.
//image.modifyImage();
// Allocate pixel view
Pixels view(image);
// Set all pixels in region anchored at 38x36, with size 160x230 to green.
size_t columns = view.columns(); size_t rows = view.rows();
cvImage = Mat(columns, rows, CV_8UC(1)); // create a new matrix of w x h with 8 bits per channel and 4 channels (RGBA)
Quantum* pixels = view.get(0, 0, columns, rows);
for (ssize_t row = 0; row < rows; ++row)
for (ssize_t column = 0; column < columns; ++column)
{
uchar& pixel = cvImage.at<uchar>(cv::Point(column, row)); // read the current pixel of the matrix
pixel = *pixels++; // Set the pixel values as BGRA
}
imshow("TIF Image", cvImage); // show the image
cv::waitKey(0); // wait for anykey before displaying next
} catch (Magick::Exception& error_)
{
cout << "Caught exception: " << error_.what() << endl;
}
Unfortunately, the read method reads a zero size image. It doesn't crash even! So not luck yet.
EDIT 3: S§#TTY SOLUTION
Use
mogrify -format png *.tif