From eea3f7ad6c95e285f219563c1e0b3367f8fc25f8 Mon Sep 17 00:00:00 2001 From: Andy Roberts Date: Fri, 27 Jan 2017 17:19:21 +0000 Subject: [PATCH] Add support for transparent PNGs --- stegano/lsb/lsb.py | 22 +++++++++++++++++----- tests/sample-files/transparent.png | Bin 0 -> 6895 bytes tests/test_lsb.py | 11 +++++++++++ 3 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 tests/sample-files/transparent.png diff --git a/stegano/lsb/lsb.py b/stegano/lsb/lsb.py index 917e5ce..95215d7 100755 --- a/stegano/lsb/lsb.py +++ b/stegano/lsb/lsb.py @@ -44,7 +44,8 @@ def hide(input_image_file, message, auto_convert_rgb=False): assert message_length != 0, "message length is zero" img = Image.open(input_image_file) - if img.mode != 'RGB': + + if img.mode not in ['RGB', 'RGBA']: if not auto_convert_rgb: print('The mode of the image is not RGB. Mode is {}'.\ format(img.mode)) @@ -71,7 +72,10 @@ def hide(input_image_file, message, auto_convert_rgb=False): if index + 3 <= len_message_bits : # Get the colour component. - (r, g, b) = img.getpixel((col, row)) + pixel = img.getpixel((col, row)) + r = pixel[0] + g = pixel[1] + b = pixel[2] # Change the Least Significant Bit of each colour component. r = tools.setlsb(r, message_bits[index]) @@ -79,7 +83,12 @@ def hide(input_image_file, message, auto_convert_rgb=False): b = tools.setlsb(b, message_bits[index+2]) # Save the new pixel - encoded.putpixel((col, row), (r, g , b)) + if img.mode == 'RGBA': + updated_pixel = (r, g, b, pixel[3]) + else: + updated_pixel = (r, g, b) + + encoded.putpixel((col, row), updated_pixel) index += 3 else: @@ -102,8 +111,11 @@ def reveal(input_image_file): for row in range(height): for col in range(width): - # color = [r, g, b] - for color in img.getpixel((col, row)): + # pixel = [r, g, b] or [r,g,b,a] + pixel = img.getpixel((col, row)) + if img.mode == 'RGBA': + pixel = pixel[:3] # ignore the alpha + for color in pixel: buff += (color&1)<<(7-count) count += 1 if count == 8: diff --git a/tests/sample-files/transparent.png b/tests/sample-files/transparent.png new file mode 100644 index 0000000000000000000000000000000000000000..d2fb11d9b620e663f2c6171fa0bdc31b819b35f5 GIT binary patch literal 6895 zcmb_gXIN9swx&uIL@c2<1rZ_i5@{M~QbMoN6IwzNLg*kMO%y~xkR}L7k&b|X(tD65 zO+Y|8C|yEtH~4+$9IyA>f8TzdBr|Kh?^?6oJ(E4N!?m?k&Qmi}6A=-eS5v*GOGHG> zPH4MOo+EsdvUaT!eu!OlRqhfM^{{>?bg0m(Cay$8G>m5-VxpuJ00EclsBerl*3f6^#l=`&M>Ssr>8Rl=?e0cFAUEeP2o^5KZLF!y z2}WUHoDxFfLc-kg)SR50vKXixNcW!d-*iGxj@to?MT3Aq4-XF^4>2JW#vUjlBO?P8 z76pom3K9^4u3kv2wWlD`mFKS@|B7=D=4y*^L}MLMNY1mk);1_NtQ&)~2(qNvB{}1iV^B3)x7=MjXmY^s| z69sj&^SWn^g~^Kwi%1CyiwKHJ>5E8#ghfCi(z3umF#oCW7aNR$Sz}QceH02IukGjw zLpYyxImLt||6=~`|BZT91qgz1gc0OD6H#777WjW`f8xO?XEX-p>UxHk_#6Mb?N7We z%oT-jI}2}sbi~Sw{!RJA{3pxgKYn?se{p^{{z)+YZ-n2CzY)%E1L&7d#`heZVaQ*# zh)NU0`mdfpnP3zGh0#M>+rrKS`$hT9^e6gP9+Us%5fKslJ#%K4O(h4NM~dCX9A8#_~1Y0K1x z#^We5;gPn+chRtA`RH4YAK<4Fy1jV(_`9~G=(>j}3nM=_zwP|B;^iOgZA+Xa_W~#{ z5OXV%JtkuKhdO~g*Bhe5gP^MGoo~lGr9ZJ>HLt3vEVMsfZ@&)g45qvg_?(QDE9))q z+)sV}QIXkn$(J+mjxgF@&xSX$Gu)h6bMqlD1S)T$mr$d=?}c&=7bsQ zevegz@hU}yx$2NAo8HihDgBfvUQ`k4(gtT*G%FOvFcNd4@st|W%>!ez4+UA@-L+TJ zrVpjrTpUhyV`<9bk|8J)eq9k<*zz-LJe-Ju@{W}*-t+DteEeQWPhM+Dk5F9tTtR(d z`*BR7SK?Mo(;a!NgxH=;;=YLj0#U#s+&ozQX6}bA{^S`A(`?z4psjAa?Wls0=wwSZ zVosxmqq_1+-HL6FK{pe-ozdNVJp+>t6veEKvstB2^3Sl_c3o9MWyP6pRJti`wC8G} z%y^l{v~(_ZzA=d}kF#spZx*9juLu>mt&G&oi#m4DJhzuIysBV(7lE^3Dwfh~C zjS;ubk^6>07q80X*W_26vj?xrtu6Xl^RFdbI`rhOAPzP4crC(F-o|kE5Lo~gtWcp8 zFn3iM@UcrVjjwfEj%_X)@61QX7Bzm1smct+~{rq8N=OzeLG#34AQuDzPnjDThp}WLEp+Hs@h!mUbxbW6^1uV&)Zz z>^PjhMSr;nnoHnrb>}HiP|##X&b|nx9*Wl=Zun z5=q$_h-YTbH0QoLZ@YozgU;5onc*?G7E-T3Q{;f@2E;^>vDl?pM7p+McbjLcc zcLN={O51fsj-h9FW|uBCqcG6gVpb1iMLTZ$^6T*rUd5HyIE;_%cWVz*n3HFS^^BMy z(}ASrYY#jL_CWBVe^YTcN00NtAz0%o?W(PCO-QyvmrS>CgyrDy@Hun)aGPKi#lanZ z!NSgX_I=q1t!N0UM=EpCtFW#7Q=c;AU=9t4nb1iwb15~r(kVB*LltMFnh53cj_0!F zt?hfbb*;9EvFJ6FUXkM41p~MC>4~Q6aACLEN9)0F9h<7=uk(&P%}JA62bWQ#FI;*; zb0rP7bl`;buuE_Ryxj6zyFmvCH%f1O+TkS7X@0Z1l*DV^G=?1UXa$A3%`y-2n@*AF zlFq_kl7NcC?ia@#wSEgtuVV7%5T5_slY4p&*ZPzWS!2KCE9C7}e-~IZn5LCl_hVYn ziRVEBn%g?U#vv_TzS#Ob{%DT{U}~7)OlEFmOIgkcejV2By%aNXes?)rpsqeVr%nb(=lGl;el46BQ|exFHyn$0*`7I9%l9q%5TE+& zs&~QYIDWR8qgjCs;tj@0A+d*#qUm;dzgoO7+t1~i&#WVA(<-ZKmaY!Odz5c~Ff!#H zU&I))MroYlfU)yV{a-wIT--%zW7icSi#++Cb%-;)YpE8Kc^r>HhZo0KRkzS)sAKA< zd#+s*h^?g~J$zTc9|l)P?-4Mq#+n%&MuMdom7k=tw_TDh0A8QM`{cty7BxnuH1s$Cxv!jS;qJ)~w^k&&QjS04^ToX58GN#A zksNR?y%tTmRB0Awsd29EMj@v)Y`%r2hKeo``Rvd&kP%H3O8Xqo&1IaNL9l*mNGkj)b5&ldr6tnfoJ zm^e8Z)N=*_9Y;f?Jc^>6u!AR;dKDMl#;v5RiWKEwd@BxcvoBBO_yA9E651rY*{6yq zC@-?)^3rUvaRo-55!vK}v=CWOG(P`afv;X2JxkLSzDEa0FPNFhx_*K6C)KnnS?1E} ztfim+hu0^@{7tMTv=j?v+{~$fcdu_`k~!qY1*kX9=ec_y+@5T}ble=r+9xUHhC9e^ zOIgoyF1YXA)O#r2vs$}WLC1APF-&l}GpHzQ2pga`ZjwmWlW0>DMP$UFZSu%=M7#3$w|J<(8J(57!^veE&GPe_7rP@ z_?$ql(*0mA$VN}zVa66|cX#h$XgDKImAab3EZC2NcGI-2AGJ%xEiKyb=^t!G{UnHp zDOK94^`cve{giM1=f;NYuhDHl=|P?PAadC)Tb%RJY(!6)Z$Qe>x)x$1Ipln}#M0_G zHP77gc{Fb5{$K(vy}P_^eYk%C!9^Tf$%vz*4}|C#aTpKozuBpq!oe)bFxP^K&R4Xt z*CN*Tgv`=gh@IoD4jnF zYbanuYJRMs5|uH+9HqXhl<&{Y1Ot^RY|{DpQLAZw+1HbhkT_YTs4HP;r>yTh{aU}= zaM7%<%KNh(g1)Oy)xj$e#%9hzs54OtIdZ%txJQpGvCnqeEN!>18}>ryLOU|kzDSH8 zi^*iQWMyT}Mk01gUf`(fLf35S;u^mk*+@M_Ah zZ0_mD7GV0u==e`4loJN>#EVx{cGh=$YGyRz>0;Ok!bMDdzcbyBbV+j$h3KllXM|1p zM>eKe9hvu626yS$N#OI_iX7YOR-PZamvU7-3e|s9mcz|2nqyb@L>BASdTfWe{7f#wk;%OtwSMc!C@*<0! z$nOn7Hhi=TpG1_{ZLe)7DKEk8uYqeul8l?Blxy2#B4~S!bCp!}jEr*NCK=3-!vK%i z8a3(Vmfdv!#954>L*s4fs*O!8y%~Dh>Gd8BNLt_o&f`uoy$*6}nVILN6z9qTD zB%VfYJ3s+0Nu+Ua?G{+*bzC;D7t;4@Q_r>-Ktpp30+)IE_*J{PVSuJvBzdct{CL@7Y2&@PtMEFfi^j+P15W`}MSc=N31pT~rgC}J= zk<1|TrUnr)vy&zf_GS#Fh8qdgGyCAO;x-=%qhbJUFPUNvcX)$7pJJ$eo+YuqKesS3 znFHxGnIZ(oO>P+#M=7;Kx@t2%QV7%fiu2Ze$NNV7pE||;!)L<5Yl{BXDM_o6jur0J zdFs_+acD4bq3d(&yOzm?1qs2}_f2}`Hc(I9l4+V(2I|tW^ufC3qWm(mtCU|qNu!=e z_$_pFT271~9lcDS5isdElCCc%9Jq9=#0{%InQXEgt#*4|8O?q_dB9h zv30%D%Wikj+|L7~quW;3jFdK z2Q^qfBH4Xj(c?5eCMMs7u^p|N=ck%K;;}NHu^#_xJqZL{ z4GmD!4Iw07mTXWHjeAQqL4t%;0FZ}8sxKJ3&)gs40KSFIikB<`dheB^G==!0HUluLyo;zX99s{0z&S|PrN6%Tu-N=hM8Bj;8| zOPSc>N*_#)R}aevQ=82+DUB+$w=t~~%)W(h+dU`qA-Pu6A0%w|6zzVl#j!3!6*Wd3BTf#55|3EIAmnG9Y43n!v zZsbJu=2IH8SGJ2BpZhY%vh^OE3T5=H%>-9P4V~~T(+5slQc%S);9}y5x;L*m547YY zct;X}xhodG1f};q?3uT~GVOrKzK)HXpBnmBleZ(kTU6#zrp-2NERy)=-dg@LTC9V_ z^K`oee19L=8hgLf2fhOBsG15Q6!}KWOlk1a$LRHzqRG=|fbWzf zpSPZ8ksBxg@K$0O&}utFVWd$5+ji~S_wR=&tfiTsyN4QyWd2)j7&?+)HqDC)(@E611*D-6-I&869wq#z& zrk8;hYp*N_SC*|0o)AKyGpY9&U7wRin{A(YC0@IgF~^Z>(C|oU9BT$6XtufL$21lVP~W@ zAIW!lkZiTPQ9Yf+_0G517zl@Y;tSq=%Dy?w&kD8s4uk!R51GEXoNxdlVVgI6bBl|H z;>=ut{c<*%VQN1#5EEV*K2@hKbBEPX-`h<-r9oZ?d2#-UK^wCLo48kFXCk^EYUb|9 zn2rh7viI%_0|;UI$IMR!jF1K*5uNoRs$o?2l8jz6QkaINV4016ayEXofKXHZQ3Ni% z`~dVidvkeqNLjGkR&2K3R&PcWK@0+*ytf7IghmRv1(p?NAnN=vfdmDi4k4F@)3d%F zD{oZ#*yL-QL5^v@SKC}!oRRE0^tH0wr0yW!x+`p)tUYXC-<76NMtDP-Sy<@CWzkHj zs+4-|dH@9-+dWPh$`>*~e*EL~GB^GWV8%O2aH-Hy@{&EB-3t5hGc4xDebWp7?6dlx fik3i0a!UWWycjdYcK;#~;Zjr5x>t19>e2rIa~@I> literal 0 HcmV?d00001 diff --git a/tests/test_lsb.py b/tests/test_lsb.py index 97a965d..c464724 100644 --- a/tests/test_lsb.py +++ b/tests/test_lsb.py @@ -49,6 +49,17 @@ class TestLSB(unittest.TestCase): self.assertEqual(message, clear_message) + def test_with_transparent_png(self): + messages_to_hide = ["a", "foo", "Hello World!", ":Python:"] + + for message in messages_to_hide: + secret = lsb.hide("./tests/sample-files/transparent.png", message) + secret.save("./image.png") + + clear_message = lsb.reveal("./image.png") + + self.assertEqual(message, clear_message) + def test_with_text_file(self): text_file_to_hide = "./tests/sample-files/lorem_ipsum.txt" with open(text_file_to_hide) as f: