System designTroubleshooting

OpenCV Android Mat(Mat, Rect) trim error: import the right Rect class

Mat(Mat, Rect) trim error in Kotlin: your Rect is the Android UI class. Import org.opencv.core.Rect and convert right and bottom to width and height.

LumaSpec Vision10 min read Share on LinkedIn

On this page 10 sections
  1. 01Why does Mat(Mat, Rect) fail with "None of the following functions can be called"?
  2. 02Which Rect does org.opencv.core.Mat(Mat, Rect) expect?
  3. 03How do you fix the import so the trim compiles?
  4. 04Why does the crop land in the wrong place after the fix?
  5. 05How do you keep the crop inside the camera frame?
  6. 06Why does the same trim compile in one Activity and fail in another?
  7. 07How do you pass the trim rectangle from the UI to the vision code?
  8. 08Does Mat(Mat, Rect) copy pixels or share them?
  9. 09When does this pipeline move onto machine-vision hardware?
  10. 10Questions

You have a live OpenCV Android preview, a rectangle drawn on screen, and a one-line crop that refuses to compile: Mat(mMatChange, trimArea) reports "None of the following functions can be called with the arguments supplied", even though the same code trimmed fine in another Activity. The message even lists a matching overload, which makes it look like a compiler bug. It is not. This article is for anyone building an OpenCV inspection prototype on Android in Kotlin. You will name the failing argument in one look, fix the import so the trim compiles, convert the rectangle's numbers so the crop lands where you drew it, keep the edges inside the frame, and know what the trim does to memory before you release anything.

Why does Mat(Mat, Rect) fail with "None of the following functions can be called"?

The compiler is telling you the truth: your Mat argument is fine, but your Rect argument is an android.graphics.Rect, and none of the org.opencv.core.Mat overloads accepts that class, so the call fails before anything runs.

Read the message as an argument test. Kotlin checked every constructor of org.opencv.core.Mat against the pair you passed and matched none of them. The list even contains the overload you intended, which is the giveaway:

(Mat, Rect)            defined in org.opencv.core.Mat
(Mat, Range)           defined in org.opencv.core.Mat
(Mat, Array of Range)  defined in org.opencv.core.Mat
(IntArray, Int)        defined in org.opencv.core.Mat
(Size, Int)            defined in org.opencv.core.Mat

A listed overload that still does not match means the argument's class is wrong, not its value. In the failing file, trimArea is declared as private var trimArea: Rect? = null, and the bare name Rect resolves to android.graphics.Rect. That happens because TrimView's callback — setOnActionUpListener, whose parameter is the Android Rect — forces that import into MainActivity. The two Rect classes are unrelated: no conversion exists between them, so Mat(Mat!, Rect!) rejects the Android rectangle outright.

Your own probe already cleared the other half: mMatChange went into detectOrange() and ran OpenCV code without complaint, so the Mat side is a real org.opencv.core.Mat. One argument in, one argument out — the rectangle is the failing one. If frames compile but come out colour-doubled instead, that is a different Mat problem, pixel format rather than class: Colour frame comes out doubled: match Mat type to pixel format.

Which Rect does org.opencv.core.Mat(Mat, Rect) expect?

It expects org.opencv.core.Rect, the x, y, width, height rectangle from the same package as Mat, Scalar and Range; the error's own list shows the matching constructor sitting unused.

org.opencv.core.Rect(x, y, width, height) takes four Int pixel values: where the crop starts and how far it extends. android.graphics.Rect(left, top, right, bottom) also takes four Int values: two corners of a box. Same shape, different meaning — the compiler separates them only by class, and the class is what failed.

The error also lists sibling overloads worth knowing: Mat(Mat, Range) trims rows or columns, and Mat(Mat, Array of Range) trims both at once. For a full-width band they beat a rectangle; the FAQ below shows both calls.

How do you fix the import so the trim compiles?

Import org.opencv.core.Rect in MainActivity.kt, or spell the package out at the call site as org.opencv.core.Rect(...); when the file genuinely needs both rectangle classes, alias one of them and the trim compiles.

If the file only trims Mats, swap the import:

import org.opencv.core.Rect   // was: import android.graphics.Rect

If the file also receives the drawn rectangle from TrimView, alias instead of swapping, because the callback's parameter is genuinely the Android class:

import android.graphics.Rect as UiRect
import org.opencv.core.Rect

Then declare the crop state with OpenCV semantics and a non-null initial value:

private var trimLeft = 0      // Int: pixels from the left edge
private var trimTop = 0       // Int: pixels from the top edge
private var trimWidth = 100   // Int: extent, not a right edge
private var trimHeight = 100  // Int: extent, not a bottom edge
private var trimArea: Rect = Rect(0, 0, 100, 100)  // org.opencv.core.Rect

The test that proves the fix is two lines inside onCameraFrame:

val probe = Mat(mMatChange, Rect(0, 0, 200, 200))
check(probe.width() == 200 && probe.height() == 200)

If that compiles and passes, the overload resolves; point trimArea at the drawn rectangle next. When the fix compiles but the pipeline still returns nothing, move one layer down with Fix a null image result in a vision pipeline.

Why does the crop land in the wrong place after the fix?

Because the two Rect classes disagree on what the third and fourth numbers mean: android.graphics.Rect takes left, top, right, bottom, while org.opencv.core.Rect takes x, y, width, height, so the same four values describe two different crops.

Suppose TrimView reports Rect(100, 50, 300, 200) in Android semantics: the box starts 100 px from the left and 50 px from the top, and its edges sit at 300 px and 200 px. Its size is therefore:

crop_width_px = right - left = 300 - 100 = 200 px crop_height_px = bottom - top = 200 - 50 = 150 px

The equivalent OpenCV rectangle is Rect(100, 50, 200, 150). Feeding the four raw values into org.opencv.core.Rect instead produces Rect(100, 50, 300, 200): the crop starts at the same pixel but spans x from 100 to 100 + 300 = 400 px and y from 50 to 50 + 200 = 250 px. Two different regions from one set of numbers, and no error anywhere:

Same call, two meanings android.graphics.Rect(100, 50, 300, 200) org.opencv.core.Rect(100, 50, 300, 200)
Third and fourth numbers right 300, bottom 200 px width 300, height 200 px
Crop size 200 × 150 px 300 × 200 px
Lower-right corner x = 300, y = 200 px x = 400, y = 250 px

The safe conversion uses the accessors that already do the subtraction:

val crop = Rect(uiRect.left, uiRect.top, uiRect.width(), uiRect.height())

android.graphics.Rect.width() returns right − left and height() returns bottom − top, both in px, so the helper stays one line.

How do you keep the crop inside the camera frame?

Check the edges before you construct the Mat: the crop fits when x ≥ 0, y ≥ 0, x + width ≤ frame width and y + height ≤ frame height, so on a 960 × 720 stream Rect(100, 50, 200, 150) fits and Rect(500, 600, 500, 200) does not.

setMaxFrameSize(960, 720) caps the delivered stream at 960 × 720 px, and rotation can swap rows and columns between devices, so clamp against the Mat you received rather than the numbers you configured:

  • Rect(100, 50, 200, 150): right edge = 100 + 200 = 300 px ≤ 960 px, bottom edge = 50 + 150 = 200 px ≤ 720 px — inside.
  • Rect(500, 600, 500, 200): right edge = 500 + 500 = 1000 px > 960 px — OpenCV throws a CvException at runtime, mid-frame in onCameraFrame.

Clamp once and the exception cannot happen:

val frameW = mMatChange.cols()   // 960 px in landscape
val frameH = mMatChange.rows()   // 720 px in landscape
val w = minOf(uiRect.width(), frameW)
val h = minOf(uiRect.height(), frameH)
val x = uiRect.left.coerceAtMost(frameW - w)
val y = uiRect.top.coerceAtMost(frameH - h)
trimArea = Rect(x, y, w, h)

coerceAtMost pins the right and bottom edges; touch-drawn rectangles are never negative, but values arriving through Intents or stored settings can be, so coerce x and y against 0 too when they come from anywhere but the trim view.

Why does the same trim compile in one Activity and fail in another?

Diff the two import blocks: the Activity that compiles pulls in org.opencv.core.Rect, while MainActivity pulls in android.graphics.Rect for TrimView's drawn rectangle, and Kotlin resolves the bare name Rect to whichever one the file imports.

GamePlayActivity in this project receives the rectangle only as numbers and builds its own org.opencv.core.Rect, so the name resolves cleanly there. MainActivity shares its file with the TrimView callback, whose parameter is the Android rectangle, so its Rect resolves to android.graphics.Rect. Identical crop lines, different imports, opposite results.

The ten-second test: open both import blocks, or hover the Rect symbol and read the package the IDE shows. No rebuild needed. The standing rule afterwards: in any file that legitimately holds both classes, alias them at the top — UiRect and Rect — so no bare name is ever ambiguous again.

How do you pass the trim rectangle from the UI to the vision code?

Keep the Android rectangle in the UI layer and convert once at the vision boundary: read left, top, right, bottom from android.graphics.Rect, then build org.opencv.core.Rect(left, top, right - left, bottom - top) right where the Mat is cropped.

The project already ships four string extras — RECT_LEFT, RECT_TOP, RECT_RIGHT, RECT_BOTTOM — and parses them with toInt() in GamePlayActivity. That works; the only rule is where the conversion happens:

// GamePlayActivity, after parsing the four Int extras
val uiRect = android.graphics.Rect(trimLeft, trimTop, trimRight, trimBottom)
val crop = Rect(uiRect.left, uiRect.top, uiRect.width(), uiRect.height())

One helper owning the conversion keeps MainActivity and GamePlayActivity from drifting apart:

fun uiToCvRect(r: UiRect): Rect = Rect(r.left, r.top, r.width(), r.height())

How large the rectangle should be for a matching task — big enough to hold the part plus search margin, small enough to stay inside the frame — is its own sizing decision, walked through in Pattern match error template image too small: fix ROI size.

Does Mat(Mat, Rect) copy pixels or share them?

It shares: Mat(Mat, Rect) builds a header over the parent's existing buffer and copies no pixels, so the trim costs nothing, and OpenCV frees the buffer only after every Mat referencing it has been released.

Two consequences matter in this pipeline. First, writes go through: Core.inRange with the trimmed Mat as destination writes the mask into the parent's pixels, while the region outside the crop stays untouched — exactly what you want when you return the full frame to the screen. Second, storage is reference-counted, so releasing mMatChange in onCameraViewStopped does not free bytes that a retained trim still references; release both headers.

The five-line test that proves the sharing:

val parent = Mat(240, 320, CvType.CV_8UC1)
parent.setTo(Scalar.all(0.0))
val view = Mat(parent, Rect(0, 0, 10, 10))  // trim: header only, no copy
view.setTo(Scalar.all(255.0))               // write through the view
check(parent.get(0, 0)[0] == 255.0)         // parent shows 255: one buffer

parent.get(0, 0) reads row 0, column 0 of the parent into a DoubleArray; 255.0 there proves the write landed through the other header.

Two habits follow. Allocate the trim once per rectangle change instead of every onCameraFrame — Reuse image buffers instead of per-frame allocation shows the pattern. And when the frame source becomes a machine-vision SDK buffer rather than an Android preview, the same header rule decides whether a copy happens at all — see Wrap an SDK frame buffer in an OpenCV cv::Mat without copying.

When does this pipeline move onto machine-vision hardware?

When the prototype has to inspect parts at line speed on a fixed station, move the same Mat pipeline onto hardware built for it: on-device inspection devices need no PC, and multi-camera stations get trigger and light control from a controller class of their own.

A smart camera runs the whole inspection on the device, and the org.opencv.core discipline carries over one-to-one. Multi-camera cells with built-in light channels belong to vision controllers, and wide-frame inspections are the territory of area scan cameras. Two fixed-lens devices that carry this pipeline pattern onto a line:

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
Smart cameras & vision sensorsHikrobot MV-SC3050XC-16M-ATN-H 5 MP color vision sensorMV-SC3050XC-16M-ATN-H
Interface
Fast Ethernet
Resolution
5 MP
$1,809.00View part

The LS-SMC-0063 pairs a 1.6 MP sensor with an integrated 16 mm lens and runs 60 fps over GigE; the MV-SC3050XC-16M-ATN-H is a 5 MP color vision sensor with an M12 lens mount on Fast Ethernet. Both run the inspection on the device, so the rectangle discipline you just fixed becomes fixed firmware behavior instead of app code.

If the prototype has earned its keep, size the production version in the camera finder or start at the configurator; for help matching sensor, lens and light to the part you were drawing rectangles around, the contact page reaches the applications team. More debugging patterns from live integrations sit on the systems topic page.

Questions

Why does Kotlin not warn you before mixing the two Rect classes?

Both classes expose a constructor taking four Int values, so the argument shapes match even though the meanings differ; only the class of the variable decides which overload list applies. Kotlin resolves the bare name Rect to whichever class the file imports, so a stale auto-import compiles happily and shifts the crop at runtime. Keep one rectangle type per import block or alias one of them.

Can trimArea stay a nullable Rect?

Yes for the compiler: the Java binding declares its parameters as platform types, printed as Mat! and Rect! in the error, so Kotlin lets a nullable in. The runtime is less forgiving: a null passed where the crop is built throws a NullPointerException before OpenCV runs. Initialize the rectangle before the first onCameraFrame call, for example with a full-frame default, and keep it non-null afterwards.

How do you trim rows or columns without a Rect?

Use the Range overloads the error already lists. Mat(parent, Range(0, 240)) keeps the top 240 rows and trims nothing sideways, and Mat(parent, Range(0, 240), Range(40, 280)) trims both directions: rows 0 to 239, columns 40 to 279. Ranges are half-open like the edges of the Android rectangle, so the second number is the stop, not a width.

What do the exclamation marks in Mat! and Rect! mean?

They mark platform types: values coming from Java, where the compiler cannot see nullability. Kotlin treats them as nullable and non-null at once, which is why the error message prints Mat! and Rect! rather than Mat and Rect, and why passing a nullable mMatChange does not itself trigger the overload failure. The failure is the Rect class, not the exclamation marks.

Can you crop the Bitmap the same way?

Bitmap.createBitmap(source, x, y, width, height) uses x, y, width, height like org.opencv.core.Rect, so the same conversion applies. Prefer keeping the crop on the Mat and calling Utils.matToBitmap once at the end of onCameraFrame, as the pipeline already does: cropping the Bitmap duplicates the pixels, while the Mat trim is only a header over the buffer you already have.