Why does this error occur:
error: (-211:One of the arguments' values is out of range) The number of points in the view #0 is < 4 in function 'cvCalibrateCamera2Internal'
If I run this piece of code:
import cv2
import numpy as np
s = 100
n = 6
h = w = s
gray = np.random.randint(0, 255, (h, w), dtype=np.uint8) # Random image
objpoints = np.random.randint(0, s, size=(n, 1, 3)).astype(np.float32) # Random 'real world' points
imgpoints = objpoints[:, :, :2] # Assume camera image is 1-to-1 mapping of top down view of real world points
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
The number of points is clearly 6
, so not less than 4
. Is there someone willing and able to explain this to me?
Thanks in advance!
Edit
Based on the answer of @Christoph Rackwitz I modified the above code such that it works. However, I encountered a gotcha which initially added to my confusion, see the comment in code.
import cv2
import numpy as np
v = 3 # Multiple views
s = 100
n = 100
h = w = s
gray = np.random.randint(0, 255, (h, w), dtype=np.uint8)
objpoints = np.random.randint(0, s, size=(v, n, 3)).astype(np.float32)
objpoints[:, :, 2] = 0 # Planar coordinates
imgpoints = objpoints[:, :, :2]
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(
objpoints,
imgpoints.copy(), # GOTCHA: function fails without this copy due to it being a view instead of a separate array
gray.shape[::-1],
None,
None
)
CodePudding user response:
You need to know that this API takes a list of arrays. That's one array (in the list) for each "view"...
"Views" are pictures. You are allowed to have different objects in different views, hence it's asking for object points for each view, instead of assuming a single object in all views. You can have the same object in all views. Then you'd have to pass something like [objpoints, objpoints, objpoints, ...]
(or [objpoints] * nviews
in short) and [imgpoints1, imgpoints2, imgpoints3, ...]
.
For your sample data, just pass [objpoints], [imgpoints], ...
(one view, so one object points array and one image points array) and that'll make the API happy about this error.
The code you tried in your question would present six views (first dimension) to the API, with each containing one point (second dimension)...
Since your sample object points aren't planar (z=0 for all), it's then complaining that it wants an "initial intrinsic matrix". It won't complain if you give it planar object points.
As for the "gotcha": OpenCV has no problem with views as such, if they are contiguous in the innermost 1-2 dimensions. A copy ensures that, but a copy costs some effort, so don't copy without a real reason.
OpenCV's cv::Mat
is less flexible than a numpy array. A numpy slice is generally no longer contiguous, meaning elements "in the slice" aren't immediately adjacent to each other in memory. Say you have a = np.arange(10); b = a[::2]
then b
points to the same memory as a
("is a view of"), but only every second element.
cv::Mat
has provisions for a larger-than-width "row stride". However you can't have (non-trivial) strides between channels (third dimension) and I think you can't have strides between pixels.