You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 05:20:43