From e18de580244b5ff602f4e3c3b4f2413620dbad30 Mon Sep 17 00:00:00 2001 From: Rebecca Breu Date: Sun, 6 Jun 2021 20:24:34 +0200 Subject: [PATCH] Image loading now respects exif image rotation --- beeref/fileio/image.py | 54 ++++++++++++++++++++++++-- setup.py | 1 + tests/assets/test3x3.jpg | Bin 0 -> 888 bytes tests/assets/test3x3.png | Bin 158 -> 3218 bytes tests/assets/test3x3_orientation1.jpg | Bin 0 -> 960 bytes tests/assets/test3x3_orientation2.jpg | Bin 0 -> 959 bytes tests/assets/test3x3_orientation3.jpg | Bin 0 -> 965 bytes tests/assets/test3x3_orientation4.jpg | Bin 0 -> 964 bytes tests/assets/test3x3_orientation5.jpg | Bin 0 -> 958 bytes tests/assets/test3x3_orientation6.jpg | Bin 0 -> 958 bytes tests/assets/test3x3_orientation7.jpg | Bin 0 -> 966 bytes tests/assets/test3x3_orientation8.jpg | Bin 0 -> 963 bytes tests/fileio/test_image.py | 48 ++++++++++++++++++++++- 13 files changed, 97 insertions(+), 6 deletions(-) create mode 100644 tests/assets/test3x3.jpg create mode 100644 tests/assets/test3x3_orientation1.jpg create mode 100644 tests/assets/test3x3_orientation2.jpg create mode 100644 tests/assets/test3x3_orientation3.jpg create mode 100644 tests/assets/test3x3_orientation4.jpg create mode 100644 tests/assets/test3x3_orientation5.jpg create mode 100644 tests/assets/test3x3_orientation6.jpg create mode 100644 tests/assets/test3x3_orientation7.jpg create mode 100644 tests/assets/test3x3_orientation8.jpg diff --git a/beeref/fileio/image.py b/beeref/fileio/image.py index 6afb181..a43ebed 100644 --- a/beeref/fileio/image.py +++ b/beeref/fileio/image.py @@ -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()) diff --git a/setup.py b/setup.py index 3ee19fd..18887d5 100644 --- a/setup.py +++ b/setup.py @@ -11,6 +11,7 @@ setup( install_requires=[ 'pyQt6>=6.1', 'rectangle-packer>=2.0.1', + 'exif', ], packages=['beeref'], entry_points={ diff --git a/tests/assets/test3x3.jpg b/tests/assets/test3x3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..58a688eefd20945a1545bccf9f8a1c9b254a3a31 GIT binary patch literal 888 zcmex=8LvnOK-vSy@`H|qMvW5}awt1(JSZA;@q>zSQc)8pmzcPOq?D?fx`w8fiK&^ng{76V zi>sTvho@I?NN8AiL}XNQN@`kqMrKxVNoiSmMP*fUOKV$uM`zch$y26In?7UatVN5L zEM2yI#mZHiHgDOwZTpU$yAB;ba`f2o6DLnyx_ss8wd*%--g@}x@sp>|p1*kc>f@)+ zU%r0({^RE_kiQrinBhSN@fe!FfPuis#KOYN!VdBmBU3pLGYhh?DjKp0IR>&P778mF zHFAhJO3;@}>Fsqqb?2w=GF{)aPxRF{<>T_3w;#CQ`D@#(eOf=FAKVu` z`|z&%k$Rzj@)g^UMEN)T(Jx$k_Tm24{|vle?uK1|_@Ck6`L>t7nQe{Q^nAU>iWUDEnj@dTE≻PB5`TyvauY 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 delta 95 zcmbOvIgfFIvKj+tfk$L91B0G22s2hJwJ&2}U|=ut^mS!_$}Gw&$zs>fyl=7bc_;cN>=AqHl!I7kK!{C~h8 z;N|A&#=ywP#83u=4F7L4I0Jc<0gxe-nE=%N{{Vv^&|3`5%#4BzOoEKef{g!|zyM*a1L?6mQqXwbzED#l4gO`Kd};u4Zls%q*Qnp!5NX66=_R?aT2ZtfnQUcn)u zVc`*xQOPN(Y3Ui6S;Zx#W#tu>Rn0A}ZS5VMU6UqHnL2IyjG40*Enc#8+42=DS8dw7 zW$U)>J9h3mboj{8W5-XNJay^vm8;jT-?(|};iJb-o<4j2;^nK4pFV&2`tAFVpT9u< zVq{>32N}d?X#N5Q0wWU(3o{El$X|?1uS~;l_iU z%Emz-M3agxa*3&!JXHM%@*3D@#CfcVET6$WhVa)d1|DWcVB|3iGT1ZxRrzu8;rjOZ zd^V;3891i5*YVVypT5gZv80c zv%#^)A7{)7Q#UwQoKt7EHEPrI^%^Ty{AXy6eEzol@6tNK#0v2y8~tBN6}u0}NIM@q z&-7zD_n|%N{U<-!$sKqeQ~%&N?u=zm1;RhK> SpRGO|e7NC8s#mN1|C<0pm{u78 literal 0 HcmV?d00001 diff --git a/tests/assets/test3x3_orientation2.jpg b/tests/assets/test3x3_orientation2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7cb2089f3b2ba7edd9abb9f31c4567debb2dbc38 GIT binary patch literal 959 zcmex=bc_;cN>=AqHl!I1`XW1OFc| z2za@9x-l>^GBK0^A;bUM49-9vWdLLdWhMZ1|3APW2=o>MGc%(g1Ct;lvmoRDBMkCD zAF@KshWUk&iJ66!jh%y&iyNq5s{jKNBQrA-3o|P#3ot+!Yk~3%EP|{;iiVDC!hwnG zN`)dujT0AgC_8ODC>nI}gNkudQ4=SZn7D+bl&YG#hNhN@shPQjrIoXbtDC!rr&n-D zXjpheWK?oWYFc_mW>#@YX<2ziWmR)aYg>CqXV;|3Q>IRvK4a#rMT?g#UABD1%2k^- zZ`rzS`;MKv4jn#n^w{weCr@3veC6u3>o;!Rdidz^lc&#~zj*oT3M&~ka)>xhT)6Qd zr?PR-2hpUWi(FzVCJ$9Vg1iRy8F3zKBFkrRk0JbZi-Cuk5g2*Qf(-Tye@j04AMn3< zQ_se_bamaC`hecoLs@x%SdcPrQRUHjB3 zn2*j=zPv~H;mp#q59bf_w_BC1%>3}5;lcaEzRHK}Ira%xq#vy3`osU=K6{4!?fFje zf;PTuzxaOO7jOL-eRNOLyu^q1d}oE8s<1v9FR(+oZNrT-mv5(8wtQMA^5CE9i67}I zOS_Lh+GufVhtF)y1Br6#f^oYRtO{MJS%13z(&TSFe~mWXm)N*9^T+Y8Z{h`h)IaE# zNxwO-`r-T58qc}uF|m*2o9tvi-j{s-b6xon8~&Nkezqmf|ClfMJo230GHvNFlh==z QJ9BeMc!d~z+5i700D_TBr2qf` literal 0 HcmV?d00001 diff --git a/tests/assets/test3x3_orientation3.jpg b/tests/assets/test3x3_orientation3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fe125cfdf1c1d4809ac9de916d9d57e31a1dcead GIT binary patch literal 965 zcmex=bc_;cN>=AqHl!IEX|8{~s_2 zc)5AHF)%VRF_Zxz!~fe1&Ojbz0AvVdCIEH+KfoXe^cDj%Gov5_lOQ9rAmjfd4Dvu9 zvO>&;`Gt{*nT3^&or9B$8>nEb00R>vGcywlGb<|#FhCe9X@jO*zpr5PhGlv7zx5ABcVAIVhJ zcka`wQU18R%j7<1{h>oYdLOlYkUzZFK62ZS>JQBa-afqFa-U<5{)7CMf1Dq}n`&(T zGq}lb=6;d;rSxO?3+XRKkMEg0=(#U?WafLW^lXFG6DuY@n$G&<t=)A-d+ZjcC-vb%b*bXDj|`y=~i)VtL$Zs)4i+*AAD&HNXC zA54DukNu-}cZG24hMW6aZ8(oqoLSvsA#bwwP>te*HP1UX$y~QOmawC|t*Bbc_;cN>=AqHl!I7k)^{C~h8 z;N|A&#=ywP#83u=4F7L4I0Jc<0gxe-nE=%N{{Vv^&|3`5%#4BzOoEKef{g!|Krb`a0_7Q41X+a?4ISBp0~6Vm z3Pp?>CobercG`GQH0a_772~9$CQdFfaS2H&RW)@DO)V2sGjj_|D`yv1H+K(Dui%i- zu<(e;sN|H?wDgS3tm2Z=vhs?`s^*r~w)T$Bu1S-pOr17;#>`oZ7B5-4Z25|nt2S-k zvUS_`9Xod&I(+2lvEwIBp1O4T%GGPvZ`{1~@X_NZPoF)1@$%KjPoKYh{r3IG&tD*a zF)}d2gAC#`G=BjDfsu)Yg_(sNWKS#c@69{;yl(wme1fGL-^|!0}nGJF!GoM8SEK8%Xjbp5Ws(H z@d2srhxgjMe4O9-RrO=~o7=yYYZ9)n|5N%w_*?Cd`RndhJb!%s&H4|;kL!e5zg{q}ke8y*|$53?VNx7P4}SbWfq`$Ks9e})6hP!b)g4ixI0c&Bp-`2efVb4a={PooFDtjd6ui3U7b2JPTt^ApWeyO z=hV)M8&7Zg UqbAHgEw#eOR?4u~w*LQ30Qu5TOaK4? literal 0 HcmV?d00001 diff --git a/tests/assets/test3x3_orientation5.jpg b/tests/assets/test3x3_orientation5.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8156dabc6ed7e32c34e177000d872efc300b6636 GIT binary patch literal 958 zcmex=bc_;cN>=AqHl!I4h7u1OFc| z2za@9x-l>^GBK0^A;bUM49-9vWdLLdWhMZ1|3APW2=o>MGc%(g1Ct;lvmoRDBMkCD zAF@KshWUk&iJ66!jh%y&iyNq5s{jKNBQrA-3o|P#3ot+!Yk~3%EP|{;iiVDC!hwnG zN`)dujT0AgC_8ODC>nI}gNkudQ4=SZn7D+bl&YG#hNhN@shPQjrIoXbtDC!rr&n-D zXjpheWK?oWYFc_mW>#@YX<2ziWmR)aYg>CqXV;|3Q>IRvK4a#rMT?g#UABD1%2k^- zZ`rzS`;MKv4jn#n^w{weCr@3veC6u3>o;!Rdidz^lc&#~zj*oT3M&~ka)>xhT)6Qd zr?PR-2hpUWi(FzVCJ$9Vg1iRy8F3zKBFkrRk0JbZi-Cuk5g2*Qf(-Tye^owQKh`f> zWAUT`G@OU|4GmG`jP#i_~GC6ZFRym${*%8*@^zpKX{M#LjB2j`MP=W z-uEOvhIbUbnSDIZ;PAtZmGvBjYDN(qDLbd>%g+DU&sy+aywI(%?~Jj{a>@Q<&m^A< zJ)gO)ct%oT#H_8^$%S8ES*of^{AXy6-+JHX&=1$&UjJsy6aV1O^uyA*xq|uVoqEm> z>%=>2xIZNKe_q4SpHgY(-*EENn|O)B{|t;D?IoTUoGuW_NHD1A`E7URV~K%9jTxWl NHECX_srLVG0stcULze&m literal 0 HcmV?d00001 diff --git a/tests/assets/test3x3_orientation6.jpg b/tests/assets/test3x3_orientation6.jpg new file mode 100644 index 0000000000000000000000000000000000000000..20b87aa4a6e4d6960470f0bd9324a5c8655e24b5 GIT binary patch literal 958 zcmex=bc_;cN>=AqHl!I2({e1OFc| z2za@9x-l>^GBK0^A;bUM49-9vWdLLdWhMZ1|3APW2=o>MGc%(g1Ct;lvmoRDBMkCD zAF@KshWUk&iJ66!jh%y&iyNq5s{jKNBQrA-3o|P#3ot+!Yk~3%EP|{;iiVDC!hwnG zN`)dujT0AgC_8ODC>nI}gNkudQ4=SZn7D+bl&YG#hNhN@shPQjrIoXbtDC!rr&n-D zXjpheWK?oWYFc_mW>#@YX<2ziWmR)aYg>CqXV;|3Q>IRvK4a#rMT?g#UABD1%2k^- zZ`rzS`;MKv4jn#n^w{weCr@3veC6u3>o;!Rdidz^lc&#~zj*oT3M&~ka)>xhT)6Qd zr?PR-2hpUWi(FzVCJ$9Vg1iRy8F3zKBFkrRk0JbZi-Cuk5g2*Qf(-Tye@lLFKI(4y zJ9mFtZq)0;`}wx+4L-JiGy9wCAMN9>*aUu9{;ug6=11vWYfHN43;%Er zpMKbm^YR||M`wfmeiVN=d{}o$jmZz|5C1M7w&&a@SmA!)KLghv@dxkOUfAEBr&P;X z*SU3%@5gBF?hpKj_Z*t%_~?Gu9@!~Z&l=^w^+}U%+o60!PxZk+kyFcOq?vAv_!Fx$ zxvj`%x!R%7V@ZV)(_0P%gucGIVrfwPr~AL%>ZZ&yOx^FZUgG7|V?XMTSnMhP<9z44 z@%bH(56ZLDI6r#x+Uk)%*AMxQ;yu^xAKO`fc;E43yZsqsp8Lkf9<6%xdCqIuxH(zJ Qxt&TjOfmSPS^xhg0NZg-?*IS* literal 0 HcmV?d00001 diff --git a/tests/assets/test3x3_orientation7.jpg b/tests/assets/test3x3_orientation7.jpg new file mode 100644 index 0000000000000000000000000000000000000000..66e4bbdb92326a0e32f0ecd6f83766c06ed47c70 GIT binary patch literal 966 zcmex=bc_;cN>=AqHl!I6II;1OFc| z2za@9x-l>^GBK0^A;bUM49-9vWdLLdWhMZ1|3APW2=o>MGc%(g1Ct;lvmoRDBMkCD zAF@KshWUk&iJ66!jh%y&iyNq5s{jKNBQrA-3o|P#3ot+!Yk~3%EP|{;iiVDC!hwnG zN`)dujT0AgC_8ODC>nI}gNkudQ4=SZn7D+bl&YG#hNhN@shPQjrIoXbtDC!rr&n-D zXjpheWK?oWYFc_mW>#@YX<2ziWmR)aYg>CqXV;|3Q>IRvK4a#rMT?g#UABD1%2k^- zZ`rzS`;MKv4jn#n^w{weCr@3veC6u3>o;!Rdidz^lc&#~zj*oT3M&~ka)>xhT)6Qd zr?PR-2hpUWi(FzVCJ$9Vg1iRy8F3zKBFkrRk0JbZi-Cuk5g2*Qf(-TyKcfF}9siyE zr{eFN{rUbq^_>4Cj(^boBe|?1{Q-Zsj`*YeH#2{0)RcV;UAOe3_qT!{?jM7X+^VS; z_-FY?n#Vy7h<%+%*D_~tt~;bme$NYaKKTt=(kYFkZB|1tgI{Fm)tmOb=e+bc_;cN>=AqHl!I0ukK1OFc| z2za@9x-l>^GBK0^A;bUM49-9vWdLLdWhMZ1|3APW2=o>MGc%(g1Ct;lvmoRDBMkCD zAF@KshWUk&iJ66!jh%y&iyNq5s{jKNBQrA-3o|P#3ot+!Yk~3%EP|{;iiVDC!hwnG zN`)dujT0AgC_8ODC>nI}gNkudQ4=SZn7D+bl&YG#hNhN@shPQjrIoXbtDC!rr&n-D zXjpheWK?oWYFc_mW>#@YX<2ziWmR)aYg>CqXV;|3Q>IRvK4a#rMT?g#UABD1%2k^- zZ`rzS`;MKv4jn#n^w{weCr@3veC6u3>o;!Rdidz^lc&#~zj*oT3M&~ka)>xhT)6Qd zr?PR-2hpUWi(FzVCJ$9Vg1iRy8F3zKBFkrRk0JbZi-Cuk5g2*Qf(-TypXGmOuphk} zeL#Mzd(RhN`-k`6+J*$6G$t={>4B%s=(% z)D1g$RCC^UZa#D0`nWrH`(m$U_J&8pLLR*`soyz&lkp4YFLu8i>(1scyxtS1^F#6Q zANNP%(iQeoA6s;rpZ?sZ_u$Wd!C<{JpJOKLEu8*gJIhCYzJh6v)1Uryi=6)ShRo{r WPd4jy4n8!Pl9~8n%42E!|2F~cX;-fR literal 0 HcmV?d00001 diff --git a/tests/fileio/test_image.py b/tests/fileio/test_image.py index 0210d7a..2b5f84e 100644 --- a/tests/fileio/test_image.py +++ b/tests/fileio/test_image.py @@ -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):