基于OpenCV与Python实现两张图像的中心对齐
Got it, let's walk through how to get Image 2 perfectly centered on Image 1 using the center coordinates you've already calculated (or can calculate with a modified version of your existing code).
First, let's make sure we're on the same page: you need the (x,y) center for both images. Your current code collects centers for all contours in Image 1, but I'll assume you want the center of the main object (so we'll tweak that to pick the largest contour, which is usually the one you care about).
Step 1: Get Center Coordinates for Both Images
First, refactor your code into a reusable function to get the center of an image's main object:
def get_image_center(img): gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray, 120, 255, 1) contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # Pick the largest contour (adjust this if you need a specific one) main_contour = max(contours, key=cv2.contourArea) M = cv2.moments(main_contour) cX = int(M["m10"] / M["m00"]) cY = int(M["m01"] / M["m00"]) return (cX, cY)
Use this function on both Image 1 and Image 2 to get center1 and center2 as tuples (e.g., (200, 300)).
Step 2: Calculate How Much to Shift Image 2
To align Image 2's center with Image 1's, compute the offset (dx, dy) that moves Image 2's center to match Image 1's:
dx = center1[0] - center2[0] dy = center1[1] - center2[1]
Positive values mean shift right/down; negative values mean shift left/up.
Step 3: Apply the Translation
Use OpenCV's warpAffine to shift Image 2. First, create the transformation matrix:
translation_matrix = np.float32([[1, 0, dx], [0, 1, dy]])
Then apply it to Image 2, setting the output size to match Image 1 so the result fits perfectly:
aligned_img2 = cv2.warpAffine(img2, translation_matrix, (img1.shape[1], img1.shape[0]))
This gives you Image 2 shifted so its center matches Image 1's. If Image 2 is larger than Image 1, parts of it will be cropped (which aligns with your desired effect).
Alternative: Overlay Image 2 Centered on Image 1
If you want to keep Image 1 as the background and paste Image 2 centered on top (instead of just shifting Image 2), use this method:
# Get dimensions of Image 2 rows2, cols2 = img2.shape[:2] # Calculate top-left corner of Image 2 when centered on Image 1 x_offset = center1[0] - cols2 // 2 y_offset = center1[1] - rows2 // 2 # Ensure we don't go outside Image 1's bounds x1 = max(0, x_offset) y1 = max(0, y_offset) x2 = min(img1.shape[1], x_offset + cols2) y2 = min(img1.shape[0], y_offset + rows2) # Calculate the corresponding region to take from Image 2 img2_x1 = max(0, -x_offset) img2_y1 = max(0, -y_offset) img2_x2 = cols2 - max(0, x_offset + cols2 - img1.shape[1]) img2_y2 = rows2 - max(0, y_offset + rows2 - img1.shape[0]) # Paste Image 2 onto Image 1 overlay_result = img1.copy() overlay_result[y1:y2, x1:x2] = img2[img2_y1:img2_y2, img2_x1:img2_x2]
This overlays Image 2 centered on Image 1, clipping any parts that go outside Image 1's edges.
Full Working Code
Here's the complete script putting it all together:
import cv2 import numpy as np import os def get_image_center(img): gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray, 120, 255, 1) contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) main_contour = max(contours, key=cv2.contourArea) M = cv2.moments(main_contour) cX = int(M["m10"] / M["m00"]) cY = int(M["m01"] / M["m00"]) return (cX, cY) # Load your images img1 = cv2.imread(os.path.expanduser('~\\Desktop\\c1.png')) img2 = cv2.imread(os.path.expanduser('~\\Desktop\\c2.png')) # Replace with your Image 2 path # Get centers center1 = get_image_center(img1) center2 = get_image_center(img2) # Option 1: Shift Image 2 to align its center with Image 1's dx = center1[0] - center2[0] dy = center1[1] - center2[1] translation_matrix = np.float32([[1, 0, dx], [0, 1, dy]]) aligned_img2 = cv2.warpAffine(img2, translation_matrix, (img1.shape[1], img1.shape[0])) # Option 2: Overlay Image 2 centered on Image 1 rows2, cols2 = img2.shape[:2] x_offset = center1[0] - cols2 // 2 y_offset = center1[1] - rows2 // 2 x1, y1 = max(0, x_offset), max(0, y_offset) x2, y2 = min(img1.shape[1], x_offset + cols2), min(img1.shape[0], y_offset + rows2) img2_x1, img2_y1 = max(0, -x_offset), max(0, -y_offset) img2_x2, img2_y2 = cols2 - max(0, x_offset + cols2 - img1.shape[1]), rows2 - max(0, y_offset + rows2 - img1.shape[0]) overlay_result = img1.copy() overlay_result[y1:y2, x1:x2] = img2[img2_y1:img2_y2, img2_x1:img2_x2] # Save or display results cv2.imwrite('aligned_image2.png', aligned_img2) cv2.imwrite('overlay_result.png', overlay_result) cv2.imshow('Aligned Image 2', aligned_img2) cv2.imshow('Overlay Result', overlay_result) cv2.waitKey(0) cv2.destroyAllWindows()
Quick Notes
- Contour Selection: If your image has multiple contours and you don't want the largest one, adjust the
get_image_centerfunction to pick the right contour (e.g., filter by area, shape, etc.). - Coordinate System: Remember OpenCV uses top-left as (0,0), so positive
dyshifts down, positivedxshifts right. - Cropping: The first option (
warpAffine) will crop Image 2 if it's larger than Image 1. The overlay option does the same but keeps Image 1 as the background.
内容的提问来源于stack exchange,提问作者Luca

