System designTroubleshooting

applyColorMap src.dims == 2 error: feed it a valid 8-bit Mat

The applyColorMap src.dims == 2 error means your Mat is empty. Find the load or capture step that failed, then validate the Mat before the call.

LumaSpec Vision9 min read Share on LinkedIn

On this page 8 sections
  1. 01What the assertion actually checks
  2. 02Three causes, in order of probability
  3. 03Worked example: the Android collage crash on OpenCV 4.6.0
  4. 04The guard and the fix
  5. 05What applyColorMap accepts
  6. 06Where the empty Mat comes from in a vision pipeline
  7. 07Prevention checklist
  8. 08Questions

A color-mapped view is the fastest way to see what a depth frame contains, and this crash kills it on the first call: the app dies with a CvException from applyColorMap, reading Not supported (expected: 'src.dims == 2'), where 'src.dims' is 0. Above it sits a wall of OpenCV loader lines that looks like a broken installation.

It is not one: the library loaded fine, and the Mat you passed is empty. This guide decodes what the assertion checks, ranks the three causes behind it, traces the OpenCV 4.6.0 Android crash line by line, and leaves you with a guard and a checklist that keep a bad Mat from reaching the call again.

What the assertion actually checks

applyColorMap turns an 8-bit single-channel image into a color one: COLORMAP_JET for depth frames, plus the other built-in maps in Imgproc. The check that stops your call is about shape. It requires a 2-D Mat, an image with rows and columns. In OpenCV, dims is that dimensionality, and every ordinary image is 2 no matter how many channels it has: a three-channel BGR photo is 2-D, and so is a single-channel gray frame.

The second half of the message is the diagnosis. The log prints the value it found, and here that value is 0. A Mat reporting dims 0 carries no array of pixels at all: no image was loaded into it. You did not pass the wrong kind of image; you passed no image. That is why the fix is almost never inside applyColorMap. The call is doing its job by refusing an empty input, and the real bug sits one or two lines earlier, where the Mat should have been filled.

OpenCV reports the failure as error (-2:Unspecified error) in colormap.cpp, and the loader lines above it say the opposite of a broken install. The log shows opencv_java4 being found and loaded, then "First attempt to load libs is OK". The "Cannot load info library for OpenCV" line is the loader's optional info pass and is safe to ignore; the fatal line is the CvException at the end.

Three causes, in order of probability

  1. The load or capture step returned nothing. imread on a missing file, an unsupported format or an unreadable path does not throw: as documented, it returns an empty Mat with 0 × 0 dimensions, which you can test immediately with empty() or the data pointer. On Android the same end comes from an asset name that does not match, a file that was never written, or storage the app is not allowed to read. Once a PNG does load, its channel layout has traps of its own: see read PNG image data and handle its channels correctly. Test: call empty() on the Mat the moment you receive it.

  2. The wrong Mat went into the call. The argument order is source, destination, colormap. A fresh Mat() created for the output starts unallocated, so swapping the arguments, or passing the output by mistake, hands applyColorMap an object with no data and reproduces this exact assertion. Test: log rows(), cols() and type() of the source on the line immediately before the call.

  3. The input is not a 2-D array. The assertion fires for any dimensionality other than 2, and the message always prints the number it found. A 3 means a genuinely three-dimensional Mat, such as stacked planes or a raw byte buffer wrapped with the wrong shape, and it needs reshaping into a single 2-D plane before the call. Test: read the number printed in the message; it is the actual dims value, so the message itself sorts this cause from the first one.

The message prints What it means Two-second test
src.dims is 0 The Mat is empty; nothing was loaded empty() returns true
src.dims is 3 A real n-D array, not an image Log dims() and rows()
A type or depth assertion instead A valid 2-D Mat of the wrong depth type() reports it; convert to CV_8UC1

Worked example: the Android collage crash on OpenCV 4.6.0

The inputs all come from the crash log: OpenCV 4.6.0; the assertion raised in colormap.cpp at line 738; the found value src.dims of 0 against the required 2; and the call path running from the app's image-inflating utility (line 69 of the stack) through the Java wrapper Imgproc.applyColorMap at Imgproc.java:8251 into the native applyColorMap_0 call.

The rule in words: a drawable image is a 2-D array of pixels, so the colormap code requires dims to be exactly 2 and prints the value it found instead. A found value of 0 means the object carries no pixel array, because the step that should have filled it returned nothing.

The arithmetic uses an illustrative frame size typical of a phone screen. A valid 1080 × 1920 single-channel frame holds 1080 × 1920 = 2,073,600 pixels; as CV_8UC1 that is 2,073,600 pixels × 1 byte = 2,073,600 bytes, about 1.98 MiB, with dims() reporting 2. The Mat in this crash carries none of that: total() is 0 against the 2,073,600 a real frame of that size would hold, empty() is true, and the assertion stops the call before a single pixel is read.

The result: applyColorMap is innocent. The utility meant to fill the Mat produced no image, and every downstream call inherits the emptiness.

The decision: treat empty() true or total() 0 as a stop sign. Do not call the colormap; go back to the loader, fix why it returned nothing, and re-run. Call applyColorMap only when dims() is 2, total() is greater than 0, and type() is CV_8UC1 or CV_8UC3.

The guard and the fix

Put the check in front of the call and the crash becomes one log line. The Kotlin version below uses the documented Android OpenCV classes: Mat, CvType, Imgcodecs and Imgproc. src, dst and path are this guide's illustrative variables; everything else is API.

val src: Mat = Imgcodecs.imread(path, Imgcodecs.IMREAD_GRAYSCALE)
val dst = Mat()
if (src.empty() || src.dims() != 2 || src.type() != CvType.CV_8UC1) {
    Log.e("MatGuard", "bad Mat in: ${src.rows()} x ${src.cols()}, dims ${src.dims()}, type ${src.type()}")
    return
}
Imgproc.applyColorMap(src, dst, Imgproc.COLORMAP_JET)

The test that proves the guard: a synthetic 4 × 4 image made with Mat.zeros(4, 4, CvType.CV_8UC1) passes and colorizes into dst as a 3-channel CV_8UC3 result, while the empty Mat a failed imread returns stops at empty() and logs 0 x 0. In C++ the same three checks read src.empty(), src.dims == 2 and src.type() == CV_8UC1; dims is a member there and a method in Java, and the logic is identical.

For a depth pipeline the input is always single-channel, so the guard pins CV_8UC1; for colorizing photos, accept CV_8UC3 in the same condition. A 16-bit depth frame passes neither the type check nor the call: scale it into the 0-255 range and convert with convertTo(dst, CvType.CV_8U, alpha, beta) first. The min-max scaling arithmetic that keeps the values honest is in normalize a depth map for JET colormap display, keep the values.

What applyColorMap accepts

Three properties, all readable before the call:

  • 2-D: dims() must return 2, whatever the channel count.
  • 8-bit: the built-in maps take CV_8UC1 and CV_8UC3. A 16U depth frame or a 32F float buffer fails a depth assertion, a different message from this one.
  • Not empty: with no data loaded, this dims check is the one that fires, which is the error this page is about.

The one-line rule to keep next to the keyboard: applyColorMap(src, dst, map) wants a filled 2-D 8-bit Mat, and everything upstream exists to make sure it is filled.

Where the empty Mat comes from in a vision pipeline

In camera code the source is almost always the capture step. A read or grab call returns false, or a null frame, when the camera was never opened, the stream has ended, or the next frame has not arrived yet; wrapping nothing into a Mat reproduces this crash one call later. Check the capture call's return where it happens and never wrap bytes from a failed read. Image bytes vs pixel data: decode before processing covers the wrap step, and check multi-channel image buffer size before processing covers the size arithmetic for buffers that do arrive.

Depth sensors are the classic source of the frames being colorized here. ToF and structured-light cameras hand your code a depth image whose values become the JET view, and a rejected or out-of-range frame can arrive with nothing in it. Both sensor families below output the depth frames this guide colorizes, so the guard above belongs in front of their frames too.

3D sensorsHikrobot MV-DPS200P-02 Projected structured-light 3D cameraMV-DPS200P-02
Type
Stereo / structured light
$8,829.00 · quotedView and quote
3D sensorsHikrobot MV-DT01WMU ToF sensing cameraMV-DT01WMU
Type
Depth / ToF camera
Price on requestView and quote

If frames arrive but the image looks wrong rather than empty, smaller than expected or missing detail, decimation inside the camera pipeline is the usual suspect: depth image resolution smaller than expected: check decimation first walks that check. The same guard belongs in front of every colormap call on gray frames from area scan cameras, where an empty Mat means the same thing: look at the acquisition step, not the colormap. The 3D sensors category is where the depth output of this section lives in the shop.

Prevention checklist

  • Guard once, reuse everywhere: a helper that checks empty(), dims() and type(), logs all four numbers, and returns before any native call.
  • Check every imread, imdecode and capture return where it happens; an empty result should not travel past the line that produced it.
  • Log a Mat's empty, dims, rows × cols and type whenever it crosses a function boundary.
  • Keep the synthetic test: Mat.zeros(4, 4, CvType.CV_8UC1) colorizes, and an empty Mat is refused.
  • Keep the argument order fixed: source, destination, colormap. Never pass a fresh output Mat as the source.
  • Convert at the boundary: scale 16U depth frames to CV_8UC1 where they arrive, not at every use site.

That is the whole fix on the code side. If the empty Mats trace back to the hardware, a camera that returns nothing on grab or a sensor whose frames keep failing validation, match the replacement to the resolution and interface you already have in the camera finder, step through the camera configurator, or contact the applications team with the log. The systems topic page collects the other debugging guides of this kind.

Questions

Why does the log show 'Cannot load info library for OpenCV' before the crash?

That line comes from the loader's first pass, before it falls back to loading opencv_java4 directly. The same log then confirms 'Library opencv_java4 loaded' and 'First attempt to load libs is OK', so initialization succeeded. It is unrelated to the crash: the fatal exception is the CvException raised inside applyColorMap by the empty Mat, and it disappears once the input is fixed.

Does applyColorMap accept color images?

Yes. The built-in colormaps take 8-bit input of one or three channels, so an 8UC3 photo passes the same dims == 2 check as a gray frame and comes out colorized. What they refuse is depth: a 16-bit depth frame or a 32-bit float buffer fails a different assertion, so scale the values into 0-255 and convert to CV_8UC1 first.

How do I check a Mat before every OpenCV call?

Log four properties in one line: empty(), dims(), rows() × cols() and type(). A valid gray frame reads false, 2, a non-zero pixel count and CV_8UC1. If any of the four is wrong the native call cannot succeed, so return early and trace the load or capture step that produced the Mat instead of catching the exception downstream.

Can this assertion fire with a value other than 0?

Yes. The message always prints the value it found, and any dimensionality other than 2 fails. 0 means an empty Mat with no image data, which covers most reports. 3 means a genuinely three-dimensional array, such as stacked planes or a raw buffer wrapped with the wrong shape; reshape it to a single 2-D plane before the call.