12 bits images from argus camera

Hi,

I’m trying to access in python 12 bits images from a camera (IMX485) on the AGX ORIN.
Untill now, I was using jetson.utils to access this camera, but I can only get 8bit images:

import jetson.utils

def display_csi_camera():
    # Create the camera instance
    camera = jetson.utils.gstCamera(3840, 2160, "csi://0") 

    # Create the display instance
    display = jetson.utils.glDisplay()

    # Main loop to capture and display frames from the camera
    while display.IsOpen():
        # Capture a frame from the camera
        img, width, height = camera.CaptureRGBA(zeroCopy=1)

        # Render the frame
        display.RenderOnce(img, width, height)

        # Update the window title with the current frames per second (FPS)
        display.SetTitle("CSI Camera | {:.1f} FPS".format(display.GetFPS()))

        # Check for user exit (Esc key)
        if display.IsClosed():
            break

# Call the main function to display the camera feed
if __name__ == "__main__":
    display_csi_camera()

I didn’t find a way to get 12bit images using this method.

I also tried another method based on gst commands:

import gi
gi.require_version('Gst', '1.0')
from gi.repository import Gst, GLib
import numpy as np
import torch
import torchvision.transforms as transforms

class GStreamerCapture:
    def __init__(self):
        self.pipeline = None
        self.appsink = None
        self.frames = []
        self.sample_count = 0
        self.max_samples = 10  # You can adjust this

    def start_pipeline(self, pipeline_str):
        self.pipeline = Gst.parse_launch(pipeline_str)
        self.appsink = self.pipeline.get_by_name("sink")
        self.appsink.set_property("emit-signals", True)
        self.appsink.set_property("max-buffers", 1)
        self.appsink.connect("new-sample", self.on_new_sample)
        self.pipeline.set_state(Gst.State.PLAYING)

    def on_new_sample(self, appsink):
        sample = appsink.emit("pull-sample")
        if sample:
            self.sample_count += 1
            buffer = sample.get_buffer()
            caps = sample.get_caps()
            width = caps.get_structure(0).get_value("width")
            height = caps.get_structure(0).get_value("height")
            frame = np.ndarray(
                (height, width, 3),
                buffer=buffer.extract_dup(0, buffer.get_size()),
                dtype=np.uint8,
            )
            self.frames.append(frame)

            if self.sample_count >= self.max_samples:
                self.pipeline.set_state(Gst.State.NULL)
                GLib.MainLoop().quit()
        return Gst.FlowReturn.OK

    def capture_frames(self):
        loop = GLib.MainLoop()
        loop.run()
        return self.frames

def convert_frames_to_tensors(frames):
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    tensor_frames = []

    for frame in frames:
        image = torch.tensor(frame, dtype=torch.uint8)
        image = image.permute(2, 0, 1)  # Channels-first
        image = image.float().div(255)  # Normalize to [0, 1]
        tensor_frames.append(image)

    tensor_frames = torch.stack(tensor_frames).to(device)
    return tensor_frames

if __name__ == "__main__":
    Gst.init(None)

    pipeline_str = (
        "nvarguscamerasrc ! "
        "video/x-raw(memory:NVMM), format=NV12, width=3840, height=2160, framerate=60/1 ! "
        "nvvidconv ! video/x-raw, format=BGRx ! "
        "videoconvert ! video/x-raw, format=BGR ! "
        "appsink name=sink emit-signals=True max-buffers=1"
    )

    gstreamer_capture = GStreamerCapture()
    gstreamer_capture.start_pipeline(pipeline_str)
    frames = gstreamer_capture.capture_frames()

    tensor_frames = convert_frames_to_tensors(frames)
    print(tensor_frames.shape)  # Print the shape of the tensor frames

I am not yet even sure it is correct because I realized I wasn’t even able to display a 12 bit image using gstreamer only:

gst-launch-1.0 nvarguscamerasrc ! 'video/x-raw(memory:NVMM), width=3840, height=2160, framerate=60/1, format=NV12' ! nvvidconv ! 'video/x-raw(memory:NVMM), format=NV12' ! autovideosink
Setting pipeline to PAUSED ...
Pipeline is live and does not need PREROLL ...
Setting pipeline to PLAYING ...
New clock: GstSystemClock
GST_ARGUS: Creating output stream
CONSUMER: Waiting until producer is connected...
GST_ARGUS: Available Sensor modes :
GST_ARGUS: 3840 x 2160 FR = 50,000000 fps Duration = 20000000 ; Analog Gain range min 1,000000, max 31,622776; Exposure Range min 450000, max 400000000;

GST_ARGUS: 3840 x 2160 FR = 59,999999 fps Duration = 16666667 ; Analog Gain range min 1,000000, max 31,622776; Exposure Range min 450000, max 400000000;

GST_ARGUS: 1920 x 1080 FR = 90,000001 fps Duration = 11111111 ; Analog Gain range min 1,000000, max 31,622776; Exposure Range min 450000, max 400000000;

GST_ARGUS: 1920 x 1080 FR = 90,000001 fps Duration = 11111111 ; Analog Gain range min 1,000000, max 31,622776; Exposure Range min 450000, max 400000000;

GST_ARGUS: Running with following settings:
   Camera index = 0
   Camera mode  = 1
   Output Stream W = 3840 H = 2160
   seconds to Run    = 0
   Frame Rate = 59,999999
GST_ARGUS: Setup Complete, Starting captures for 0 seconds
GST_ARGUS: Starting repeat capture requests.
CONSUMER: Producer has connected; continuing.
ERROR: from element /GstPipeline:pipeline0/GstNvArgusCameraSrc:nvarguscamerasrc0: Internal data stream error.
Additional debug info:
gstbasesrc.c(3072): gst_base_src_loop (): /GstPipeline:pipeline0/GstNvArgusCameraSrc:nvarguscamerasrc0:
streaming stopped, reason not-negotiated (-4)
Execution ended after 0:00:00.647558925
Setting pipeline to NULL ...
GST_ARGUS: Cleaning up
CONSUMER: Done Success
GST_ARGUS: Done Success
Freeing pipeline ...


Does anyone know how to get these 12 bits images in python (ideally directly on the GPU to use with pytorch), or at least what is the correct gstreamer command to get them?

Thank you very much for your help!

Sorry to tell nvarguscamerasrc current only support YUV420 8bit.

export DISPLAY=:0
gst-launch-1.0 nvarguscamerasrc sensor-id=2 ! 'video/x-raw(memory:NVMM),framerate=30/1' ! nvvidconv ! fpsdisplaysink video-sink=xvimagesink -v

Thanks

Thank you for this information.

How can I efficiently access 12 bits images from my camera to treat them using pytorch?

Thank you for your help

As my previous nvarguscamerasrc can’t output 12bit

Does it means that there is absolutly no way to use my camera in 12bits? Aren’t there any alternative way to access this feature?

No any other way except using external ISP instead of Orin ISP.

Thanks

Thank you for your answer.
That’s quite dispappointing from the company selling me this camera specially designed for AGX ORIN. They clearly state in there selling page " Output Format: RAW Bayer 10/12-bit" which make me think I could get 12bit images …

I contacted their support to ask them how I’m supposed to do to get these 12bits images.
If I get a relevant answer, I will post it here for those interested.

Below command could be support YUV420 10bits but I am sure 12bits don’t support.

gst-launch-1.0 nvarguscamerasrc num-buffers=300 ! 'video/x-raw(memory:NVMM),format=P010_10LE' ! ... ..... .. . .

Thank you!
Unfortunatly I wasn’t able to make it work

WARNING: erroneous pipeline: could not link nvarguscamerasrc0 to nvjpegenc0, neither element can handle caps video/x-raw(memory:NVMM), format=(string)P010_10LE, width=(int)3840, height=(int)2160

I have another question (maybe it’s stupid, I’m not clear about at which stage the ISP comes): is it possible to get directly the untreated raw bayer12bit data? I think I could directly use it on GPU for my use case.

I tried this:

gst-launch-1.0 nvarguscamerasrc sensor-id=0 num-buffers=1 ! "video/x-raw(memory:NVMM), format=(string)NV12, width=(int)3840, height=(int)2160" ! nvvidconv ! "video/x-bayer, format=(string)grbg12, width=(int)3840, height=(int)2160" ! filesink location=test.raw

But I got this message:

WARNING: erroneous pipeline: could not link nvvconv0 to filesink0, nvvconv0 can't handle caps video/x-bayer, format=(string)grbg12, width=(int)3840, height=(int)2160

I’m not sure if it means my pipeline is incorrect or that the ORIN can’t do this.

Thank you very much for your help

For raw data you can use v4l2 APP implement by yourself to get the 12bit raw image.
You can check the v4l2-ctl as sample below is the command to capture the raw data without preview.

v4l2-ctl -d /dev/video0 --set-fmt-video=width=1920,height=1080,pixelformat=RG10 --set-ctrl bypass_mode=0 --stream-mmap

Thank you very much for your answer

I think I succeed to get 12 bit raw images:

v4l2-ctl -v width=3840,height=2160,pixelformat=‘RG12’ --set-ctrl frame_rate=60000000 -c bypass_mode=0 -c sensor_mode=0 --stream-mmap --stream-count=1 --stream-to=imag.raw -d /dev/video0

At least the file size seems to correspond: 16588800 bytes = 3840 x 2160 x16

I’m still struggling to get it work with python, but once it works correctly, I will post the code here for those interested.

If the python use v4l2src you need to modify it to support bayer format sensor.

Maybe reference to below.

Additional note: you may also try having this commit bayer2rgb: Support video/x-bayer 10/12/14/16 bit depths (4c92d409) · Commits · GStreamer / gstreamer · GitLab

Furthermore, you may better tell your use case for better advice.
Depending on your needs, not everything may work for more than 8 bits depth with gstreamer or NVIDIA accelerated plugins.

Thank you very much for these indications.

Furthermore, you may better tell your use case for better advice.

I’m designing a complex sensor. At the end of the optical chain, I project a signal on the camera sensor. I need to precisely measure both the location and the intensity of this signal to get a precise measure (hence the 12bits need, btw if you know another camera easier to use with the ORIN and providing 12bits with >=4k resolution and >30fps, don’t hesitate to tell me ;-) ). I don’t need to reconstruct colors (in fact if it was black and white, that would be even better for me), thus I should be able to easely work with a raw RGB12 format if I can get it (ideally on GPU).

Maybe reference to below.
Additional note: you may also try having this commit

I would prefer to avoid patching and recompiling v4l2 if possible. As my previous command was giving a file which seemed correct, I was imagining it should be easy to pipe these data in some way to python (I don’t need to debayer it or anything, a raw 12bit table should be perfect for me). But I haven’t succeed yet.

Currectly I’m trying to directly read the raw data:
thank you chatGPT, this would be beyond my capabilities otherwise

import v4l2
import fcntl
import mmap
import numpy as np

# Open the V4L2 device
video_device = '/dev/video0'
fd = open(video_device, 'rb+', buffering=0)
cp = v4l2.v4l2_capability()
fcntl.ioctl(fd, v4l2.VIDIOC_QUERYCAP, cp)

# Set the desired format (adjust these values as needed)
fmt = v4l2.v4l2_format()
fmt.type = v4l2.V4L2_BUF_TYPE_VIDEO_CAPTURE
fmt.fmt.pix.width = 1920
fmt.fmt.pix.height = 1080
fmt.fmt.pix.pixelformat = 0x21424752  # 0x21424752 is the numeric code for V4L2_PIX_FMT_SRGGB12
fcntl.ioctl(fd, v4l2.VIDIOC_S_FMT, fmt)

# Request a buffer
req = v4l2.v4l2_requestbuffers()
req.count = 1
req.type = v4l2.V4L2_BUF_TYPE_VIDEO_CAPTURE
req.memory = v4l2.V4L2_MEMORY_MMAP
fcntl.ioctl(fd, v4l2.VIDIOC_REQBUFS, req)

# Map the buffer
buf = v4l2.v4l2_buffer()
buf.type = v4l2.V4L2_BUF_TYPE_VIDEO_CAPTURE
buf.memory = v4l2.V4L2_MEMORY_MMAP
buf.index = 0
fcntl.ioctl(fd, v4l2.VIDIOC_QUERYBUF, buf)
mmapped_data = mmap.mmap(fd.fileno(), buf.length, mmap.MAP_SHARED, mmap.PROT_READ | mmap.PROT_WRITE, offset=buf.m.offset)

# Queue the buffer for capture
fcntl.ioctl(fd, v4l2.VIDIOC_QBUF, buf)

# Start streaming
buf_type = v4l2.V4L2_BUF_TYPE_VIDEO_CAPTURE
fcntl.ioctl(fd, v4l2.VIDIOC_STREAMON, buf_type)

# Capture a frame
fcntl.ioctl(fd, v4l2.VIDIOC_DQBUF, buf)

# Process the raw image data
raw_image = np.frombuffer(mmapped_data, dtype=np.uint16, count=buf.bytesused//2)  # Use 'bytesused' instead of 'length'
raw_image = raw_image.reshape((fmt.fmt.pix.height, fmt.fmt.pix.width))

# Stop streaming and release resources
fcntl.ioctl(fd, v4l2.VIDIOC_STREAMOFF, buf_type)
mmapped_data.close()
fd.close()

# Now you have the raw image data in the 'raw_image' numpy array

But it is not yet working, I’m having a bad adress error that I don’t understand (‘/dev/video0’ exists)

    fcntl.ioctl(fd, v4l2.VIDIOC_STREAMON, buf_type)
OSError: [Errno 14] Bad address

If you have any idea on how to make this code work or any other way to efficiently get these RGB12 images in python, that would be really helpfull, this is not my expertise domain and I’m really struggling with this.

Thank you for your very appreciated help!