diff --git a/stitching/imgs_out/pano.jpg b/stitching/imgs_out/pano.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2059fca210816dd4e8612942e93e51e0c5638f03 Binary files /dev/null and b/stitching/imgs_out/pano.jpg differ diff --git a/stitching/scan.py b/stitching/scan.py index 516703ba838ca5d68271fed2929a44ce0a33126c..b13dff20e078a61c54d21bf7d3f7d6e2e8e0be99 100644 --- a/stitching/scan.py +++ b/stitching/scan.py @@ -109,7 +109,7 @@ def get_warped_masks_and_images(imgs, cameras, warp_scale, seam_aspect): num_imgs = len(imgs) sizes = get_sizes(imgs) for i in range(0, num_imgs): - um = cv.UMat(255 * np.ones(sizes[i], np.uint8)) + um = cv.UMat(255 * np.ones((sizes[i][1], sizes[i][0]), np.uint8)) masks.append(um) warper = cv.PyRotationWarper('affine', warp_scale * seam_aspect) for idx in range(num_imgs): @@ -127,27 +127,26 @@ def get_warped_masks_and_images(imgs, cameras, warp_scale, seam_aspect): return corners, images_warped, masks_warped -def compensate_exposure(corners, images_warped, masks_warped): +def estimate_exposure_compensation(corners, images_warped, masks_warped): comp = cv.detail.ExposureCompensator_createDefault(cv.detail.ExposureCompensator_GAIN_BLOCKS) - comp.feed(corners, images_warped, masks_warped) + comp.feed(corners=corners, images=images_warped, masks=masks_warped) return comp def find_seams(corners, imgs_w, msks_w): imgs_w_f = [img.astype(np.float32) for img in imgs_w] - seam_finder = cv.detail_GraphCutSeamFinder('COST_COLOR') - seam_finder.find(imgs_w_f, corners, msks_w) - return msks_w + seam_finder = cv.detail.SeamFinder_createDefault(cv.detail.SeamFinder_VORONOI_SEAM) + # seam_finder = cv.detail_DpSeamFinder('COLOR') + # seam_finder = cv.detail.SeamFinder_createDefault(cv.detail.SeamFinder_NO) + updated_masks = seam_finder.find(imgs_w_f, corners, msks_w) + return updated_masks -def resize_seam_masks(msks_w, orig_msks): - seam_msks = [] - for idx in range(len(msks_w)): - dilated_mask = cv.dilate(msks_w[idx], None) - seam_mask = resize_mask(dilated_mask, orig_msks[idx]) - mask_warped = cv.bitwise_and(seam_mask, orig_msks[idx]) - seam_msks.append(mask_warped) - return seam_msks +def resize_seam_mask(msk_w, orig_msk): + dilated_mask = cv.dilate(msk_w, None) + seam_mask = resize_mask(dilated_mask, orig_msk) + mask_warped = cv.bitwise_and(seam_mask, orig_msk) + return mask_warped def get_sizes(imgs): @@ -158,7 +157,7 @@ def get_sizes(imgs): return sizes -def blend_images(corners, imgs, msks, msks_w, comp): +def blend_images(corners, imgs, seam_msks, msks_w, comp): sizes = get_sizes(imgs) blender = cv.detail_MultiBandBlender() dst_sz = cv.detail.resultRoi(corners=corners, sizes=sizes) @@ -167,9 +166,9 @@ def blend_images(corners, imgs, msks, msks_w, comp): blender.setNumBands((np.log(blend_width) / np.log(2.) - 1.).astype(np.int32)) blender.prepare(dst_sz) for idx in range(len(imgs)): - comp.apply(idx, corners[idx], imgs[idx], msks[idx]) + comp.apply(idx, corners[idx], imgs[idx], msks_w[idx]) img_s = imgs[idx].astype(np.int16) - msk_w = resize_seam_masks(msks[idx], msks_w[idx]) + msk_w = resize_seam_mask(seam_msks[idx], msks_w[idx]) blender.feed(cv.UMat(img_s), msk_w, corners[idx]) result = None result_mask = None @@ -189,24 +188,39 @@ def get_reg_data(imgs, img_names, reg_megapix=0.6): return indices, graph, cameras -def compose_from_reg_data(imgs, indices, cameras, reg_scale, seam_scale): # TODO: fully implement composing step - warp_scale = get_warped_image_scale(cameras) - seam_aspect = seam_scale / reg_scale - corners, imgs_w, msks_w = get_warped_masks_and_images(imgs, cameras, warp_scale, seam_aspect) - return corners +def prepare_reg_data(imgs, indices, reg_megapix=0.6, seam_megapix=0.1): + imgs_subset = [imgs[i[0]] for i in indices] + reg_scale = get_scale(imgs[0], megapix=reg_megapix) + seam_scale = get_scale(imgs[0], megapix=seam_megapix) + return imgs_subset, reg_scale, seam_scale + + +def compose_from_reg_data(imgs, cameras, reg_scale, seam_scale): + warp_scale_full = get_warped_image_scale(cameras) + corners_full, images_w_full, masks_w_full = get_warped_masks_and_images(imgs, cameras, warp_scale_full, 1/reg_scale) + imgs_low_res = [resize_image(img, seam_scale) for img in imgs] + warp_scale_low = get_warped_image_scale(cameras) + corners_low, images_w_low, masks_w_low = get_warped_masks_and_images(imgs_low_res, cameras, warp_scale_low, + seam_scale/reg_scale) + comp = estimate_exposure_compensation(corners_low, images_w_low, masks_w_low) + seam_masks = find_seams(corners_low, images_w_low, masks_w_low) + result, result_mask = blend_images(corners_full, images_w_full, seam_masks, masks_w_full, comp) + return result, result_mask def full_stitching_pipeline(imgs, img_names): - indices, graph, cameras = get_reg_data(imgs, img_names) - pano = compose_from_reg_data(imgs, indices, cameras) - return pano + indices, graph, cameras = get_reg_data(imgs, img_names, reg_megapix=0.6) + imgs_subset, reg_scale, seam_scale = prepare_reg_data(imgs, indices, reg_megapix=0.6, seam_megapix=0.1) + result, result_mask = compose_from_reg_data(imgs_subset, cameras, reg_scale, seam_scale) + return result if __name__ == '__main__': img_names = [f'pano_{i}.jpg' for i in range(1, 4)] imgs = [cv.imread(f'imgs/{name}') for name in img_names] - indices, graph, cameras = get_reg_data(imgs, img_names) - print(graph) - reg_scale = get_scale(imgs[0], megapix=0.6) - seam_scale = get_scale(imgs[0], megapix=0.1) - print(compose_from_reg_data(imgs, indices, cameras, reg_scale, seam_scale)) + # print(imgs[0].shape[0] * imgs[0].shape[1] / 1e6) + result = full_stitching_pipeline(imgs, img_names) + print(result.shape) + out_name = 'imgs_out/pano.jpg' + cv.imwrite(out_name, result) + print('Stitched image saved as ' + out_name)