Image loading now respects exif image rotation

This commit is contained in:
Rebecca Breu 2021-06-06 20:24:34 +02:00
parent ace9238329
commit e18de58024
13 changed files with 97 additions and 6 deletions

View file

@ -21,17 +21,63 @@ from urllib import request
from PyQt6 import QtGui
import exif
logger = logging.getLogger(__name__)
def exif_rotated_image(path=None):
"""Returns a QImage that is transformed according to the source's
orientation EXIF data.
"""
img = QtGui.QImage(path)
if img.isNull():
return img
with open(path, 'rb') as f:
exifimg = exif.Image(f)
if 'orientation' in exifimg.list_all():
orientation = exifimg.orientation
else:
return img
transform = QtGui.QTransform()
if orientation == exif.Orientation.TOP_RIGHT:
return img.mirrored(horizontal=True, vertical=False)
if orientation == exif.Orientation.BOTTOM_RIGHT:
transform.rotate(180)
return img.transformed(transform)
if orientation == exif.Orientation.BOTTOM_LEFT:
return img.mirrored(horizontal=False, vertical=True)
if orientation == exif.Orientation.LEFT_TOP:
transform.rotate(90)
return img.transformed(transform).mirrored(
horizontal=True, vertical=False)
if orientation == exif.Orientation.RIGHT_TOP:
transform.rotate(90)
return img.transformed(transform)
if orientation == exif.Orientation.RIGHT_BOTTOM:
transform.rotate(270)
return img.transformed(transform).mirrored(
horizontal=True, vertical=False)
if orientation == exif.Orientation.LEFT_BOTTOM:
transform.rotate(270)
return img.transformed(transform)
return img
def load_image(path):
if isinstance(path, str):
return (QtGui.QImage(path), path)
return (exif_rotated_image(path), path)
if path.isLocalFile():
return (QtGui.QImage(path.path()), path.path())
return (exif_rotated_image(path.path()), path.path())
img = QtGui.QImage()
img = exif_rotated_image()
try:
imgdata = request.urlopen(path.url()).read()
except URLError as e:
@ -42,5 +88,5 @@ def load_image(path):
with open(fname, 'wb') as f:
f.write(imgdata)
logger.debug(f'Temporarily saved in: {fname}')
img = QtGui.QImage(fname)
img = exif_rotated_image(fname)
return (img, path.url())

View file

@ -11,6 +11,7 @@ setup(
install_requires=[
'pyQt6>=6.1',
'rectangle-packer>=2.0.1',
'exif',
],
packages=['beeref'],
entry_points={

BIN
tests/assets/test3x3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 888 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 B

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 960 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 959 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 965 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 964 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 958 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 958 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 966 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 963 B

View file

@ -1,8 +1,52 @@
import math
import os.path
import httpretty
import pytest
from PyQt6 import QtCore
from PyQt6 import QtCore, QtGui
from beeref.fileio.image import load_image
from beeref.fileio.image import exif_rotated_image, load_image
def test_exif_rotated_image_without_path(qapp):
img = exif_rotated_image()
assert img.isNull() is True
def test_exif_rotated_image_not_a_file(qapp):
img = exif_rotated_image('foo')
assert img.isNull() is True
@pytest.mark.parametrize('path,expected',
[('test3x3.png', 'test3x3.png'),
('test3x3_orientation1.jpg', 'test3x3.jpg'),
('test3x3_orientation2.jpg', 'test3x3.jpg'),
('test3x3_orientation3.jpg', 'test3x3.jpg'),
('test3x3_orientation4.jpg', 'test3x3.jpg'),
('test3x3_orientation5.jpg', 'test3x3.jpg'),
('test3x3_orientation6.jpg', 'test3x3.jpg'),
('test3x3_orientation7.jpg', 'test3x3.jpg'),
('test3x3_orientation8.jpg', 'test3x3.jpg')])
def test_exif_rotated_image(path, expected, qapp):
def get_fname(p):
root = os.path.dirname(__file__)
return os.path.join(root, '..', 'assets', p)
img = exif_rotated_image(get_fname(path))
assert img.isNull() is False
expected = QtGui.QImage(get_fname(expected))
assert expected.isNull() is False
# The JPEG format isn't pixel perfect, so we have to check whether
# pixels are approximately the same:
for x in range(3):
for y in range(3):
col_img = img.pixelColor(x, y).getRgb()
col_expected = expected.pixelColor(x, y).getRgb()
diff = [(col_img[i] - col_expected[i])**2 for i in range(4)]
assert math.sqrt(sum(diff)) < 3
def test_load_image_loads_from_filename(view, imgfilename3x3):