Cameras & sensorsTroubleshooting

Morph kernel corrupted image artifacts: transfer fault or kernel bug

Rectangles after a morph kernel usually mean a corrupted image from the transfer path, not bad math. The artifacts, one test, and the fixes.

LumaSpec VisionLettura 10 min Condividi su LinkedIn

In questa pagina Sezioni 9
  1. 01The symptom and the one-minute decision
  2. 02The four likely causes, ranked
  3. 03Worked example: a 3 × 3 Laplacian turns stale blocks into outlined rectangles
  4. 04Why one frame size shows it and another does not
  5. 05Fixes on the acquisition side, in order
  6. 06When the acquisition path has to be replaced
  7. 07When it really is the kernel
  8. 08Checklist before you change anything
  9. 09Questions

You run a 3 × 3 morph — a Laplacian sharpen — and the output fills with small rectangles that are not in the scene. Two suspects stand out: the morph kernel itself, or the frame transfer between the sensor and memory. The difference decides where you work next, because a kernel fault is in your code and a transfer fault is in firmware you cannot patch from a script. What follows is the one test that splits the two in about a minute, the arithmetic that explains why a sharpen kernel draws these rectangles at all, the cause traced in one embedded camera firmware, and the fixes in the order worth trying.

The symptom and the one-minute decision

Repeating rectangular blocks, bands spanning whole rows, tiles of an earlier frame: corruption like this shows up after processing, tracks a frame-size or clock setting, and repeats the same way frame after frame. Treat it as a transfer fault until a saved frame proves otherwise. That is the decision, and it costs one file write.

Split the pipeline at the point of capture. Save the frame exactly as it left the camera, before any filter runs:

# kernel_size: int = 1; kernel: a list of nine ints, as in the reported script
kernel_size = 1
kernel = [-1, -1, -1,
          -1, +8, -1,
          -1, -1, -1]

img = sensor.snapshot()
# write this frame to a file here, with your library's image-save call
img.morph(kernel_size, kernel)

Rectangles already in the raw frame: the acquisition path corrupted the image, and the kernel only made it visible, because a sharpen kernel turns value steps into lines. Clean raw frame with rectangles only after the morph: the fault is in the kernel call, its scale and offset, or the buffer handling around it. In the reported case the raw frame was already corrupt: the driver's copy engine was losing a race on a shared line buffer, and a later firmware revision fixed it.

The four likely causes, ranked

Ordered by how often each explains row-aligned rectangles after a morph. Test in this order and you will usually stop at the first row.

Likelihood Cause What you see One test Fix
High Copy engine loses the line race in the camera driver Blocks of whole rows, same pattern every frame, tied to one frame size Raw frame already corrupted Firmware update; disable full copy offload; slower pixel clock
Medium Frame buffer reused before your code is done with it Corruption changes frame to frame and moves with timing Copy the frame out before the next trigger Own the buffer copy before returning it to the camera
Medium Kernel scale, offset or border handling Pattern repeats every 3 px, or damage hugs the image border Run the same morph on a stored clean image Clamp results to 0–255; clamp window indices at the borders
Low Link-level data loss on cable or port Random stripes that change when cables move Reseat, then swap cable and port Replace the cable; shorten the run

Row two has its own walkthrough in heap corruption in vision software: fix image buffer lifetime. Row three is covered in the kernel section below. The rest of this guide is row one, because the frame-size clue points straight at it.

Worked example: a 3 × 3 Laplacian turns stale blocks into outlined rectangles

Inputs, each with its source:

  • Kernel: 3 × 3, kernel size 1, taps of −1 on eight positions and +8 at the centre — the script from the reported case.
  • Pixel format: 8-bit grayscale, one byte per pixel, range 0–255 DN (digital numbers).
  • Test values, chosen for clean arithmetic: a fresh background of 100 DN and one stale block of 180 DN — the values an overwritten line carries wherever scene brightness differs from the previous frame.
out = 8 * centre - (sum of the 8 neighbouring pixels) clamp below 0 to 0 DN, above 255 to 255 DN

In words: each output pixel is eight times the centre minus the sum of its eight neighbours, clamped to the 8-bit range. Now the four pixels that matter:

  • Fresh interior: 8 × 100 − (8 × 100) = 800 − 800 = 0 DN. The kernel is blind on uniform ground.
  • Stale interior: 8 × 180 − (8 × 180) = 1440 − 1440 = 0 DN. The middle of a stale block renders exactly like background, which is why you see outlines, not filled blocks.
  • Stale edge: 8 × 180 − (4 × 180 + 4 × 100) = 1440 − (720 + 400) = 1440 − 1120 = 320 DN, clamped to 255 DN.
  • Fresh pixel touching the block: 8 × 100 − (4 × 180 + 4 × 100) = 800 − 1120 = −320 DN, clamped to 0 DN.

Result: every stale block becomes a bright one-pixel outline at 255 DN with a dark rim at 0 DN, interior unchanged. Count the rectangles and you have counted the overwritten regions.

Decision, with its threshold: outlines whose edges sit on row boundaries and hold the same position frame after frame are stale data from the transfer path — stop tuning the kernel. A pattern that repeats every 3 pixels, the window width, or one confined to the outer pixel rows, is a kernel-code fault.

Why one frame size shows it and another does not

The resolution clue is the strongest evidence for the transfer path. Kernel arithmetic does not change with frame size — nine taps are nine taps at any resolution. The driver's copy plan does. In the affected firmware, the full-offload path moved every sensor line through one shared line buffer: the camera-interface DMA writes a line, a second DMA channel copies it into the frame in memory, and the next line immediately reuses the same buffer. With full offload off, the driver kept two line buffers and took one interrupt per line — slower, but a line could never be overwritten mid-copy.

Frame-size and clock settings move the timing on both sides of that race. When the copy channel cannot drain its queue before the next line lands, the frame receives a mix of fresh and stale lines, and the blocks appear. A capture-mode change that shifts line timing can therefore turn a clean stream into a tiled one with no change on your side.

Fixes on the acquisition side, in order

  1. Update the camera firmware first. The reported fault sat in the full-offload copy path and was fixed in a later revision; on a board whose driver you did not write, the update is the whole fix.
  2. If you build the firmware yourself, turn the full copy-offload build option off and rebuild. The partial path keeps the second line buffer and pays one interrupt per line — a fair price until the image is clean.
  3. Lengthen the line window: drop the sensor pixel clock, or raise horizontal blanking if the driver exposes it. Either gives the copy engine more time per line.
  4. If the driver is yours, use two line buffers or a ring of them and accept the per-line interrupt. Correct frames first, offload second.

When the acquisition path has to be replaced

When the firmware can be neither updated nor rebuilt, replace the transfer path, not the kernel. Standard machine-vision interfaces carry delivery checks of their own: a GigE Vision camera resends missing packets before the frame completes, and USB3 checks every packet with retry at the link layer, so a frame your software holds arrived whole. The sensor-to-memory line buffer then lives behind a standards-compliant cable instead of inside your driver.

For embedded builds, these board-level cameras span the mounts and transports worth shortlisting:

Specification LS-BCM-0020 MV-CB016-10GM-M-W LS-BCM-0003
Shutter Rolling shutter – Global
Sensor model IMX178 Sony IMX296 Sony IMX252
Resolution 3072 x 2048 1440 × 1080 2048 × 1536 (3MP)
Max. frame rate 18 fps 65.2 fps @ 1440 × 1080 Mono 8 120 fps
Pixel size 2.4 μm 3.45 μm × 3.45 μm 3.45 μm
Sensor size 1/1.8" 1/2.9" 1/1.8"
Pixel bit depth 12 – 10-bit
Sensor make Sony – Sony
Sensor type CMOS CMOS, global shutter CMOS
Dynamic range 60.0 dB 74 dB >70.0 dB
Exposure time 25 μs ~ 1 s UltraShort exposure mode: 1 μs to 14 μs; Standard exposure mode: 15 μs to 10 sec 16 μs ~ 1 s
Data interface GigE Gigabit Ethernet (1000 Mbit/s), compatible with Fast Ethernet (100 Mbit/s) USB3.0
Price $290.00 $559.00 $690.00

Values as printed in each product's data sheet; open a part for its drawing and full specifications.

The LS-BCM-0020 suits a CS-mount optics path on GigE; the MV-CB016-10GM-M-W puts a global shutter behind an M12 lens; the LS-BCM-0003 takes the USB3 route at 120 fps. When the processing already runs on a PC, an area scan camera is the simpler swap: the LS-CMR-0053 is a 12 MP GigE area scan camera with an IMX226 sensor, a 1/1.7" rolling shutter, 9 fps and a C-mount.

Area scan cameras12 MP GigE area scan camera, IMX226, 1/1.7" rolling shutter, 9 fps (LS-CMR-0053)LS-CMR-0053
Sensor format
1/1.7"
Lens mount
C-mount
Interface
GigE
Resolution
12 MP
$560.00View partBuild a system with it

A smart camera collapses capture and processing into one maker-maintained stack, so a fault of this class becomes a firmware ticket instead of your driver work. The LS-SMC-0063 pairs a 1.6 MP sensor with a 16 mm lens and runs 60 fps.

Smart cameras & vision sensors1.6 MP smart camera, 60 fps, 16 mm lens, white light (polarized, transparent light) (LS-SMC-0063)LS-SMC-0063
Interface
GigE
Resolution
Up to 3 MP
$1,110.00View part

Filter by interface in the camera finder, or start a matched set at the camera step of the system configurator. The broader families are area scan cameras and smart cameras. If your board revision is involved, the fastest route to a straight answer is the contact page.

When it really is the kernel

Acquit the transfer path and the work moves into your code, where two faults cover most cases. First, border handling: a 3 × 3 window reads one pixel outside the image at every edge, and a wrapper that does not clamp its indices paints garbage into the outer rows. Second, scale and offset: a convolution kernel maps many inputs outside 0–255, and what happens next is down to the library. An 8-bit wrap instead of a clamp turns the −320 DN result above into −320 + 2 × 256 = 192 DN — a bright line exactly where the clamped version shows black. If your rectangles invert when you change the kernel offset, this is your bug.

The confirmation test is free: run the same morph on a stored, known-good image. A kernel fault follows the kernel; the Laplacian and smoothing convolution kernel crash guide walks those failure modes, and debug packed Bayer acquisition before image processing applies the same split on the capture side. One more angle: if you resized buffers around a changed frame format, check the multi-channel image buffer size before processing — row-stride mismatches produce bands too.

Checklist before you change anything

  • Raw frame saved before the morph, and the rectangles are already in it → transfer fault; start at the firmware fixes.
  • Outlines row-aligned, positions stable frame after frame → stale lines; the count of rectangles is the count of overwritten regions.
  • Only one frame size or clock setting shows it → transfer timing, not kernel math.
  • Same morph on a stored clean image is clean → kernel acquitted twice over.
  • Frame copied out before its buffer returns to the camera → rules out buffer reuse; see push the camera buffer back only after copying your frame data.
  • Firmware revision current → rules out the known, already-fixed offload fault.
  • Pixel clock halved and the artifacts thin out → the copy engine was losing the line race; hold the slower clock until the firmware fix lands.

Work the list top to bottom and the third item settles most cases: rectangles in the raw frame are never a kernel problem, and no amount of morph tuning will move them.

Domande

Why do the rectangles show bright outlines but blank interiors?

The 3 × 3 Laplacian returns zero wherever a pixel and all eight neighbours match, so the middle of a stale block renders exactly like background and only the boundary shows. Small steps behave the same: a stale block just 4 DN above a 100 DN background gives an edge value of 8 × 104 − (4 × 104 + 4 × 100) = 16 DN, a visible outline from a step your eye never saw in the raw frame.

Will a faster processor or more memory stop the corruption?

No. The race is between two DMA engines sharing one line buffer; CPU speed and RAM size change nothing on that path. What matters is time per line and buffer count: line time scales as active pixels divided by pixel clock, so halving the clock doubles the copy engine's window, and a second line buffer removes the overwrite entirely for a small per-line cost.

Can the rectangles be filtered out in software after capture?

Not usefully. The stale blocks contain pixels from an earlier frame — the true values for those positions never reached memory, so any repair invents data. Inspection decisions taken over an inpainted block are guesses. Fix the transfer instead: firmware update, full offload disabled, a slower pixel clock, or a camera whose transport checks delivery.

Why does the fault appear at one frame size and vanish at another?

Kernel arithmetic is resolution-independent: nine taps behave the same at any size. The driver's copy plan is not — line timing, blanking and sometimes the buffer strategy itself change with the capture mode. Sweep frame sizes and the pixel clock: artifacts that move with those settings are timing races in the transfer path, strong enough confirmation to start the firmware fixes.

Which camera interfaces detect this class of data loss?

GigE Vision cameras resend missing packets before a frame completes, and USB3 checks every packet with retry at the link layer, so software receives either a whole frame or a reported error. That turns silent corruption into a visible one. On a custom board with a bare camera interface and DMA, nothing checks the copy — which is exactly how stale-line tiles get through unnoticed.