为何OpenCV imread()会混淆图像宽高?如何统一处理?
问题原因与解决方案
核心原因:EXIF方向标签导致存储尺寸与显示尺寸不一致
OpenCV读取图像时,默认忽略EXIF中的Orientation(方向)标签,直接返回图像像素矩阵的原始尺寸:shape[0]是像素矩阵的行数(对应存储的高度),shape[1]是列数(对应存储的宽度)。
你提到的“实际尺寸”,是系统看图软件或相册根据EXIF方向标签旋转后显示的尺寸。当照片为竖屏拍摄时,很多设备会将像素按横屏存储,但通过EXIF标记旋转方向(比如顺时针90度),此时:
- 存储的像素尺寸是
宽5312 × 高2988(对应OpenCV返回的(2988,5312,3),即shape[0]=2988(存储高度),shape[1]=5312(存储宽度)) - 看图软件根据EXIF旋转90度后,显示为
宽2988 × 高5312,这就和OpenCV返回的尺寸看起来“颠倒”了
示例二中的照片没有EXIF旋转标记或标记为正常方向,所以存储尺寸和显示尺寸一致,OpenCV返回的shape也就和你认知的“实际尺寸”匹配。
统一获取显示尺寸的解决方案
要得到和看图软件一致的实际显示尺寸,需要读取EXIF方向标签,根据标签调整宽高,或直接旋转图像让OpenCV处理后的尺寸与显示尺寸对齐。
方案1:仅计算实际显示宽高(不旋转图像)
使用PIL库读取EXIF信息,根据Orientation标签判断是否需要交换宽高:
from PIL import Image import cv2 def get_display_size(image_path): # 读取OpenCV原始尺寸 img = cv2.imread(image_path) orig_h, orig_w = img.shape[:2] # 读取EXIF方向信息 with Image.open(image_path) as img_pil: exif = img_pil._getexif() if exif and 274 in exif: orientation = exif[274] # 仅当需要旋转90/270度时,宽高交换 if orientation in [6, 8]: display_w, display_h = orig_h, orig_w else: display_w, display_h = orig_w, orig_h else: display_w, display_h = orig_w, orig_h return display_w, display_h
方案2:旋转图像至显示方向,统一尺寸
如果需要后续处理的图像和显示方向一致,可以同时旋转图像并获取修正后的尺寸:
from PIL import Image import cv2 def get_aligned_image_and_size(image_path): img = cv2.imread(image_path) orig_h, orig_w = img.shape[:2] with Image.open(image_path) as img_pil: exif = img_pil._getexif() if exif and 274 in exif: orientation = exif[274] # 根据方向旋转图像 if orientation == 1: aligned_img = img elif orientation == 2: aligned_img = cv2.flip(img, 1) elif orientation == 3: aligned_img = cv2.rotate(img, cv2.ROTATE_180) elif orientation == 4: aligned_img = cv2.flip(img, 0) elif orientation == 5: aligned_img = cv2.flip(cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE), 1) elif orientation == 6: aligned_img = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE) elif orientation == 7: aligned_img = cv2.flip(cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE), 1) elif orientation == 8: aligned_img = cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE) else: aligned_img = img else: aligned_img = img # 旋转后的图像shape就是显示尺寸 display_h, display_w = aligned_img.shape[:2] return aligned_img, display_w, display_h
补充说明
EXIF的Orientation标签取值对应:1(正常)、2(水平翻转)、3(180度旋转)、4(垂直翻转)、5(顺时针90+水平翻转)、6(顺时针90)、7(逆时针90+水平翻转)、8(逆时针90)。只有当标签为6或8时,存储尺寸和显示尺寸的宽高会交换,其他情况宽高保持一致。
内容的提问来源于stack exchange,提问作者Noone
相关产品推荐
相关产品推荐

