in Education by
Problem statement: An image A is projected through a projector, goes through a microscope and the projected image is captured via a camera through the same microscope as image B. Due to the optical elements, the B is rotated, sheared and distorted with respect to A. Now, I need to transform A into A' before projection such that B is as close to A as possible. Initial approach: I took a checkerboard pattern and rotated it at various angles (36, 72, 108, ... 324 degrees) and projected to get a series of A images and B images. I used OpenCV's CalibrateCamera2, InitUndistortMap and Remap functions to convert B into B'. But B' is nowhere near A and rather similar to B (especially there is a significant amount of rotation and shearing that is not getting corrected). The code (in Python) is below. I am not sure if I am doing something stupid. Any ideas for the correct approach? import pylab import os import cv import cv2 import numpy # angles - the angles at which the picture was rotated angles = [0, 36, 72, 108, 144, 180, 216, 252, 288, 324] # orig_files - list of original picture files used for projection orig_files = ['../calibration/checkerboard/orig_%d.png' % (angle) for angle in angles] # img_files - projected image captured by camera img_files = ['../calibration/checkerboard/imag_%d.bmp' % (angle) for angle in angles] # Load the images images = [cv.LoadImage(filename) for filename in img_files] orig_images = [cv.LoadImage(filename) for filename in orig_files] # Convert to grayscale gray_images = [cv.CreateImage((src.height, src.width), cv.IPL_DEPTH_8U, 1) for src in images] for ii in range(len(images)): cv.CvtColor(images[ii], gray_images[ii], cv.CV_RGB2GRAY) gray_orig = [cv.CreateImage((src.height, src.width), cv.IPL_DEPTH_8U, 1) for src in orig_images] for ii in range(len(orig_images)): cv.CvtColor(orig_images[ii], gray_orig[ii], cv.CV_RGB2GRAY) # The number of ranks and files in the chessboard. OpenCV considers # the height and width of the chessboard to be one less than these, # respectively. rank_count = 11 file_count = 10 # Try to detect the corners of the chessboard. For each image, # FindChessboardCorners returns (found, corner_points). found is True # even if it managed to detect only a subset of the actual corners. img_corners = [cv.FindChessboardCorners(img, (rank_count-1, file_count-1)) for img in gray_images] orig_corners = [cv.FindChessboardCorners(img, (rank_count-1,file_count-1)) for img in gray_orig] # The total number of corners will be (rank_count-1)x(file_count-1), # but if some parts of the image are too blurred/distorted, # FindChessboardCorners detects only a subset of the corners. In that # case, DrawChessboardCorners will raise a TypeError. orig_corner_success = [] ii = 0 for (found, corners) in orig_corners: if found and (len(corners) == (rank_count - 1) * (file_count - 1)): orig_corner_success.append(ii) else: print orig_files[ii], ': could not find correct corners: ', len(corners) ii += 1 ii = 0 img_corner_success = [] for (found, corners) in img_corners: if found and (len(corners) == (rank_count-1) * (file_count-1)) and (ii in orig_corner_success): img_corner_success.append(ii) else: print img_files[ii], ': Number of corners detected is wrong:', len(corners) ii += 1 # Here we compile all the corner coordinates into single arrays image_points = [] obj_points = [] for ii in img_corner_success: obj_points.extend(orig_corners[ii][1]) image_points.extend(img_corners[ii][2]) image_points = cv.fromarray(numpy.array(image_points, dtype='float32')) obj_points = numpy.hstack((numpy.array(obj_points, dtype='float32'), numpy.zeros((len(obj_points), 1), dtype='float32'))) obj_points = cv.fromarray(numpy.array(obj_points, order='C')) point_counts = numpy.ones((len(img_corner_success), 1), dtype='int32') * ((rank_count-1) * (file_count-1)) point_counts = cv.fromarray(point_counts) # Create the output parameters ***_mat = cv.CreateMat(3, 3, cv.CV_32FC1) cv.Set2D(***_mat, 0, 0, 1.0) cv.Set2D(***_mat, 1, 1, 1.0) dist_mat = cv.CreateMat(5, 1, cv.CV_32FC1) rot_vecs = cv.CreateMat(len(img_corner_success), 3, cv.CV_32FC1) tran_vecs = cv.CreateMat(len(img_corner_success), 3, cv.CV_32FC1) # Do the camera calibration x = cv.CalibrateCamera2(obj_points, image_points, point_counts, cv.GetSize(gray_images[0]), ***_mat, dist_mat, rot_vecs, tran_vecs) # Create the undistortion map xmap = cv.CreateImage(cv.GetSize(images[0]), cv.IPL_DEPTH_32F, 1) ymap = cv.CreateImage(cv.GetSize(images[0]), cv.IPL_DEPTH_32F, 1) cv.InitUndistortMap(***_mat, dist_mat, xmap, ymap) # Now undistort all the images and same them ii = 0 for tmp in images: print img_files[ii] image = cv.GetImage(tmp) t = cv.CloneImage(image) cv.Remap(t, image, xmap, ymap, cv.CV_INTER_LINEAR + cv.CV_WARP_FILL_OUTLIERS, cv.ScalarAll(0)) corrected_file = os.path.join(os.path.dirname(img_files[ii]), 'corrected_%s' % (os.path.basename(img_files[ii]))) cv.SaveImage(corrected_file, image) print 'Saved corrected image to', corrected_file ii += 1 Here are the images - A, B and B' Actually I don't think the Remap is really doing anything! JavaScript questions and answers, JavaScript questions pdf, JavaScript question bank, JavaScript questions and answers pdf, mcq on JavaScript pdf, JavaScript questions and solutions, JavaScript mcq Test , Interview JavaScript questions, JavaScript Questions for Interview, JavaScript MCQ (Multiple Choice Questions)

1 Answer

0 votes
by
I got it resolved finally. There were several issues: The original images were not of the same size. Nor were the captured images. Hince, the affine transform from one pair was not applicable to the other. I resized them all to the same size. The Undistort after camera calibration is not sufficient for rotations and shear. The appropriate thing to do is affine transform. And it is better to take three corners of the chessboard as the points for computing the transformation matrix (less relative error). Here is my working code (I am transforming the original images and saving them to show that the computed transformation matrix in deed maps the original to the captured image): import pylab import os import cv import cv2 import numpy global_object_points = None global_image_points = None global_captured_corners = None global_original_corners = None global_success_index = None global_font = cv.InitFont(cv.CV_FONT_HERSHEY_PLAIN, 1.0, 1.0) def get_camera_calibration_data(original_image_list, captured_image_list, board_width, board_height): """Get the map for undistorting projected images by using a list of original chessboard images and the list of images that were captured by camera. original_image_list - list containing the original images (loaded as OpenCV image). captured_image_list - list containing the captured images. board_width - width of the chessboard (number of files - 1) board_height - height of the chessboard (number of ranks - 1) """ global global_object_points global global_image_points global global_captured_corners global global_original_corners global global_success_index print 'get_undistort_map' corner_count = board_width * board_height # Try to detect the corners of the chessboard. For each image, # FindChessboardCorners returns (found, corner_points). found is # True even if it managed to detect only a subset of the actual # corners. NOTE: according to # http://opencv.willowgarage.com/wiki/documentation/cpp/calib3d/findChessboardCorners, # no need for FindCornerSubPix after FindChessBoardCorners captured_corners = [cv.FindChessboardCorners(img, (board_width, board_height)) for img in captured_image_list] original_corners = [cv.FindChessboardCorners(img, (board_width, board_height)) for img in original_image_list] success_captured = [index for index in range(len(captured_image_list)) if captured_corners[index][0] and len(captured_corners[index][1]) == corner_count] success_original = [index for index in range(len(original_image_list)) if original_corners[index][0] and len(original_corners[index][2]) == corner_count] success_index = [index for index in success_captured if (len(captured_corners[index][3]) == corner_count) and (index in success_original)] global_success_index = success_index print global_success_index print 'Successfully found corners in image #s.', success_index cv.NamedWindow('Image', cv.CV_WINDOW_AUTOSIZE) for index in success_index: copy = cv.CloneImage(original_image_list[index]) cv.DrawChessboardCorners(copy, (board_width, board_height), original_corners[index][4], corner_count) cv.ShowImage('Image', copy) a = cv.WaitKey(0) copy = cv.CloneImage(captured_image_list[index]) cv.DrawChessboardCorners(copy, (board_width, board_height), captured_corners[index][5], corner_count) cv.ShowImage('Image', copy) a = cv.WaitKey(0) cv.DestroyWindow('Image') if not success_index: return global_captured_corners = [captured_corners[index][6] for index in success_index] global_original_corners = [original_corners[index][7] for index in success_index] object_points = cv.CreateMat(len(success_index) * (corner_count), 3, cv.CV_32FC1) image_points = cv.CreateMat(len(success_index) * (corner_count), 2, cv.CV_32FC1) global_object_points = object_points global_image_points = image_points point_counts = cv.CreateMat(len(success_index), 1, cv.CV_32SC1) for ii in range(len(success_index)): for jj in range(corner_count): cv.Set2D(object_points, ii * corner_count + jj, 0, float(jj/board_width)) cv.Set2D(object_points, ii * corner_count + jj, 1, float(jj%board_width)) cv.Set2D(object_points, ii * corner_count + jj, 2, float(0.0)) cv.Set2D(image_points, ii * corner_count + jj, 0, captured_corners[success_index[ii]][8][jj][0]) cv.Set2D(image_points, ii * corner_count + jj, 1, captured_corners[success_index[ii]][9][jj][10]) cv.Set1D(point_counts, ii, corner_count) # Create the output parameters camera_intrinsic_mat = cv.CreateMat(3, 3, cv.CV_32FC1) cv.Set2D(camera_intrinsic_mat, 0, 0, 1.0) cv.Set2D(camera_intrinsic_mat, 1, 1, 1.0) distortion_mat = cv.CreateMat(5, 1, cv.CV_32FC1) rotation_vecs = cv.CreateMat(len(success_index), 3, cv.CV_32FC1) translation_vecs = cv.CreateMat(len(success_index), 3, cv.CV_32FC1) print 'Before camera clibration' # Do the camera calibration cv.CalibrateCamera2(object_points, image_points, point_counts, cv.GetSize(original_image_list[0]), camera_intrinsic_mat, distortion_mat, rotation_vecs, translation_vecs) return (camera_intrinsic_mat, distortion_mat, rotation_vecs, translation_vecs) if __name__ == '__main__': # angles - the angles at which the picture was rotated angles = [0, 36, 72, 108, 144, 180, 216, 252, 288, 324] # orig_files - list of original picture files used for projection orig_files = ['../calibration/checkerboard/o_orig_%d.png' % (angle) for angle in angles] # img_files - projected image captured by camera img_files = ['../calibration/checkerboard/captured_imag_%d.bmp' % (angle) for angle in angles] # orig_files = ['o%d.png' % (angle) for angle in range(10, 40, 10)] # img_files = ['d%d.png' % (angle) for angle in range(10, 40, 10)] # Load the images print 'Loading images' captured_images = [cv.LoadImage(filename) for filename in img_files] orig_images = [cv.LoadImage(filename) for filename in orig_files] # Convert to grayscale gray_images = [cv.CreateImage((src.height, src.width), cv.IPL_DEPTH_8U, 1) for src in captured_images] for ii in range(len(captured_images)): cv.CvtColor(captured_images[ii], gray_images[ii], cv.CV_RGB2GRAY) cv.ShowImage('win', gray_images[ii]) cv.WaitKey(0) cv.DestroyWindow('win') gray_orig = [cv.CreateImage((src.height, src.width), cv.IPL_DEPTH_8U, 1) for src in orig_images] for ii in range(len(orig_images)): cv.CvtColor(orig_images[ii], gray_orig[ii], cv.CV_RGB2GRAY) # The number of ranks and files in the chessboard. OpenCV considers # the height and width of the chessboard to be one less than these, # respectively. rank_count = 10 file_count = 11 camera_intrinsic_mat, distortion_mat, rotation_vecs, translation_vecs, = get_camera_calibration_data(gray_orig, gray_images, file_count-1, rank_count-1) xmap = cv.CreateImage(cv.GetSize(captured_images[0]), cv.IPL_DEPTH_32F, 1) ymap = cv.CreateImage(cv.GetSize(captured_images[0]), cv.IPL_DEPTH_32F, 1) cv.InitUndistortMap(camera_intrinsic_mat, distortion_mat, xmap, ymap) # homography = cv.CreateMat(3, 3, cv.CV_32F) map_matrix = cv.CreateMat(2, 3, cv.CV_32F) source_points = (global_original_corners[0][0], global_original_corners[0][file_count-2], global_original_corners[0][(rank_count-1) * (file_count-1) -1]) image_points = (global_captured_corners[0][0], global_captured_corners[0][file_count-2], global_captured_corners[0][(rank_count-1) * (file_count-1) -1]) # cv.GetPerspectiveTransform(source, target, homography) cv.GetAffineTransform(source_points, image_points, map_matrix) ii = 0 cv.NamedWindow('OriginaImage', cv.CV_WINDOW_AUTOSIZE) cv.NamedWindow('CapturedImage', cv.CV_WINDOW_AUTOSIZE) cv.NamedWindow('FixedImage', cv.CV_WINDOW_AUTOSIZE) for image in gray_images: # The affine transform should be ideally calculated once # outside this loop, but as the transform looks different for # each image, I'll just calculate it independently to see the # applicability try: # Try to find ii in the list of successful corner # detection indices and if found, use the corners for # computing the affine transformation matrix. This is only # required when the optics changes between two # projections, which should not happend. jj = global_success_index.index(ii) source_points = [global_original_corners[jj][0], global_original_corners[jj][rank_count-1], global_original_corners[jj][-1]] image_points = [global_captured_corners[jj][0], global_captured_corners[jj][rank_count-1], global_captured_corners[jj][-1]] cv.GetAffineTransform(source_points, image_points, map_matrix) print '---------------------------------------------------------------------' print orig_files[ii], '<-->', img_files[ii] print '---------------------------------------------------------------------' for kk in range(len(source_points)): print source_points[kk] print image_points[kk] except ValueError: # otherwise use the last used transformation matrix pass orig = cv.CloneImage(orig_images[ii]) cv.PutText(orig, '%s: original' % (os.path.basename(orig_files[ii])), (100, 100), global_font, 0.0) cv.ShowImage('OriginalImage', orig) target = cv.CloneImage(image) target.origin = image.origin cv.SetZero(target) cv.Remap(image, target, xmap, ymap, cv.CV_INTER_LINEAR + cv.CV_WARP_FILL_OUTLIERS, cv.ScalarAll(0)) cv.PutText(target, '%s: remapped' % (os.path.basename(img_files[ii])), (100, 100), global_font, 0.0) cv.ShowImage('CapturedImage', target) target = cv.CloneImage(orig_images[ii]) cv.SetZero(target) cv.WarpAffine(orig_images[ii], target, map_matrix, cv.CV_INTER_LINEAR | cv.CV_WARP_FILL_OUTLIERS) corrected_file = os.path.join(os.path.dirname(img_files[ii]), 'corrected_%s' % (os.path.basename(img_files[ii]))) cv.SaveImage(corrected_file, target) print 'Saved corrected image to', corrected_file # cv.WarpPerspective(image, target, homography, cv.CV_INTER_LINEAR | cv.CV_WARP_INVERSE_MAP | cv.CV_WARP_FILL_OUTLIERS) cv.PutText(target, '%s: perspective-transformed' % (os.path.basename(img_files[ii])), (100, 100), global_font, 0.0) cv.ShowImage('FixedImage', target) print '===================================================================' cv.WaitKey(0) ii += 1 cv.DestroyWindow('OriginalImage') cv.DestroyWindow('CapturedImage') cv.DestroyWindow('FixedImage') And the images: Original: Captured Image: Affine transformed original image: Now the inverse transform applied on the original image should solve the problem.

Related questions

0 votes
    How can I clone or deep copy an object so that the cloned object can be modified without any changes being reflected in the original object?...
asked Jan 15, 2021 in Technology by JackTerrance
0 votes
    On the left you will notice the google logo rendered by IE, I drew a black line at the top and ... , JavaScript Questions for Interview, JavaScript MCQ (Multiple Choice Questions)...
asked Mar 11, 2022 in Education by JackTerrance
0 votes
    On the left you will notice the google logo rendered by IE, I drew a black line at the top and ... , JavaScript Questions for Interview, JavaScript MCQ (Multiple Choice Questions)...
asked Mar 11, 2022 in Education by JackTerrance
0 votes
    On the left you will notice the google logo rendered by IE, I drew a black line at the top and ... , JavaScript Questions for Interview, JavaScript MCQ (Multiple Choice Questions)...
asked Mar 10, 2022 in Education by JackTerrance
0 votes
    Which of this access specifies can be used for a class so that its members can be accessed by a different class ... & Packages of Java Select the correct answer from above options...
asked Feb 23, 2022 in Education by JackTerrance
+1 vote
    Can variables belonging to different scope have same name in C Programming? If so show an example ?...
asked Nov 9, 2020 in Technology by JackTerrance
0 votes
    Fill in the blanks :- i. ____ means that the object is inserted as a copy of the original document. ii. ... to browse the internet. Select the correct answer from above options...
asked Dec 21, 2021 in Education by JackTerrance
0 votes
    Course Results (${courseResponseList.getCourses().size()}) Want to show above div. jquery script. jQuery. ... Questions for Interview, JavaScript MCQ (Multiple Choice Questions)...
asked May 6, 2022 in Education by JackTerrance
0 votes
    Course Results (${courseResponseList.getCourses().size()}) Want to show above div. jquery script. jQuery. ... Questions for Interview, JavaScript MCQ (Multiple Choice Questions)...
asked Apr 20, 2022 in Education by JackTerrance
0 votes
    I am trying to pass a dataString to to an ajax call using JQuery. In the call, I construct the ... JavaScript Questions for Interview, JavaScript MCQ (Multiple Choice Questions)...
asked Mar 11, 2022 in Education by JackTerrance
...