2

I have a raw image from the MAPIR NDVI Camera and want to load it in MATLAB and export the two bands needed (NIR, red edge) as tif.

According to the manufacturer, the NIR data is stored in the blue channel and the visible light (red edge) in the red channel, as 16 bit.

I tried reading the file with the code from "How can I read in a RAW image in MATLAB?" but it creates these fragments and i'm not sure why this is happening. Either i read the file wrong, or it is because of a lower resolution of the NIR channel.

Here is the link to the RAW image used.

Community
  • 1
  • 1
  • RAW means no format, so RAW. RAW format is not a format, but a lack of one, each device in the world may have a different way of writting in RAW. When you say RAW, it means "whatever the device has measured, bit by bit". Thus, only you (or the manufacturer) knows how to exactly read your RAW data. Just follow the instructions – Ander Biguri Mar 27 '17 at 13:47
  • The instructions from the manufacturer are: "Use the QGIS Plugin", which is unfortunately not working on OSX. – Emotional_Cabbage Mar 27 '17 at 13:54
  • Well, either figure out the way of running QGID plugging (maybe a virtual machine) or make the manufacturer tell you how exactly is the file written. No other option, there is no way of guessing what the raw file contains – Ander Biguri Mar 27 '17 at 13:58
  • Do you know the dimensions of the image? W x H pixels? – Mark Setchell Mar 27 '17 at 14:46
  • The specification of the camera says it 4,032px by 3,024px, i.e. 12,192,768px. At 16-bit (2 bytes per pixel) that makes 24,385,536 bytes. Your file is 31,850,496 bytes though which is too much for one band/channel and not enough for two bands/channels (Red + NIR) so there is some compression or subsampling going on that you would have to ask the manufacturer about. – Mark Setchell Mar 27 '17 at 14:57
  • Thanks! But the resolution is actually 4,608 x 3,456 px – Emotional_Cabbage Mar 27 '17 at 16:12
  • 1
    Ok, I can do it then - I think it looks like this http://thesetchells.com/a.jpg My code is a bit untidy and I am busy for a bit, but I should have a solution for you later on. – Mark Setchell Mar 27 '17 at 16:54
  • I had some progress, but there still some missing information: **1.** I am not sure about the green color channel. **2.** There is missing information about the image processing algorithm. **3.** [color correction matrix](http://www.imatest.com/docs/colormatrix/) might be required. – Rotem Mar 27 '17 at 22:22

2 Answers2

1

Looks like the reason for the "fragments" is CFA (color filter array).
Pixels arrangement is like Bayer Mosaic as follows:

enter image description here

It isn't clear what is the CFA alignment.

You posted:

According to the manufacturer, the NIR data is stored in the blue channel and the visible light (red edge) in the red channel

But there are 4 possible combinations of Bayer alignment.
In Matlab demosaic function documentation combinations are referred as 'gbrg', 'grbg', 'bggr' and 'rggb'.

I don't know what is the most common alignment.
I can't say which channel supposed to be the "blue channel", and which is the "red channel". (all we know is they positioned diagonally).
I also couldn't find the alignment information in Google.

Since I couldn't tell which is which, I have extracted all 4 possible channels:

srcN = 4608; %Raw image width (number of columns).
srcM = 3456; %Raw image height (number of rows).

%Open raw image for reading.
f = fopen('2017_0321_134045_015.RAW', 'r');

%Read raw data into matrix I (use '*uint16' for keeping data in uint16 - no conversion to double).
I = fread(f, [srcN, srcM], '*uint16');

fclose(f);

%Transpose I, because RAW image format is row-major, and Matlab matrix format is column-major.
I = I';


%Assume Bayer mosaic sensor alignment.
%Seperate to mosaic components.
J1 = I(1:2:end, 1:2:end);
J2 = I(1:2:end, 2:2:end);
J3 = I(2:2:end, 1:2:end);
J4 = I(2:2:end, 2:2:end);

figure;imshow(J1);title('J1');
figure;imshow(J2);title('J2');
figure;imshow(J3);title('J3');
figure;imshow(J4);title('J4');

%Save all 4 images as tif
imwrite(J1, 'J1.tif');
imwrite(J2, 'J2.tif');
imwrite(J3, 'J3.tif');
imwrite(J4, 'J4.tif');

Here are the four images (shrank by a factor of x4):

J1:
enter image description here

J2:
enter image description here

J3:
enter image description here

J4:
enter image description here


Update:

Emotional_Cabbage posted the following reference image:
enter image description here

I tried to get the raw image to the reference, but I can't get it right.

I think the CFA alignment is:
J1 - Red color channel.
J2 and J3 - Monochromatic instead of green color channel.
J4 - NIR color channel.

Illustration:
enter image description here

I used the following processing code:

J = demosaic(I, 'rggb');
J = imadjust(J, [0.02, 0.98], [], 0.45); %Adjust image intensity, and use gamma value of 0.45
J(:,:,2) = J(:,:,2)*0.75; %Reduce the green by factor of 0.75
figure;imshow(J);

Result:
enter image description here

There is still some missing information for getting the right result...

Rotem
  • 30,366
  • 4
  • 32
  • 65
  • Thanks for your help! But sadly it doesn't seem to be quite right. The image is supposed to look like [this](http://i.imgur.com/MBf6Mhq.jpg). And no matter which combination i tried, i couldn't get the same result. Apparently the images are stored as chunks of 12 bit in a 16 bit _word_. So the first 12 bits are one pixel and the remaining 4 and the following 8 are the next pixel. – Emotional_Cabbage Mar 27 '17 at 18:09
  • @Emotional_Cabbage That cannot be correct, Mr Cabbage. 4,032px by 3,024px makes 15,925,248 pixels and your file is exactly twice that in bytes, so you have 2 bytes per pixel, i.e. 1 byte for red and 1 byte for NIR, not 12-bits of red and 12-bits of NIR. – Mark Setchell Mar 27 '17 at 19:06
  • _All 16 bits are used by the RAW format, but the pixels are chunked in a way that some of the data for each pixel overlaps into the next word. So the first 12 bits of the first word will be one pixel, then the next 4 bits of that word will be the next pixel, then the first 8 bits of the next word will be the rest of the second pixel, etc._ Is what the support told us. – Emotional_Cabbage Mar 27 '17 at 19:11
  • @Emotional_Cabbage That cannot be correct. If so, you would need 3 bytes (24 bits) to store 2 pixels (of 12 bits each) so your file would need to be 4608*3456*3, or 47775744 bytes, but it is only 31850496 bytes. – Mark Setchell Mar 27 '17 at 19:16
  • @Emotional_Cabbage Also, the image you say is *"correct"* has three channels all of which are unique, yet the RAW image has 2 channels so where did the 3rd channel come from? It is not a copy of any other channel, since the 3 channels all have different means and standard deviations. – Mark Setchell Mar 27 '17 at 19:18
  • Why times 3? We assume that we only have the nir and red edge channel. With 4608*3457*2 we would get 31,850,496, or the size of the image. – Emotional_Cabbage Mar 27 '17 at 19:19
  • I don't know where the third channel comes from. If you take a picture, the raw and jpg are created seperately. – Emotional_Cabbage Mar 27 '17 at 19:20
  • @Emotional_Cabbage If you have 12 bits for red and 12 bits for NIR channel, you will have 24 bits for each pixel - i.e. 3 bytes - hence times 3. – Mark Setchell Mar 27 '17 at 19:22
  • @Emotional_Cabbage I am not trying to argue with you - I am trying to help you but their support needs to tell us the correct information then we can decode it on a Mac for you. – Mark Setchell Mar 27 '17 at 19:25
  • And i'm really glad for your help, but i can't tell you anything else. Guess we have to wait then. – Emotional_Cabbage Mar 27 '17 at 19:31
  • The only way to get the `raw` image looking like the `jpg` image, is by using image processing techniques. (Like the only way to get RGB colored image from Bayer image is by using image processing). – Rotem Mar 27 '17 at 19:56
  • I had some progress, but there still some missing information: **1.** I am not sure about the green color channel. **2.** There is missing information about the image processing algorithm. **3.** [color correction matrix](http://www.imatest.com/docs/colormatrix/) might be required. – Rotem Mar 27 '17 at 22:21
  • Your edit did it! I got the QGIS Plugin running on Windows, and created a reference TIF which looks exactly like your image without the color correction. [Yours](https://drive.google.com/open?id=0B0LBNu7HlEuFa2JOc2RPUlhHSE0) [QGIS](https://drive.google.com/open?id=0B0LBNu7HlEuFaGQ0WUlYUDJqMVE). Thank you very much! – Emotional_Cabbage Mar 28 '17 at 08:12
1

Although you already have accepted another answer, I wanted to save the results of my efforts here in case other folk have similar problems. This code runs on a Mac or Linux, per original question.

////////////////////////////////////////////////////////////////////////////////
// raw2tif.c
// Mark Setchell
//
// Reads a RAW file from a MAPIR NDVI camera and converts it to a TIF
//
// Usage:
// raw2tif RAWfile result.tif
////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include "tiffio.h"

#define WIDTH   4608
#define HEIGHT  3456
#define RAW_BITSPERSAMPLE   8
#define RAW_SAMPLESPERPIXEL 2
#define TIF_BITSPERSAMPLE   8
#define TIF_SAMPLESPERPIXEL 3

int main(int argc,char* argv[])
{
   char ifilename[128];
   char ofilename[128];
   unsigned char inrow[WIDTH*RAW_BITSPERSAMPLE*RAW_SAMPLESPERPIXEL/8];
   FILE* in;

   // Check input and output file names supplied
   if(argc!=3){
      fprintf(stderr,"Usage: raw2tif RAWfile TIFfile");
      exit(EXIT_FAILURE);
   }

   // Pick up arguments - infile (RAW) and outfile (TIF)
   strcpy(ifilename,argv[1]);
   strcpy(ofilename,argv[2]);

   // Open input file
   in=fopen(ifilename,"rb");
   if(in==NULL){
      fprintf(stderr,"ERROR: Unable to open RAW input file");
      exit(EXIT_FAILURE);
   }

   // Open output file
   TIFF *tif= TIFFOpen(ofilename, "w");
   if(tif==NULL){
      fprintf(stderr,"ERROR: Unable to open output file");
      exit(EXIT_FAILURE);
   }

   // Set baseline tags
   TIFFSetField(tif,TIFFTAG_IMAGEWIDTH,WIDTH);
   TIFFSetField(tif,TIFFTAG_IMAGELENGTH,HEIGHT);
   TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,TIF_BITSPERSAMPLE);
   TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,TIF_SAMPLESPERPIXEL);
   TIFFSetField(tif,TIFFTAG_ORIENTATION,ORIENTATION_TOPLEFT);
   TIFFSetField(tif,TIFFTAG_COMPRESSION,COMPRESSION_LZW);
   TIFFSetField(tif,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG);
   TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,PHOTOMETRIC_MINISBLACK);

   uint32  rowsPerStrip;
   rowsPerStrip = HEIGHT;
   rowsPerStrip = TIFFDefaultStripSize(tif, rowsPerStrip);
   TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, rowsPerStrip);
   TIFFSetupStrips(tif);

   // Line buffer
   int scanlineSize = TIFFScanlineSize(tif);
   unsigned char* scanline = (unsigned char*) _TIFFmalloc(scanlineSize);

   // Iterate over rows of RAW file writing rows of TIF file
   for(int row=0;row<HEIGHT;row++)
   {
      if(fread(inrow,WIDTH*RAW_BITSPERSAMPLE*RAW_SAMPLESPERPIXEL/8,1,in)!=1){
         fprintf(stderr,"ERROR: Error reading input file at row %d\n",row);
         exit(EXIT_FAILURE);
      }
      // Following few lines need correcting when input file format is known
      unsigned char* ip=inrow;
      unsigned char* op=scanline;
      for(int col=0;col<WIDTH;col++){
         *op++=*ip++;  // Write RAW red to TIF red
         *op++=0;      // Set TIF green to 0
         *op++=*ip++;  // Write RAW NIR to TIF blue
      }
      if(TIFFWriteScanline(tif,scanline,row,0) != 1){
         fprintf(stderr,"ERROR: Error writing output file at row %d\n",row);
         exit(EXIT_FAILURE);
      }
   }

   // Clean up
   _TIFFfree(scanline);
   TIFFClose(tif);
}

A suitable Makefile is:

CC=clang -Wall -pedantic
TIF_INC=-I/usr/include -I/usr/local/include
TIF_LIB=-L/usr/lib -L/usr/local/lib -ltiff

raw2tif: raw2tif.c
    $(CC) raw2tif.c $(TIF_INC) $(TIF_LIB) -o raw2tif
Mark Setchell
  • 191,897
  • 31
  • 273
  • 432