schoolpost / PiDNG

Create Adobe DNG RAW files using Python. Works with any Bayer RAW Data including native support for Raspberry Pi cameras.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Raspiraw support

stefandouw opened this issue · comments

I tried for 2 days now to modify core.py to accept files from raspiraw output but they turn out really blue and has terrible noise. Would it be possible to add support for raspiraw?

raspiraw: https://github.com/6by9/raspiraw
useful info about unpacking raspiraw files: https://www.strollswithmydog.com/open-raspberry-pi-high-quality-camera-raw/

You can refer to this issue here to see how this can be done. You shouldn't need to edit core.py.
#28

Thank you so much! I should've checked the closed issue's, my bad.

Would it be possible to add an example file that can convert using raspiraw files? I tried multiple times with #28 as reference but without any usable result. My dng's were either heavily purple tinted with a lot of noise or straight up black. Any help would be appreciated, thank you!

Would it be possible to add an example file that can convert using raspiraw files? I tried multiple times with #28 as reference but without any usable result. My dng's were either heavily purple tinted with a lot of noise or straight up black. Any help would be appreciated, thank you!

I'll look into creating an example script for this use case, can you send me a folder ( zipped ) of the raspiraw's you wish to convert? The header is also required so we can know whether the image is flipped or not to figure out bayer order.

Thank you very much for looking into it, the zip file can be found here.

Inside you will find a raspiraw raw file created with the following command:
~/raspiraw/raspiraw -t 100 -g 1010 -eus 5000 -hd0 /dev/shm/hd0.32k -o /dev/shm/out.%03d.raw
Included is also the separate header file and raspiraw raw file + header file combined using:
ls /dev/shm/.raw | while read i; do cat /dev/shm/hd0.32k "$i" > "$i".all; done
Also the combined file converted to a tiff using dcraw:
ls /dev/shm/
.all | while read i; do ~/dcraw/dcraw -f -o 1 -v -6 -T -q 3 -W "$i"; done
Finally also a jpg created with raspistill (in case you need a reference):
raspistill -t 100 -o raspistill.jpg -vf -hf

If you need anything else, please let me know. Thank you very much!

Thank you very much for looking into it, the zip file can be found here.

Inside you will find a raspiraw raw file created with the following command:
~/raspiraw/raspiraw -t 100 -g 1010 -eus 5000 -hd0 /dev/shm/hd0.32k -o /dev/shm/out.%03d.raw
Included is also the separate header file and raspiraw raw file + header file combined using:
ls /dev/shm/.raw | while read i; do cat /dev/shm/hd0.32k "$i" > "$i".all; done Also the combined file converted to a tiff using dcraw: ls /dev/shm/.all | while read i; do ~/dcraw/dcraw -f -o 1 -v -6 -T -q 3 -W "$i"; done
Finally also a jpg created with raspistill (in case you need a reference):
raspistill -t 100 -o raspistill.jpg -vf -hf

If you need anything else, please let me know. Thank you very much!

Below is a simple script that given a header file and a .raw file will output a DNG.
It's not perfect and lacks some metadata that the raspistill jpeg+raw contains so I will still advise if you can use that format to do that instead.

import ctypes
import sys
import os
import io
from pydng.core import RAW2DNG, DNGTags, Tag
import numpy as np
import struct, collections

hdr = "hd0.32k"
rawFile = 'out.001.raw'

class BroadcomRawHeader(ctypes.Structure):
    _fields_ = [
        ('name',          ctypes.c_char * 32),
        ('width',         ctypes.c_uint16),
        ('height',        ctypes.c_uint16),
        ('padding_right', ctypes.c_uint16),
        ('padding_down',  ctypes.c_uint16),
        ('dummy',         ctypes.c_uint32 * 6),
        ('transform',     ctypes.c_uint16),
        ('format',        ctypes.c_uint16),
        ('bayer_order',   ctypes.c_uint8),
        ('bayer_format',  ctypes.c_uint8),
    ]

BAYER_ORDER = {
    0: [0, 1, 1, 2],
    1: [1, 2, 0, 1],
    2: [2, 1, 1, 0],
    3: [1, 0, 2, 1],
}

file = open(hdr, 'rb')
img = io.BytesIO(file.read())

data = img.getvalue()
assert data[:4] == 'BRCM'.encode("ascii")
header = BroadcomRawHeader.from_buffer_copy(data[176:176 + ctypes.sizeof(BroadcomRawHeader)])


# image specs
width = header.width
height = header.height
bpp= 12

# load raw data into 16-bit numpy array.
numPixels = width*height
rf = open(rawFile, mode='rb')
img = io.BytesIO(rf.read())
data = img.getvalue()
data = np.frombuffer(data, dtype=np.uint8)

reshape, crop = {
    1: ((1952, 3264), (1944, 3240)),
    2: ((2480, 4128), (2464, 4100)),
    3: ((3040, 6112), (3040, 6084)),                #raspiraw seems to omit rows vs the embedded raw in the jpgs, 3040 instead of 3056.
}[3]
data = data.reshape(reshape)[:crop[0], :crop[1]]
data = data.astype(np.uint16)
shape = data.shape
unpacked_data = np.zeros(
    (shape[0], int(shape[1] / 3 * 2)), dtype=np.uint16)
unpacked_data[:, ::2] = (data[:, ::3] << 4) + \
    (data[:, 2::3] & 0x0F)
unpacked_data[:, 1::2] = (
    data[:, 1::3] << 4) + ((data[:, 2::3] >> 4) & 0x0F)
data = unpacked_data


ccm1 = [[6759, 10000], [-2379, 10000], [751, 10000],
        [-4432, 10000], [13871, 10000], [5465, 10000],
        [-401, 10000], [1664, 10000], [7845, 10000]]

ccm2 = [[5603, 10000], [-1351, 10000], [-600, 10000],
        [-2872, 10000], [11180, 10000], [2132, 10000],
        [600, 10000], [453, 10000], [5821, 10000]]

fm1 = [[7889, 10000], [1273, 10000], [482, 10000],
        [2401, 10000], [9705, 10000], [-2106, 10000],
        [-26, 10000], [-4406, 10000], [12683, 10000]]

fm2 = [[6591, 10000], [3034, 10000], [18, 10000],
        [1991, 10000], [10585, 10000], [-2575, 10000],
        [-493, 10000], [-919, 10000], [9663, 10000]]

ci1 = 17
ci2 = 21

# set DNG tags.
t = DNGTags()
t.set(Tag.ImageWidth, width)
t.set(Tag.ImageLength, height)
t.set(Tag.TileWidth, width)
t.set(Tag.TileLength, height)
t.set(Tag.Orientation, 1)
t.set(Tag.PhotometricInterpretation, 32803)
t.set(Tag.SamplesPerPixel, 1)
t.set(Tag.BitsPerSample, bpp)
t.set(Tag.CFARepeatPatternDim, [2,2])
t.set(Tag.CFAPattern, BAYER_ORDER[header.bayer_order])
t.set(Tag.BlackLevel, (4096 >> (16 - bpp)))
t.set(Tag.WhiteLevel, ((1 << bpp) -1) )
t.set(Tag.ColorMatrix1, ccm1)
t.set(Tag.ColorMatrix2, ccm2)
t.set(Tag.ForwardMatrix1, fm1)
t.set(Tag.ForwardMatrix2, fm2)
t.set(Tag.CalibrationIlluminant1, ci1)
t.set(Tag.CalibrationIlluminant2, ci2)
t.set(Tag.AsShotNeutral, [[1,1],[1,1],[1,1]])
t.set(Tag.DNGVersion, [1, 4, 0, 0])
t.set(Tag.DNGBackwardVersion, [1, 2, 0, 0])
t.set(Tag.Make, "Camera Brand")
t.set(Tag.Model, "Camera Model")
t.set(Tag.PreviewColorSpace, 2)

# save to dng file.
RAW2DNG().convert(data, tags=t, filename="custom", path="")

Thank you very much for taking the time! Do you know what header information is missing and causing this heavy noise?
I did manage to find a post of somebody successfully and properly unpacking the data, I just don't know how to reproduce those results with python.

You should use raspistill with the raw option turned on, instead of raspiraw. That's what they are doing in the article you posted.

The jpeg+raw from raspistill gives you extra exif information and makernote metadata that is used in the dng metadata.