Unverified Commit 227e7a73 authored by Tong Gao's avatar Tong Gao Committed by GitHub
Browse files

Support image reading while ignoring EXIF orientation info (#1091)

* Support image reading while ignoring EXIF orientation info

Add unit test for ignore_orientation flags in imread()

* add documentation for imread

* Add test cases
parent 94818ad1
...@@ -5,7 +5,8 @@ from pathlib import Path ...@@ -5,7 +5,8 @@ from pathlib import Path
import cv2 import cv2
import numpy as np import numpy as np
from cv2 import IMREAD_COLOR, IMREAD_GRAYSCALE, IMREAD_UNCHANGED from cv2 import (IMREAD_COLOR, IMREAD_GRAYSCALE, IMREAD_IGNORE_ORIENTATION,
IMREAD_UNCHANGED)
from mmcv.utils import check_file_exist, is_str, mkdir_or_exist from mmcv.utils import check_file_exist, is_str, mkdir_or_exist
...@@ -30,7 +31,10 @@ supported_backends = ['cv2', 'turbojpeg', 'pillow', 'tifffile'] ...@@ -30,7 +31,10 @@ supported_backends = ['cv2', 'turbojpeg', 'pillow', 'tifffile']
imread_flags = { imread_flags = {
'color': IMREAD_COLOR, 'color': IMREAD_COLOR,
'grayscale': IMREAD_GRAYSCALE, 'grayscale': IMREAD_GRAYSCALE,
'unchanged': IMREAD_UNCHANGED 'unchanged': IMREAD_UNCHANGED,
'color_ignore_orientation': IMREAD_IGNORE_ORIENTATION | IMREAD_COLOR,
'grayscale_ignore_orientation':
IMREAD_IGNORE_ORIENTATION | IMREAD_GRAYSCALE
} }
imread_backend = 'cv2' imread_backend = 'cv2'
...@@ -102,6 +106,7 @@ def _pillow2array(img, flag='color', channel_order='bgr'): ...@@ -102,6 +106,7 @@ def _pillow2array(img, flag='color', channel_order='bgr'):
array[:, :, :3] = array[:, :, (2, 1, 0)] # RGB to BGR array[:, :, :3] = array[:, :, (2, 1, 0)] # RGB to BGR
else: else:
# Handle exif orientation tag # Handle exif orientation tag
if flag in ['color', 'grayscale']:
img = ImageOps.exif_transpose(img) img = ImageOps.exif_transpose(img)
# If the image mode is not 'RGB', convert it to 'RGB' first. # If the image mode is not 'RGB', convert it to 'RGB' first.
if img.mode != 'RGB': if img.mode != 'RGB':
...@@ -117,17 +122,18 @@ def _pillow2array(img, flag='color', channel_order='bgr'): ...@@ -117,17 +122,18 @@ def _pillow2array(img, flag='color', channel_order='bgr'):
img_rgba = img.convert('RGBA') img_rgba = img.convert('RGBA')
img = Image.new('RGB', img_rgba.size, (124, 117, 104)) img = Image.new('RGB', img_rgba.size, (124, 117, 104))
img.paste(img_rgba, mask=img_rgba.split()[3]) # 3 is alpha img.paste(img_rgba, mask=img_rgba.split()[3]) # 3 is alpha
if flag == 'color': if flag in ['color', 'color_ignore_orientation']:
array = np.array(img) array = np.array(img)
if channel_order != 'rgb': if channel_order != 'rgb':
array = array[:, :, ::-1] # RGB to BGR array = array[:, :, ::-1] # RGB to BGR
elif flag == 'grayscale': elif flag in ['grayscale', 'grayscale_ignore_orientation']:
img = img.convert('L') img = img.convert('L')
array = np.array(img) array = np.array(img)
else: else:
raise ValueError( raise ValueError(
'flag must be "color", "grayscale" or "unchanged", ' 'flag must be "color", "grayscale", "unchanged", '
f'but got {flag}') f'"color_ignore_orientation" or "grayscale_ignore_orientation"'
f' but got {flag}')
return array return array
...@@ -139,8 +145,13 @@ def imread(img_or_path, flag='color', channel_order='bgr', backend=None): ...@@ -139,8 +145,13 @@ def imread(img_or_path, flag='color', channel_order='bgr', backend=None):
pathlib.Path. If it is a numpy array (loaded image), then pathlib.Path. If it is a numpy array (loaded image), then
it will be returned as is. it will be returned as is.
flag (str): Flags specifying the color type of a loaded image, flag (str): Flags specifying the color type of a loaded image,
candidates are `color`, `grayscale` and `unchanged`. candidates are `color`, `grayscale`, `unchanged`,
Note that the `turbojpeg` backend does not support `unchanged`. `color_ignore_orientation` and `grayscale_ignore_orientation`.
By default, `cv2` and `pillow` backend would rotate the image
according to its EXIF info unless called with `unchanged` or
`*_ignore_orientation` flags. `turbojpeg` and `tifffile` backend
always ignore image's EXIF info regardless of the flag.
The `turbojpeg` backend only supports `color` and `grayscale`.
channel_order (str): Order of channel, candidates are `bgr` and `rgb`. channel_order (str): Order of channel, candidates are `bgr` and `rgb`.
backend (str | None): The image decoding backend type. Options are backend (str | None): The image decoding backend type. Options are
`cv2`, `pillow`, `turbojpeg`, `tifffile`, `None`. `cv2`, `pillow`, `turbojpeg`, `tifffile`, `None`.
......
...@@ -184,12 +184,30 @@ class TestIO: ...@@ -184,12 +184,30 @@ class TestIO:
# consistent exif behaviour # consistent exif behaviour
img_cv2_exif = mmcv.imread(self.exif_img_path) img_cv2_exif = mmcv.imread(self.exif_img_path)
img_pil_exif = mmcv.imread(self.exif_img_path, backend='pillow') img_pil_exif = mmcv.imread(self.exif_img_path, backend='pillow')
assert img_cv2_exif.shape == img_pil_exif.shape assert img_cv2_exif.shape == (400, 300, 3)
assert img_pil_exif.shape == (400, 300, 3)
img_cv2_exif_unchanged = mmcv.imread( img_cv2_exif_unchanged = mmcv.imread(
self.exif_img_path, flag='unchanged') self.exif_img_path, flag='unchanged')
img_pil_exif_unchanged = mmcv.imread( img_pil_exif_unchanged = mmcv.imread(
self.exif_img_path, backend='pillow', flag='unchanged') self.exif_img_path, backend='pillow', flag='unchanged')
assert img_cv2_exif_unchanged.shape == img_pil_exif_unchanged.shape assert img_cv2_exif_unchanged.shape == (300, 400, 3)
assert img_pil_exif_unchanged.shape == (300, 400, 3)
img_cv2_color_ignore_exif = mmcv.imread(
self.exif_img_path, flag='color_ignore_orientation')
img_pil_color_ignore_exif = mmcv.imread(
self.exif_img_path,
backend='pillow',
flag='color_ignore_orientation')
assert img_cv2_color_ignore_exif.shape == (300, 400, 3)
assert img_pil_color_ignore_exif.shape == (300, 400, 3)
img_cv2_grayscale_ignore_exif = mmcv.imread(
self.exif_img_path, flag='grayscale_ignore_orientation')
img_pil_grayscale_ignore_exif = mmcv.imread(
self.exif_img_path,
backend='pillow',
flag='grayscale_ignore_orientation')
assert img_cv2_grayscale_ignore_exif.shape == (300, 400)
assert img_pil_grayscale_ignore_exif.shape == (300, 400)
def test_imfrombytes(self): def test_imfrombytes(self):
# backend cv2, channel order: bgr # backend cv2, channel order: bgr
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment