I'd like to use the same affine matrix M on some individual (x,y) points as I use on images with cv2.warpAffine. It seems cv2.transform is the way to go . When I try send an Nx2 matrix of points I get negged (
src = np.array([
[x1,y1],[x2,y2],[x3,y3],[x4,y4]], dtype = "float32")
print('source shape '+str(src.shape))
dst=cv2.transform(src,M)
cv2.error: /home/jeremy/sw/opencv-3.1.0/modules/core/src/matmul.cpp:1947: error: (-215) scn == m.cols || scn + 1 == m.cols in function transform
I can get the transform I want just using numpy arithmetic :
dst = np.dot(src,M[:,0:2]) +M[:,2]
print('dest:{}'.format(dst))
But would like to understand whats going on . The docs say that cv2.transform wants a number of channels equal to number of columns in M but I'm not clear what the channels would be - maybe an 'x' channel and 'y' channel, but then would would the third be, and what would the different rows signify?
OpenCV on Python often wants points in the form
np.array([ [[x1, y1]], ..., [[xn, yn]] ])
This is not clear in the documentation for cv2.transform() but is more clear in the documentation for other functions that use points, like cv2.perspectiveTransform() where they mention coordinates to be on separate channels:
src – input two-channel or three-channel floating-point array
Transforms can also be used in 3D (using a 4x4 perspective transformation matrix) so that would explain the ability to use two- or three-channel arrays in cv2.transform().
The channel is the last dimension of the source array. Let's read the docs of cv2.transform() at the beginning.
To the question:
Because the function transforms each element from the parameter src, the dimension of src is required to be bigger than 2.
import cv2
import numpy as np
rotation_mat = np.array([[0.8660254, 0.5, -216.41978046], [-0.5, 0.8660254, 264.31038357]]) # 2x3
rotate_box = np.array([[410, 495], [756, 295], [956, 642], [610, 842]]) # 2x2
result_box = cv2.transform(rotate_box, rotation_mat) # error: (-215:Assertion failed) scn == m.cols || scn + 1 == m.cols in function 'transform'
The reason is the dimension of each element of rotate_box is (2,). The transform by multiplication on matrices can not proceed.
To another answer:
As long as the last dimension fits, other dimensions do not matter. Continue the above snippet:
rotate_box_1 = np.array([rotate_box]) # 1x4x2
result_box = cv2.transform(rotate_box_1, rotation_mat) # 1x4x2
rotate_box_2 = np.array([[[410, 495]], [[756, 295]], [[956, 642]], [[610, 842]]]) # 4x1x2
result_box = cv2.transform(rotate_box_2, rotation_mat) # 4x1x2
To reader:
Note the shape returned by cv2.transform() is the same as the src.
Related
My code failed to work on pca because of the dimension
I uploaded the images and then converted them to grayscale and uint8 then I calculated the mean
This line does the mean for the images but here theimages become 2d
me_df= img_df.mean(axis=0)
Then, I selected a region of interest
mask = np.zeros(me_df.shape, dtype=me_df.dtype)
mask[roi_lower:roi_upper,roi_left:roi_right] = 1
mask_df = mean_df * mask
Then I tried to convert it into 1d, but the code does not wrok as it is not 3d
I tried many things such as remove mask_df.shape[2] but the rest of the code. Did not work
The error messages are:
maximum supported dimension for an ndarray is 32, found 640 or only
integer scalar arrays can be converted to a scalar index
image_shape = mask_df[0].shape
frames_reshaped = mask_df.reshape([
mask_df.shape[0],
mask_df.shape[1]*mask_df.shape[2]
])
ncomp=4
pca = PCA(n_components=ncomp)
pca.fit(test)
The PCA did not work because of the above errors
This code should show the image of the pca output with mean image
dd = pca.components_[pc_number-1].
reshape(frames_reshaped)
mag = np.max(dd) - np.min(dd)
et = (dd-np.min(dd))/mag*255
plt.imshow(et, cmap="Greys_r")
I'm using opencv-contrib-python (4.5.4.60) to calibrate stereovision emulated by 2 pictures taken with one camera (for now I only have one of them) like there are two cameras for stereo depth estimation in future. I find intrinsic parameters of camera from several photos and trying to pass ChAruCo markers points from two photos into stereoCalibrate, but get assertion failed:
ret, M1, d1, M2, d2, R, T, E, F = cv2.stereoCalibrate(objpoints_L, imgpoints_L, imgpoints_R, camera_matrix, distortion_coefficients0, camera_matrix, distortion_coefficients0,img_r1.shape[:2], F = F)
cv2.error: OpenCV(4.5.4) D:\a\opencv-python\opencv-python\opencv\modules\calib3d\src\calibration.cpp:1088: error: (-215:Assertion failed) (count >= 4) || (count == 3 && useExtrinsicGuess) in function 'cvFindExtrinsicCameraParams2'
I have checked input type of object points and image points with cv2.utils.dumpInputArray()
InputArray: empty()=false kind=0x00010000 flags=0x01010000 total(-1)=40 dims(-1)=2 size(-1)=1x40 type(-1)=CV_32FC3
InputArray: empty()=false kind=0x00010000 flags=0x01010000 total(-1)=40 dims(-1)=2 size(-1)=1x40 type(-1)=CV_32FC2
InputArray: empty()=false kind=0x00010000 flags=0x01010000 total(-1)=40 dims(-1)=2 size(-1)=1x40 type(-1)=CV_32FC2
sorted them so I pass only matching on both photos, but still get assertion failed and can't figure out what I'm doing wrong.
The problem was that ChAruCo markers are returned as array of objects with single point (n, 1, 2) by cv2.aruco.detectMarkers. Functions accept this points format (for example cv2.solvePnP or cv2.findFundamentalMat) but if you try to pass them into cv2.stereoCalibrate, which checks for every object to have more then 3 points but get n objects with single point instead, you would get assertion failed. To use this points you have to reshape array to single object with all ChAruCo points (1,n,2). Do the same reshape to (1, n, 3) to object points obtained by cv2.aruco.getBoardObjectAndImagePoints.
So I wrote this little program which allows me to select 4 points on two images.
Usign those points I get a transformation matrix. After that I select a point on one of the images and want to get visualization of where that point will be on other image.
Say my point is marked like this -> (x,y) - so it's a tuple. How should I format this "position" on image so it can be possible to transform it.
I have looked at documentation for perspectiveTransform() method and figured that I should be storing it in following shape:
numpy.array([
[self.points[self.length-1][0]],
[self.points[self.length-1][1]]
], dtype="float32")
Which would give me on a single click this format:
Point= [[ 2300.]
[ 634.]]
This format doesn't seem to work, I use this Transformation matrix:
M = [[ -1.71913123e+00 -4.76850572e+00 5.27968944e+03]
[ 2.07693562e-01 -1.09738424e+01 6.35222770e+03]
[ 1.02865125e-04 -4.80067600e-03 1.00000000e+00]]
in this method (and get following error):
cv2.perspectiveTransform(src, M)
OpenCV Error: Assertion failed (scn + 1 == m.cols) in cv::perspectiveTransform, file C:\builds\master_PackSlaveAddon-win64-vc12-static\opencv\modules\core\src\matmul.cpp
Any advice or tip is welcome.
I figured out the answer.
Found it on this link
The key is to put your point like this:
pts = numpy.array([[x,y]], dtype = "float32")
And then call another numpy.array on existing variable pts:
pts = numpy.array([pts])
The procedure is the same after this.
I've had following codes that use Python and OpenCV. Briefly, I have a stack of image taken at different focal depth. The codes pick out pixels at every (x,y) position that has the largest Laplacian of Guassian response among all focal depth(z), thus creating a focus-stacked image. Function get_fmap creates a 2d array where each pixel will contains the number of the focal plane having the largest log response. In the following codes, lines that are commented out are my current VIPS implementation. They don't look compatible within the function definition because it's only partial solution.
# from gi.repository import Vips
def get_log_kernel(siz, std):
x = y = np.linspace(-siz, siz, 2*siz+1)
x, y = np.meshgrid(x, y)
arg = -(x**2 + y**2) / (2*std**2)
h = np.exp(arg)
h[h < sys.float_info.epsilon * h.max()] = 0
h = h/h.sum() if h.sum() != 0 else h
h1 = h*(x**2 + y**2 - 2*std**2) / (std**4)
return h1 - h1.mean()
def get_fmap(img): # img is a 3-d numpy array.
log_response = np.zeros_like(img[:, :, 0], dtype='single')
fmap = np.zeros_like(img[:, :, 0], dtype='uint8')
log_kernel = get_log_kernel(11, 2)
# kernel = get_log_kernel(11, 2)
# kernel = [list(row) for row in kernel]
# kernel = Vips.Image.new_from_array(kernel)
# img = Vips.new_from_file("testimg.tif")
for ii in range(img.shape[2]):
# img_filtered = img.conv(kernel)
img_filtered = cv2.filter2D(img[:, :, ii].astype('single'), -1, log_kernel)
index = img_filtered > log_response
log_response[index] = img_filtered[index]
fmap[index] = ii
return fmap
and then fmap will be used to pick out pixels from different focal planes to create a focus-stacked image
This is done on an extremely large image, and I feel VIPS might do a better job than OpenCV on this. However, the official documentation provides rather scant information on its Python binding. From the information I can find on the internet, I'm only able to make image convolution work ( which, in my case, is an order of magnitude faster than OpenCV.). I'm wondering how to implement this in VIPS, especially these lines?
log_response = np.zeros_like(img[:, :, 0], dtype = 'single')
index = img_filtered > log_response
log_response[index] = im_filtered[index]
fmap[index] = ii
log_response and fmap are initialized as 3D arrays in the question code, whereas the question text states that the output, fmap is a 2D array. So, I am assuming that log_response and fmap are to be initialized as 2D arrays with their shapes same as each image. Thus, the edits would be -
log_response = np.zeros_like(img[:,:,0], dtype='single')
fmap = np.zeros_like(img[:,:,0], dtype='uint8')
Now, back to the theme of the question, you are performing 2D filtering on each image one-by-one and getting the maximum index of filtered output across all stacked images. In case, you didn't know as per the documentation of cv2.filter2D, it could also be used on a multi-dimensional array giving us a multi-dimensional array as output. Then, getting the maximum index across all images is as simple as .argmax(2). Thus, the implementation must be extremely efficient and would be simply -
fmap = cv2.filter2D(img,-1,log_kernel).argmax(2)
After consulting the Python VIPS manual and some trial-and-error, I've come up with my own answer. My numpy and OpenCV implementation in question can be translated into VIPS like this:
import pyvips
img = []
for ii in range(num_z_levels):
img.append(pyvips.Image.new_from_file("testimg_z" + str(ii) + ".tif")
def get_fmap(img)
log_kernel = get_log_kernel(11,2) # get_log_kernel is my own function, which generates a 2-d numpy array.
log_kernel = [list(row) for row in log_kernel] # pyvips.Image.new_from_array takes 1-d list array.
log_kernel = pyvips.Image.new_from_array(log_kernel) # Turn the kernel into Vips array so it can be used by Vips.
log_response = img[0].conv(log_kernel)
for ii in range(len(img)):
img_filtered = img[ii+1].conv(log_kernel)
log_response = (img_filtered > log_response).ifthenelse(img_filtered, log_response)
fmap = (img_filtered > log_response).ifthenelse(ii+1, 0)
Logical indexing is achieved through ifthenelse method :
result_img = (test_condition).ifthenelse(value_if_true, value_if_false)
The syntax is rather flexible. The test condition can be a comparison between two images of the same size or between an image and a value, e.g. img1 > img2 or img > 5. Like wise, value_if_true can be a single value or a Vips image.
Cannot make this work:
img1 = cv::imread('glassL.jpg')
img2 = cv::imread('glassR.jpg')
img1g = cv::Mat.new
cv::cvtColor(img1, img1g, CV_BGR2GRAY);
img2g = cv::Mat.new
cv::cvtColor(img2, img2g, CV_BGR2GRAY);
F = cv::findFundamentalMat(img1g, img2g, cv::FM_RANSAC, 0.1, 0.99)
It throws this error:
OpenCV Error: Assertion failed (npoints >= 0 && points2.checkVector(2) == npoints && points1.type() == points2.type()) in findFundamentalMat, file /tmp/opencv-XbIS/opencv-2.4.8.2/modules/calib3d/src/fundam.cpp, line 1103
/usr/local/var/rbenv/versions/2.1.0/lib/ruby/gems/2.1.0/gems/ropencv-0.0.15/lib/ropencv/ropencv_types.rb:10509:in `find_fundamental_mat': /tmp/opencv-XbIS/opencv-2.4.8.2/modules/calib3d/src/fundam.cpp:1103: error: (-215) npoints >= 0 && points2.checkVector(2) == npoints && points1.type() == points2.type() in function findFundamentalMat (RuntimeError)
I am using ropencv (Ruby + FFI), but I tried with Python cv2 and got exactly the same error. I cannot find any documentation on this and I am lost. checkVector(2) returns -1 on both grayscale and color images and I don't know how to convert them to make them work with findFundamentalMat. Help please.
You are passing the images directly to the function of to compute the fundamental matrix and this is not correct. In the documentation, it says:
points1 – Array of N points from the first image. The point coordinates should be floating-point (single or double precision).
points2 – Array of the second image points of the same size and format as points1 .
Therefore, you cannot simply put the images. There is a full example here using OpenCV. But there is a nice explanation here step by step using matlab so you can understand which points are you going to use (such as harris corners).