A working infrared preview does not prove that the recording pipeline receives encodable frames. The preview may accept a single-channel array while the video writer expects a color image, leaving a small file with no decodable pictures. This troubleshooting procedure is for acquisition programs that display infrared frames correctly but save empty or unplayable video. You will check the actual arrays, establish an explicit frame-to-encoder contract, record a short controlled clip, and verify it after shutdown. Start with those checks before changing camera hardware or trying more filename extensions.
1. Match the symptom to the first test
A file's size is a clue, not a diagnosis. A small file may contain only container information, while a larger file can still lack usable video. Work through the likely causes in order, keeping the camera configuration unchanged during each test.
| Symptom | Likely cause | First test | Corrective action |
|---|---|---|---|
| Preview works, recording contains no pictures | Single-channel input sent to a color-configured writer | Log array shape and writer color setting | Match channels explicitly |
| Recording fails only for one stream | Different dimensions or data type | Compare that stream's actual array properties | Give the stream its own writer configuration |
| Writer never opens | Unsupported encoder, invalid path, or unavailable backend | Check isOpened() immediately |
Correct initialization before acquiring more frames |
| File becomes playable only after the program exits | Writer not finalized before playback | Release it before opening the file | Put release in guaranteed cleanup |
| File decodes in the recording application but not a player | Player or codec compatibility | Decode the finalized file independently | Choose a supported delivery format |
| Video plays but scientific values are wrong | Display conversion or lossy encoding | Compare decoded pixels with source data | Keep a separate measurement archive |
For broader checks covering paths, writer setup, and cleanup, use the empty camera recording file troubleshooting procedure. The steps below concentrate on the contract between infrared arrays and the encoder.
2. Inspect the array that reaches the writer
Log properties immediately before writing, not only after acquisition. Resizing, color conversion, stacking, or queue handling can change the image between those locations.
For a NumPy image array, inspect:
print(frame.shape, frame.dtype, frame.strides)
print("contiguous:", frame.flags.c_contiguous)
A typical monochrome image has shape (480, 640) and type uint8. A typical color image has shape (480, 640, 3). The array order is height followed by width; the writer's image-size argument uses width followed by height.
Do not assume that an infrared stream is always an unsigned 8-bit image. Acquisition interfaces can expose unsigned 16-bit samples, packed formats, or other representations. Inspect the delivered array and its documented pixel format before selecting a conversion.
Record the properties of several frames, including one after any stream restart. A writer configured for one geometry should not receive another geometry halfway through the recording.
Also check buffer ownership. Some acquisition APIs reuse image memory after a frame object is released or returned to a pool. If frames cross a queue or outlive that object, make an owned copy while the acquisition buffer remains valid. A contiguous array is not necessarily an owned copy.
If an array is already being misinterpreted before encoding, the pixel format and matrix type mismatch guide explains why changing the writer alone will not fix it.
3. Choose one explicit writer contract
For an ordinary unsigned 8-bit monochrome source, test one of these paths. Do not mix their input arrays within a file.
| Recording path | Array passed to the writer | Writer configuration | Main limitation |
|---|---|---|---|
| Native grayscale | Two-dimensional uint8
|
isColor=False |
Backend and encoder must support the path |
| Grayscale represented as color | Three-channel uint8 after grayscale-to-BGR conversion |
isColor=True |
More intermediate data; not a measurement-preserving guarantee |
| Measurement archive | Original samples plus metadata | A documented lossless storage path | Requires suitable analysis software rather than just a media player |
The color-conversion path is a useful diagnostic because it makes channel handling explicit. It does not make an unsupported encoder available, and it does not make lossy encoding lossless.
A raw-data calculation helps establish the scale of the stream:
For a hypothetical monochrome stream:
- Frame size:
640 × 480 × 1 × 1 = 307,200 bytes. - At 30 frames per second:
307,200 × 30 = 9,216,000 bytes per second. - Over 5 seconds:
9,216,000 × 5 = 46,080,000 bytes, or about 46.1 MB before compression.
Representing that same image with three unsigned 8-bit channels makes the intermediate frame data three times larger. The encoded file size is not predictable from this arithmetic because compression depends on the encoder, settings, and image content.
4. Record a short clip with explicit checks
Separate camera acquisition from the recording test. Supply an iterable of valid, two-dimensional infrared arrays to the function below. The acquisition adapter must provide its own timeout and keep each source buffer valid while it is being processed.
This example deliberately rejects other data types rather than silently converting measurement data. It uses a color representation of grayscale for the diagnostic recording.
import cv2
import numpy as np
def record_ir_test(frames, path="ir_check.avi", fps=30.0,
max_frames=150):
if fps <= 0 or max_frames <= 0:
raise ValueError("fps and max_frames must be positive")
writer = None
expected_shape = None
submitted = 0
try:
for frame in frames:
if frame is None:
continue
gray = np.asarray(frame)
if gray.dtype != np.uint8 or gray.ndim != 2:
raise ValueError("Expected a 2D uint8 infrared image")
if writer is None:
height, width = gray.shape
expected_shape = gray.shape
writer = cv2.VideoWriter(
path,
cv2.VideoWriter_fourcc(*"MJPG"),
fps,
(width, height),
isColor=True,
)
if not writer.isOpened():
raise RuntimeError("Video writer did not open")
if gray.shape != expected_shape:
raise ValueError("Frame dimensions changed")
gray = np.ascontiguousarray(gray)
color = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)
writer.write(color)
submitted += 1
if submitted >= max_frames:
break
finally:
if writer is not None:
writer.release()
if submitted == 0:
raise RuntimeError("No frames were submitted")
return submitted
Here, MJPG with an AVI container is a diagnostic candidate, not a universal compatibility promise. Support depends on the installed video backend and encoder. If initialization fails, investigate that failure before changing the camera stream.
The submitted counter records calls to the writer. It does not prove that every frame was encoded successfully: write() does not provide a per-frame success acknowledgement through this interface.
For a native grayscale test, open a separate writer with isColor=False and submit the two-dimensional array directly. Do not retain the color conversion while changing only the flag. Verify the resulting file independently because backend support varies.
5. Finalize the file, then decode it
Always release the writer before attempting playback. Depending on the container and backend, shutdown writes buffered output or metadata needed to read the recording correctly.
Place cleanup in finally, including when acquisition raises an exception. Stop acquisition and return camera buffers through their documented lifecycle as well. Closing a preview window is not a substitute for releasing the writer.
After record_ir_test returns, reopen the file:
capture = cv2.VideoCapture("ir_check.avi")
decoded = 0
last_shape = None
try:
if not capture.isOpened():
raise RuntimeError("Recorded file could not be opened")
while True:
ok, image = capture.read()
if not ok:
break
decoded += 1
last_shape = image.shape
finally:
capture.release()
print("decoded frames:", decoded)
print("decoded image shape:", last_shape)
A successful first-frame read is useful, but decoding the entire short clip catches truncation later in the file. Compare decoded frames with submitted frames and inspect the beginning, middle, and end visually.
At a configured playback rate of 30 frames per second, 150 encoded frames represent 150 / 30 = 5 seconds of nominal playback. That is not necessarily 5 seconds of elapsed acquisition time. A conventional writer frame-rate setting does not preserve irregular capture timing automatically.
For synchronized analysis, retain source timestamps and frame identifiers separately. If the short clip succeeds but sustained recording fails, move to the high-speed recording and dropped-frame checks.
6. Isolate initialization and playback failures
When isOpened() is false, check these items before recording another long run:
- Use a writable local directory with sufficient free space. Confirm the resolved output path rather than assuming the current directory.
- Pair the filename container with an encoder supported by the installed backend. A four-character codec identifier is a request, not proof that an encoder exists.
- Initialize with the actual width and height of the submitted image. Account for cropping or resizing before writer creation.
- Record which backend and library build are active, where that information is available.
- Test a short generated image sequence through the same writer configuration. This removes camera acquisition from the experiment.
Changing .avi to .mp4 does not transcode a file or install an encoder. Likewise, changing several settings together makes it difficult to identify which correction mattered.
If the file decodes through your application but another player rejects it, test a second decoder before calling the file corrupt. A recording can be valid yet use an encoding that a particular player does not support. The video capture backend fallback guide covers that separate read-side problem.
7. Keep measurement data separate from viewing video
A playable infrared recording is not necessarily a usable measurement archive. Grayscale-to-color conversion followed by lossy video encoding can alter intensities, even when the image still looks correct.
For unsigned 16-bit source data, do not cast directly to uint8. That operation does not perform a controlled display scaling and can discard the information you intended to preserve.
Create a separate display image using a documented intensity range. Keep that range fixed across frames when visual brightness must remain comparable. Independently scaling every frame can make a changing signal appear constant or make a stable scene appear to change.
For quantitative thermal work, retain calibration information and relevant acquisition metadata alongside the original samples. The high-bit-depth grayscale export guide addresses preserving those samples, while raw thermal counts and calibration essentials explains why recorded counts alone are not automatically temperatures.
For depth streams, keep the depth scale, invalid-value convention, timestamps, and calibration needed for reconstruction. Use the lossless depth recording guide when a viewing copy is insufficient.
8. Treat projected dots as a separate imaging issue
Visible dots or structured texture in an infrared image can originate from an active illumination system. They do not, by themselves, explain an empty encoded file. Resolve the recording contract before changing illumination.
If the device provides a documented projector or emitter control, evaluate its effect through the supported interface. Changing active illumination can reduce stereo matching quality or leave a reflected-infrared image too dark. A dark room may provide little useful reflected light once the active source is disabled.
Do not apply undocumented power values, modify laser hardware, or alter protective functions. Follow the device's optical safety instructions and verify the depth output whenever illumination settings change.
Thermal imaging is a different modality from recording reflected infrared light. A thermal camera should not be selected merely to solve a software channel mismatch.
9. Check before changing hardware or ordering parts
Identify the measurement first: reflected intensity, thermal information, or depth. The infrared cameras category and 3D sensors category cover different sensing approaches; the camera finder helps narrow hardware after that distinction is settled.
For thermal hardware evaluation, inspect the LS-IRC-0044 and LS-IRC-0016. These catalog thermal options are not substitutes for diagnosing a reflected-infrared recording pipeline. Confirm the required acquisition interface, pixel format, and recording software support before selection.
- Lens mount
- C-mount
- Interface
- GigE
- Resolution
- Up to 3 MP
- Spectrum
- LWIR
- Interface
- USB3
Before accepting the recording workflow, check:
- Array contract: dimensions, data type, and channel count match the writer configuration.
- Initialization: the writer opens before frames are submitted.
- Ownership: queued images remain valid after acquisition buffers are returned.
- Shutdown: the writer is released even when acquisition stops unexpectedly.
- Readback: the finalized short clip decodes completely, with the expected frame count.
- Timing: capture timestamps are retained when nominal playback timing is insufficient.
- Measurement integrity: original samples are archived separately when video conversion changes their meaning.
Keep the passing short clip as a regression test before increasing recording duration or adding streams. For hardware selection, bring the measured array properties, required sensing band, recording duration, and synchronization needs to application support.
Questions
Why does infrared preview work while the saved video is empty?
A preview and a video writer can accept different image representations. A single-channel infrared array may display correctly but fail when submitted to a writer configured for color. Check the actual array, writer initialization, image dimensions, and finalization before assuming the camera or recording container is faulty.
Should infrared frames be recorded as grayscale or color?
Use native grayscale only when the selected backend and encoder support it, with the writer configured accordingly. Converting unsigned 8-bit grayscale to three-channel BGR is a useful compatibility test. Neither path guarantees preservation of measurement values when the video encoder uses lossy compression.
Can an ordinary video file preserve thermal or depth measurements?
Not by default. A normal viewing-video workflow may reduce bit depth, change intensities, or discard acquisition metadata. Preserve original samples through a documented lossless recording method, and retain calibration and timestamps where needed. Generate a separate display video for convenient playback.
How do you confirm that a recording was finalized correctly?
Release the writer before opening the output for playback, including after exceptions or an early stop. Then reopen the finalized file and decode the entire short test clip. Comparing decoded frames with submitted frames provides stronger evidence than checking file size or reading only the first image.
Will disabling an infrared projector fix an unplayable recording?
Changing active illumination is not a normal fix for an encoder mismatch. It changes the captured scene and may also reduce depth quality. First correct the recording pipeline, then evaluate illumination through documented device controls while following optical safety instructions and checking any dependent depth output.

