Color mismatch between two Arducam 64MP cameras on Raspberry Pi with locked exposure and manual AWB

I’m running two Arducam 64MP Hawkeye Autofocus modules on a Raspberry Pi setup using a master/slave architecture. The camera node script controls everything — it receives trigger_at timestamps and handles plate_start/plate_end capture windows. Images are delivered via MQTT. I’m using rpicam-still (libcamera-still) for capture.

Here are the capture parameters I’ve locked down on both cameras:

  • width=9152, height=6944
  • mode=“9152:6944:10”
  • quality=90
  • denoise=off
  • autofocus_mode=manual with fixed lens-position per camera (camera2 is at 6.90)
  • shutter=3000
  • gain=1.0
  • awb=custom
  • awbgains=1:1

Both cameras fire at the same moment with the same exposure settings. I expected them to produce nearly identical images, but I’m seeing noticeable color differences between the two JPEGs — hue shifts, different color temperatures, channel balance looks off between them.

A small brightness difference I could live with, but the color inconsistency is way more than I expected given that everything is locked. Has anyone else seen this with multiple 64MP Hawkeye modules? Is this normal unit-to-unit variation or am I missing something in my pipeline?

Thanks for the detailed write-up. A few things to check before we dig deeper:

  1. Have you tried switching from awb=custom with awbgains=1:1 to a preset like awb=daylight? That would help rule out whether the fixed 1:1 gains are just exposing underlying sensor differences rather than compensating for them.

  2. Are you aware of any tuning files on your system? On Raspberry Pi with libcamera, check if these exist:

    • /usr/share/libcamera/ipa/rpi/pisp/arducam_64mp.json
    • /usr/share/libcamera/ipa/rpi/pisp/ov64a40.json

    If they’re present, libcamera will load them automatically, and sometimes tuning files can interact with locked parameters in unexpected ways.

  3. Could you share a sample of the color difference you’re seeing? A side-by-side crop of the same scene from both cameras would help.

Good suggestions. Here’s what I’ve tried since:

First, I switched to awb=daylight on both cameras (kept everything else locked). The color inconsistency was still there — maybe slightly different than before, but definitely not corrected. The two cameras still render the scene with different color casts.

Then I tested different fixed gains: awbgains=1.92/1.79 on both modules. Same story. The hue shift between the two cameras persists regardless of which fixed gains I use.

I also checked for tuning files — both of these are present on my system:

  • /usr/share/libcamera/ipa/rpi/pisp/arducam_64mp.json
  • /usr/share/libcamera/ipa/rpi/pisp/ov64a40.json

So the IPA is loading those. But since both cameras are identical models, they’d be loading the same tuning data anyway, right?

I’ve been going back and forth on this and have a few broader questions:

  1. Is this level of color difference considered normal due to sensor variation, lens differences, IR-cut filter variation, or factory calibration tolerances? Like, should I just expect this with these modules?

  2. Do you recommend per-camera calibration — CCM (Color Correction Matrix), lens shading correction, black level calibration — for multi-camera color consistency? If so, is there a documented workflow?

  3. Are there module-specific tuning files (per serial number or per batch) that could reduce color variation? Or is everyone using the same JSON?

  4. For color standardization across multiple cameras, is awb=daylight or awb=custom with measured per-camera gains the better path? Any official guidance on multi-camera color consistency with these modules?

These are excellent questions, and what you’re seeing actually has a known explanation.

Root cause: hardware-level variation between sensor modules.

With high-resolution Quad-Bayer sensors like the 64MP (IMX678-based), even modules from the same production batch can exhibit visible color differences when you force identical software parameters across them. Here’s why:

The IR-cut filter coating has a cutoff wavelength that varies slightly from unit to unit — we’re talking nanometer-scale tolerance differences in the coating process. Combine that with minor variations in the sensor’s own spectral response, and you get visible hue shifts and color temperature differences between two modules even when awbgains, shutter, and gain are locked to the same values.

When you let AWB run freely, the algorithm compensates for these differences automatically and the images converge. But when you lock gains to identical numbers (whether 1:1, 1.92/1.79, or any other fixed pair), you’re essentially forcing the same correction onto hardware that needs slightly different corrections. The hardware tolerances become visible.

The awb=daylight preset suffers from the same limitation — it applies a fixed color temperature assumption that doesn’t account for per-module IR-cut and sensor variance. The tuning files loaded from /usr/share/libcamera/ipa/rpi/pisp/ are also generic — they’re designed for a “typical” module, not calibrated to your specific unit.

So to directly answer your questions:

  1. Yes, this is normal. With locked parameters on Quad-Bayer sensors, unit-to-unit color variation at this level is expected.

  2. Per-camera calibration would help — CCM, LSC, and black level calibration per module could reduce the gap — but there isn’t a ready-made user-facing calibration workflow for this on the libcamera/RPi stack. It’s non-trivial.

  3. No per-serial or per-batch tuning files are publicly distributed. All modules ship with the same generic tuning data.

  4. Neither awb=daylight nor awb=custom with a single measured gain pair will fully solve this across modules, because the required correction differs per unit. You’d need per-camera measured gains.

That makes a lot of sense, thanks for the detailed explanation. So the IR-cut filter coating variance and sensor spectral response differences are the real culprits here — software can only do so much when the hardware itself isn’t matched.

So where does that leave me practically? I can’t really run per-camera calibration easily based on what you’re saying, and I definitely don’t want to run AWB unlocked because that would introduce frame-to-frame inconsistency within each camera. Is there any way to get modules that are pre-matched from the factory, or some screening process? Otherwise I’m going to have to build a calibration rig myself, which I’d rather avoid.

There’s actually an option for this. Arducam offers a Customized Screening Service where the engineering team manually tests and hand-picks modules from inventory to create matched sets.

Here’s what they do: they evaluate modules under standard lighting conditions and select pairs (or sets of 4) that exhibit:

  • Matched AWB convergence points
  • Consistent color response

This “Golden Pair” selection significantly reduces the baseline color difference between modules, so your existing locked-parameter control logic has a much better starting point to work with.

For a master/slave multi-camera architecture using Quad-Bayer sensors, this is honestly the recommended approach. Software-level parameter locking alone can’t fully compensate for per-module hardware tolerances — hardware-level matching (Golden Pair selection) is the way to go for real color consistency across modules.

I’ve sent you the details on how to proceed with the sales team via private message — please check your inbox. They’ll help arrange the “Custom Color-Matched 64MP Selection” for your setup.