2

I'm trying to read and save a 12bit Raw file using Python and openCV. The code I'm using saves an image but the saved image is garbled / distorted.

The image is from a FLIR Oryx Camera 23MP (5320x4600) 12Bit with BayerRG12P pixelformat, which should be RG GB bayer pattern.

import cv2
import numpy as np

width = 5320
height = 4600

with open("aaa12packed.Raw", "rb") as rawimg:
   img = np.fromfile(rawimg, np.dtype('u1'), width * height).reshape(height, width)
   colimg = cv2.cvtColor(img, cv2.COLOR_BAYER_GR2RGB)
   cv2.imwrite("test.jpeg", colimg)

I uploaded two raw files that I'm using to test the debayer/demoisaic. You can download them using the link below:

"RG12P.Raw" (this is the 12bit regular) and "RG12packed.Raw" (this is the 12bit packed)

Raw files download

** My end goal is to use openCV to process folders containing RAW image sequences and process/convert them to another high resolution format like DPX 10bit or equivalent.

I'm very new to this - any help is appreciated.

EDIT#1: Adding screenshot with information from FLIR manual about the 12bits formats used.

Link to 12 bit channel image formats (packed and regular)

Marvguitar
  • 23
  • 1
  • 4

2 Answers2

3

We may reuse my answer from the following post.

OpenCV doesn't support DPX 10bit image format.
You may use Tiff format or 16 bits PNG, but you may need to scale the pixels by 16 (placing the 12 bits in the upper part of the 16 bits).


Code sample:

import cv2
import numpy as np

width = 5320
height = 4600

with open("RG12P.Raw", "rb") as rawimg:
    # Read the packed 12bits as bytes - each 3 bytes applies 2 pixels
    data = np.fromfile(rawimg, np.uint8, width * height * 3//2)

    data = data.astype(np.uint16)  # Cast the data to uint16 type.
    result = np.zeros(data.size*2//3, np.uint16)  # Initialize matrix for storing the pixels.

    # 12 bits packing: ######## ######## ########
    #                  | 8bits| | 4 | 4  |  8   |
    #                  |  lsb | |msb|lsb |  msb |
    #                  <-----------><----------->
    #                     12 bits       12 bits
    result[0::2] = ((data[1::3] & 15) << 8) | data[0::3]
    result[1::2] = (data[1::3] >> 4) | (data[2::3] << 4)
    bayer_im = np.reshape(result, (height, width))

    # Apply Demosacing (COLOR_BAYER_BG2BGR gives the best result out of the 4 combinations).
    bgr = cv2.cvtColor(bayer_im, cv2.COLOR_BAYER_BG2BGR)  # The result is BGR format with 16 bits per pixel and 12 bits range [0, 2^12-1].

    # Show image for testing (multiply by 16 because imshow requires full uint16 range [0, 2^16-1]).
    cv2.imshow('bgr', cv2.resize(bgr*16, [width//10, height//10]))
    cv2.waitKey()
    cv2.destroyAllWindows()

    # Convert to uint8 before saving as JPEG (not part of the conversion).
    colimg = np.round(bgr.astype(float) * (255/4095))
    cv2.imwrite("test.jpeg", colimg)

Result:
enter image description here

Rotem
  • 30,366
  • 4
  • 32
  • 65
  • This works! Next step would be converting all raw from a folder, if it's not too complicated would be great to add to the code. Thanks again for this! – Marvguitar Dec 29 '21 at 23:17
  • Iterating all files with `.raw` extension within a folder is simple, but out of the scope of your question. There are so many examples as [here](https://stackoverflow.com/questions/3964681/find-all-files-in-a-directory-with-extension-txt-in-python) and [here](https://stackoverflow.com/questions/30230592/loading-all-images-using-imread-from-a-given-folder) and [here](https://stackoverflow.com/questions/57217941/converting-all-files-jpg-to-png-from-a-directory-in-python). You may choose the destination image file name to be the same as the source name , but with different extension. – Rotem Dec 29 '21 at 23:28
1

I'm not sure of the difference between the two files you shared, but here is a fairly speedy technique to read and unpack the 12-bit samples. I am not really sure what you actually want so I have put plenty of debug code and comments so you can see what I am doing then you can fine-tune.

Don't be put off by the length of the code, there are only really 4 lines which I have numbered in comments:

#!/usr/bin/env python3
# Demosaicing Bayer Raw image

import cv2
import numpy as np

filename = 'RG12P.Raw'

# Set width and height
w, h = 5320, 4600

# Read entire file and reshape as rows of 3 bytes
bayer = np.fromfile(filename, dtype=np.uint8).reshape((-1,3))    # LINE 1
print(f'DEBUG: bayer.shape={bayer.shape}')
for i in 0,1,2:
   print(f'DEBUG: bayer[{i}]={bayer[i]}')

# Make each 3-byte row into a single np.uint32 containing 2 samples of 12-bits each
bayer32 = np.dot(bayer.astype(np.uint32), [1,256,65536])         # LINE 2
print(f'DEBUG: bayer32.shape={bayer32.shape}')
for i in 0,1,2:
   print(f'DEBUG: bayer32[{i}]={bayer32[i]}')

# Extract high and low samples from pairs
his = bayer32 >> 12                                              # LINE 3
los = bayer32 &  0xfff                                           # LINE 4

print(f'DEBUG: his.shape={his.shape}')
for i in 0,1,2:
   print(f'DEBUG: his[{i}]={his[i]}')

print(f'DEBUG: los.shape={los.shape}')
for i in 0,1,2:
   print(f'DEBUG: los[{i}]={los[i]}')


# Stack into 3 channel
#BGR16 = np.dstack((B,G,R)).astype(np.uint16)

# Save result as 16-bit PNG
#cv2.imwrite('result.png', BGR16)

Sample Output

DEBUG: bayer.shape=(12236000, 3)
DEBUG: bayer[0]=[119 209  36]
DEBUG: bayer[1]=[249  17  29]
DEBUG: bayer[2]=[ 49 114  35]
DEBUG: bayer32.shape=(12236000,)
DEBUG: bayer32[0]=2412919
DEBUG: bayer32[1]=1905145
DEBUG: bayer32[2]=2322993
DEBUG: his.shape=(12236000,)
DEBUG: his[0]=589
DEBUG: his[1]=465
DEBUG: his[2]=567
DEBUG: los.shape=(12236000,)
DEBUG: los[0]=375
DEBUG: los[1]=505
DEBUG: los[2]=561
Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • Thanks Mark ! I get same results as you in debug. However it doesn't save the image when I uncomment the "stack in 3 channels" and "save result.. " lines because B G R variables are not set which I assume would be using some of the numbers seen in the debug? you can see more details about image formats in the OP, I added an screenshot from the manual explaining the 12 bit formats in the images I linked. Basically two 12bit pixels packed in 3 bytes. – Marvguitar Dec 29 '21 at 17:39
  • My code assumes the first of the two packing formats you have now added the description for. The code for saving the image is commented out because I don't know what you actually want. Basically, you have a list (actually a Numpy array) of all the high 12-bit samples and another array of all the low 12-bit samples. You can reshape them and stack them however you need them for your subsequent analysis. – Mark Setchell Dec 29 '21 at 20:04
  • Thanks for explaining this - what I want is to debayer the RAW image and save it as a 10bit color image (DPX, TIFF ). I don't understand what to put in the stack, it looks like the stack is the "demosaic method" using the data you extracted? – Marvguitar Dec 29 '21 at 21:47