RAW 每像素 12 位数据格式

RAW 12 bits per pixel data format

我正在分析每像素 12 位、GRBG、Little Endian、1920x1280 分辨率的原始图像,但我对数据或 RGB 像素的存储方式感到困惑。图像大小为4915200字节,计算时为4915200/(1920x1280) = 2。这意味着每个像素占用2字节,2字节中的4位用于填充。我尝试使用十六进制编辑器编辑图像,但我不知道像素是如何存储在图像中的。如果您有任何想法,请分享。

Image Link

您可以将图像加载到 Numpy 数组中并像这样正确地重新整形:

import numpy as np

# Load image and reshape
img = np.fromfile('Image_12bpp_grbg_LittleEndian_1920x1280.raw',dtype=np.uint16).reshape((1280,1920))

print(img.shape)
(1280, 1920)

然后您可以去马赛克和缩放以获得 16 位 PNG。请注意,我不知道你的校准系数所以我猜:

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

import cv2
import numpy as np

filename = 'Image_12bpp_grbg_LittleEndian_1920x1280.raw'

# Set width and height
w, h = 1920, 1280

# Read mosaiced image as GRGRGR...
#                        BGBGBG...
bayer = np.fromfile(filename, dtype=np.uint16).reshape((h,w))

# Extract g0, g1, b, r from mosaic
g0 = bayer[0::2, 0::2]      # every second pixel down and across starting at 0,0
g1 = bayer[1::2, 1::2]      # every second pixel down and across starting at 1,1
r  = bayer[0::2, 1::2]      # every second pixel down and across starting at 0,1
b  = bayer[1::2, 0::2]      # every second pixel down and across starting at 1,0

# Apply (guessed) color matrix for 16-bit PNG
R = np.sqrt(r) * 1200
B = np.sqrt(b) * 2300
G = np.sqrt((g0+g1)/2) * 1300    # very crude

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

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

关键词: Python, raw, image processing, Bayer, de-Bayer, mosaic, demosaic, de-mosaic, GBRG, 12-bit.

That means each pixel takes 2 bytes and 4 bits in 2bytes are used for padding

嗯,有点。这意味着每个 sample 都存储在两个连续的字节中,并带有 4 位填充。但在原始图像中,样本通常不是像素,不完全是像素。原始图像还没有被去马赛克,它们毕竟是原始的。对于 GRGB,拜耳模式如下所示:

文件中的内容是 12+4 位样本的 1920x1280 网格,排列顺序与像素相同,但每个样本只有一个通道,即与其在拜耳模式。

此外,颜色 space 可能是线性的,而不是 Gamma 压缩的。除非您对其进行逆向工程,否则色彩平衡是未知的。一个合适的解码器应该有一个校准过的颜色矩阵,但我没有。

我结合了这两件事并猜测了一个色彩平衡来做一个真正基本的解码(去马赛克不好,只是为了证明上面的信息可能是准确的):

使用此 C# 代码:

    Bitmap bm = new Bitmap(1920, 1280);        
    for (int y = 0; y < 1280; y += 2)
    {
        int i = y * 1920 * 2;
        for (int x = 0; x < 1920; x += 2)
        {
            const int stride = 1920 * 2;
            int d0 = data[i] + (data[i + 1] << 8);
            int d1 = data[i + 2] + (data[i + 3] << 8);
            int d2 = data[i + stride] + (data[i + stride + 1] << 8);
            int d3 = data[i + stride + 2] + (data[i + stride + 3] << 8);
            i += 4;
            int r = Math.Min((int)(Math.Sqrt(d1) * 4.5), 255);
            int b = Math.Min((int)(Math.Sqrt(d2) * 9), 255);
            int g0 = Math.Min((int)(Math.Sqrt(d0) * 5), 255);
            int g3 = Math.Min((int)(Math.Sqrt(d3) * 5), 255);
            int g1 = Math.Min((int)(Math.Sqrt((d0 + d3) * 0.5) * 5), 255);
            bm.SetPixel(x, y,         Color.FromArgb(r, g0, b));
            bm.SetPixel(x + 1, y,     Color.FromArgb(r, g1, b));
            bm.SetPixel(x, y + 1,     Color.FromArgb(r, g1, b));
            bm.SetPixel(x + 1, y + 1, Color.FromArgb(r, g3, b));
        }
    }