From cc7f2bf1356968ebc875818b1acf7c9144838375 Mon Sep 17 00:00:00 2001 From: Tom White Date: Sun, 24 Sep 2023 11:24:48 +0000 Subject: [PATCH] updates to region_based example Changes to get example running on examples from paper --- examples/elephant_mask.png | Bin 0 -> 2902 bytes examples/pile_mask.png | Bin 0 -> 5613 bytes examples/pile_top_mask.png | Bin 0 -> 3492 bytes region_based.py | 29 ++++++++++++++++------------- 4 files changed, 16 insertions(+), 13 deletions(-) create mode 100755 examples/elephant_mask.png create mode 100755 examples/pile_mask.png create mode 100755 examples/pile_top_mask.png mode change 100644 => 100755 region_based.py diff --git a/examples/elephant_mask.png b/examples/elephant_mask.png new file mode 100755 index 0000000000000000000000000000000000000000..50f25b2c1d8553cf7be785390536ee8a264d7fda GIT binary patch literal 2902 zcmbtW3se(l7M__9Oh6u@1_XgID33yt$q*n3MuUI`H4=f<;QGjrnUK`HNP+~kFi;Ox zmjzs3si=)Ba$46z@v&uBsl=le>4HMlN>#*CvAC^oSFQB16JD!(y4|zgoILK#eD}Ne zyZ8S0WV2$)0w1qnF8~01GGu94002=Y1c1~xaD#StIrYOd%W|y%z>RQ!K>NYD>i~d$ zQkSE&DdmgBYLkJB;if!-Yd4tPUcl_Rb~C14OW5E%B41~eupgXkWy3mL!d{*rN95*I zLaURNScvSBB{}MnwQ3R0o;wGgZ5LAn2EvBHc7xt%72753VO}w1yKnQ@@G!)-R>Dqk z2ZWV!1)OTK5U_~L=co~c4<}KUAQ2UcL`g|WaWIOY@jN7vhbC~)cyU6Km@j}wUu-I( z1=onP(&mrGqpl=ut<7c@^LRx?MckrzuE~3L2)h}vJX4`f!ltqvkzp{8(;BU#rJ{<)vtwo+pNqI<4FhrYIL=&X(GLg5 z)jUE^7zm@yN@4lqShLn-Gg-B!|3E$d__Y93dFAr)7(bhf!7v`eYD-^7$rvrj&!Vk4 z#b$z+MOaOR7B!K+j!HAat;Q@)wGf!iWXUm^^kdCZjHL{t30yuL?d}aCL=(9NE@cQ{ zAu7Q0;uB(EI9iUWbw;omo$ zL(%vgev%jwijjoZpwvv^m<{`{U|cQMm@Ed2s)o*h=2~>k|@B~VLAf6yNLJY+@xQ4IBHAH-#NG%vv zLHVbd)P?T0kNM;OqkpDFM;$#(|EqbZ*(Q`hvko2dfgni%;7XK zemp-(sNo0`^VA%Hh!UKKBZ(Y7&d)>9ym(BLB$&-}pA^ao950!Mc1s8ULcf59oyniULv!cH)UH`+86ljjt!E8#fk>7kMp{ZI z*T(or8{1;ZyPaT3Se^HsN9oe1)sp}qus5r!wOb*jrR~ywO`AgYd}nwo`E5`CWXT*U zB-OL_0f#5;@Hj`izyEKISU)hkW=?odsPx8#k7>cRu2PT8CxiFqBs+U)ewW~nH(fE7 zEe?m8Jvt`Z7G87kG z;??)oKLWK9ptN-Q0{adl8MYOpkr$wsbgh5r+kv%@TxX$Uf!bVW&4fMA1wQ9L?SK4X zBt1w%E(RMy{Z}(jBiHBO-|*XcHdft1WE)^7!*Vl}7eBLwId>jiNc*-S=HgwVDdyn7 zs$Dp^gR!_qTE~1^6G8_}&!hoH{Ewm4|H65-@b}F4j`Q;4d=Lb&788RmZzUwO`zhe4Em0t=2^{xm_ zy>(%}XV*~@FR!Fq+8%BgVu#d5`$V(4;uI~u*ZQS?&Ir7uFSQ=|AoUCo@Xc?o#W~NET#BH-2&_W`W~y>k7r@ffPxKCmZ0#po zAK99}S$1V>`VVUQjYg60)`1t3Pc6tGbL(kO!rtkqUs2vZ6m&lKcr4OHK3}@q!&S2H z_!i+JeZkWk{ZYV9`r-mrX_@K`->L_G6Z=BmDG7Kqi--r8U9msxQ9LG`UO z`t8sOAM=au?tjc{yVTYrlpchZZEU>00E+Y*tdMT=>WD+=&wFJm99+s!>=!Te)EBr` z`U|Gi2Sa+B*3AAqUmDo6C%qZ6 zCaR`6e*#_}Yp^pMc2$WF(ndQF=m?UQF;!I)JRekU1w0ZXSwRfxO|lv~n@)Cum#31y z17EHMp098XKs&)}KIBRlBHiVAE5Ws48nPXV^c-wP^*88{*E#F5Mcpo;+_Xaz? zU~k2He#*=R8*@F?nRSN(9f7XBs~x`3T8a2-A+D-*8SPyS#@exTidgerr;X#$HU=++x~Q_^NaP7%x8YFukE@|PPUb~ XX2pJl_%74C|M_O5FG*{hr>giTXeAi~ literal 0 HcmV?d00001 diff --git a/examples/pile_mask.png b/examples/pile_mask.png new file mode 100755 index 0000000000000000000000000000000000000000..fce149ae847304ec8de23ed499e8bb8794494a9a GIT binary patch literal 5613 zcmcguXH-^bc- zh5#VwRU}|S(u=Fj(l_V@%0d02Isk8?Shj4?05G4scKWowm!X!1$T`i^Cu9{*oRE?` zDhq)2pQ%QE<}s_hsspQ6*-SM$=ikJ;+ytUxM5eFom!7jf1~#|ZIdPxE?=qWm^KhhS zn^fIuW7bZ+J4`r4a!|i_*W$j(WraE6-pu#Gft1tqex+Xb+-9*vD%>I-oxTFpmuI5An`d+k#%Z(>LPLc62{y^$;zr zSYO->h&%#=qMQ>fP_!ZVderzUH~6|6!W&#f(ka$RIKLS?*@Fq76B@yJ0fI!5F9z~J!Aox9qj zl662t0Rc|$<*G-D=pYF)Efs^=)*j&`Z3vKSW9#EDv>f9m#Wo4}EzS=P?;HN;@hpKK3R-QdjhG`unPcz_2x>df?D*%_~_8M{e%l;>GN}{^SryL>b`{mYUu+6 z3b5-oQo60j#@dk{)lj}qg>$wg=r#q81M61DdN2eF=BB{)`vD4DxV>r5^uH<#?O|qN znLS|ptW0L)zAEk5ko(~MkQKU?+7mbrt|#FVbIIvp+t+Q}eDe$=Cu&g|vTmyrV$|yJ z(Cx^>&BMpvT$bdyw_yG!NniA0b-)M1M`!kZiyh{i43%3Dj@!n>_+bGtLXWIl$oRh2 zg&Z;mA?~x9xA+2F$WN&U0xXXJyTft0fK>ouD%PWL5)i15&lmxq^vCYYSx@+MzAyoB zhH&#pzB=QsN^T;VHKkIhl+1^)VK{@^wV$lauE8)0?_qwnJ5HV7n5@)^dY_LJAaEoN zu@_XzDqtrDIWOOw_TXc{?@s%;yEFoWQvb@tWwTG|E{APor)UEcUt%Om)JFUGV>m-( z@-DZ5=;yDV8Hj8Rr$<)WVvACq+=>q`<2IsuzKY1*+qze(CHv&&TLWY(!Uy*uOIYCE z7lK!__i;8WIPSS~vp~UK6}a$uX7hy~KHebsUZfq|EX!*Dpv&g+gFBk|Kg|Wit9k9l z%;im^`8My!Cdn8^$ic3bx2 zLD85jy@CUQoa1fw){i958VWLRDY7fWo<}vDu}ZO~u_3V&ImRYlCTeW~IV{GnOxRvsF%5os*5sZ3 zNH(UG_f=cAkV#4QiM$3wKf@^fMg!~ga_j9|8+O>Y5#eVE5!&g-c12(KAMWG*B6oGc zT%+C76ge&FoN&?B^6mPJjxTys&{sN0$LO?Ijy=)-k$vj;^?Hc)%bUS@#jcd|4f`6f z4M9>!DK)7#?M}J&xgohHy92w+yKQGZXL)C*yD2^%skl_h)MX`CrBxqH@rh!C;_%`g zpT;TG?{?DxlbGe2W!A~QX02t{rSxUKDM~w~S@@*Pai$Z|{4TNzcJp=7c0ZgGu92GO zZx3a0)H)VFs&Y(kK9_!wBtCb?VMN}aPt~62zQ>k$y#H#iv$BotHMyRQs*KO0<%jE^ zI49W`Hy3>OuB-bh*-Pvsew&F@Y)f%YvnlT??b&)5{z5b5^ZENH?pMck$K9w!$6btD zB_^1vng*CIn|2V-wO^i=oOW$DbI}(xOWu{Nlw4bwm7APf<(up}z&*?zb-3m5vXNW2 z?9A2-^GwnV!>rmi?Wep?-SG7Gsq=O5D4ot2I{qoUD*Mxb@sM%L`F0+4X}r}zg*XutSe-?JE-Mymn3|RKap=enEUV%xtya@1_egm*S%LI z+lix>HXX_$7oxHsd^pwPROspJKy-33bg`RBCc&f}(oO$6Ynlu3oZejfF|&V;R&`dX z{5j%$Lv5O$@+J98(Ki&r6FB4Fs*Qz-Xy{At)*H)o zyC}r9csn^80=`kxND7I%$=ImQL%NepFKP- z;X2Lf1%`S0th_qXSrY!1yDj}m+GW}nt;YuJsZ;A>EVp=Xltt(3lk(qnlKd|%42o80 zmhg(*H{$aO+8ZHMt;_^u8`QX`0Pab zN?>ZJRjuiE?yI8mF7fX6iax)fpR8d*Jg538Pi9KrOZb~PmuUtsyS2>S>WdPv6)dh8 z59uRm`RRR5iwb%X#JHkKnRfDacjIQgzi-S`&K%R&}-#ovjS4${bENrm69H&*>Cd zuYUX4KM`d$U+S?Wx@y~Vpy)!bN}h^Vh{yJj_G?S`>8q=?vu4V9-b)v!Pc>7%hi*8L z+q^o~I*wXB4Jf00nGq~<6YtwYI#9g4=DRY>=f^y!(`ywN<^OuCynB0!af59tw1E2N zD%o{#4Nt~5e!9d)Va41@qduX1zcbXLRj1V^c2?|RRI0GBAXcDk@sEa>q0G0LT#Yp$ zrnG=*uU8WT+ryg|8qK#l=ULY8H&)~YWdHP}yvly9R4rC5T-p$zvhZ>%x1lGPLZTE8 zRF?@S9@@fD^?x>R*i~mX>8+|x+@NkruipN-DW#>*NKZ0F?6eIp0uU$;0DK#O4SE;+ z0{|Zx0H*K&C?x>E=aEuupiEa_^mU9i=_T#g=jH#xj^03e8|oU-XrQA8OHWn-#zXs( z7XU}ac7DkJg_-QIson0Y&t`DvuhZx_G(&;`i|^52CwsuqV4fp5`&_bNdDR6E5B3uC4x zjX`_GVN3e#bQNP7S3-jq;;<;}fnYia@2z2l5RT7t!0%z+1_g{v^mTv45FRF};6U&b zZV~wG5oUJWK{R?tVWFr-UwT8BXo=$1ek|8-)8tRdFF?1VB@8wcEnpy^bP4{weI+Fl zBNO3;*RP>;pP6<+I~qc^kr)nG2_3=xhQKwo3yEs#c!b#MB6(0#4Q+cLUfD zv*hvsPZMsB9x6iD4QjZFRYVpPhB`8f^twCH6suu3-Pd;!Zr#BiZ_&q#HX}K3r0!cCe6Kpe-{0K{fWY)~?v3V#Lc^o#(6NU$}65KI$Zu!jj34!YQh_!a4o z82kej3KA_-9fv^Cg`e&YyuUF1Cvg>8QzhLgrZ0`x1sX!h+>s7znLR|10e4_l9oQol z+6`e7=%C1ai!dU9vzM&@0`l)E;>O&OD4DvczmAVg{V+X9BSa}2_$f&j$XrPr;F6Lg ztW<7OUC2hYAxSy@81DJj97K*8VPoK_vjNnwgLz&Es>8Zc-+2Xa;feBokzm}yaAzuf zK93lol7m=z;K4Fgq!W)=6KXz>MGy?bfD05WfWot6wGOX1U*|YhRvsjv1FrZ7Q+}Tp zk<0f%DO=X}QP4jscQ(*(vmgPq(t{xmDWHw7al#1dJt1ksc(C^VDUnu!^iBp3VC#*p z^A$%3jhnAk&E)D|1!dNwWDrhE7&!AveZyHg7CaWSrMXA#AM>s1rep97#*RE-VwsuD zq>$HF*fV$QtbfSFQW8*J+A5^aG(f+vdYx$2DFtW`nr$keXt^|}J*{6}=pfARq2Cdd zJMFW)JpXc15hBf6T7n`Kp+qaB2eU#3v<2ckfhSPu`bHdDqU@(G(N(l7bq% z+YWW2SDb}l#L6^X6guyP5x&!Oal%=c-Z! zW{)O&enQ9~Oa*zttBWjTCUX}HAbH{_5X0wQhw|G?=|(Tasl&@v&Z;Ao^|Fhf_XIR+ z^-AOWc|E$&rUHS-uw^sbZUcg1bQJO%IxVlwguDB$w(nMg>R^*E4CJRJr}!O|#pfEk z(7ZBG`S`f2tRkp}(GgM2L-7^Wxq^0!DAKN*s}YQ3O9azN6%a(e@Exg7zA%Cu6oPIi z3&SddvFp&G!B{pF0X|vI4y2hxdPko!uAMCB4y-}_n@b>!A$LG}!E}_Amt%py_J8KV z0k3JT4D`IZADw{yh1hne^q+X{uO{g=YEbby8{P9XtyK##c=+Gg{9_!Mu`j?a50p=| zd&QuCk@39ZYFdgk{QARsJl9{$JSPaPJsYf6<$#w%=eVHx9cKK+dA~(3A7WmX%p%N0 z-l$l(3H(QpQ12EZg*Jb4ef=iwqf!wiV*Pdh7OhfaHFJou5KzY|-44NQ0iFV!WItsk zEUkRw$f3pg`ldCp)-@WLvvGh~FLe8*HBCTTmH9fu8hlbX9D^@66Aov_m--2Zv$2sm z26MY0b6UsQireYZA+hn+5xODRgVvjJ?iIU)OVQ>uo^b~JcKmKZc-h*{3ABekdPX-l zvES)T|6F9l%}!B&xpo)$M*r#3CJD~LsgR^+S#5osa0 zNW9ukxJV5>E)&iYg>xM1t^;WE?PD$wm(-RZzm)r!)19d zv)_kGd}!X+G-dX~iy@zSpc{qXb}qoc0tlv#tBbxLBB|}=m>+TwF*J5B9X>fJL_XEW z0)qZCh_82Yvm~*I=**6PwsM|Wp>=AmYZBc;+|L+(6m|M0R?!*Z*NoB>$lyb93r z`&tpMO!|om%5 zv-lyzkMxYcQkY+~aNO`ZGxy&)%fDRN|8ax=Lv#B|FtlY^V){GkF^LTmJ<%h{XT^ literal 0 HcmV?d00001 diff --git a/examples/pile_top_mask.png b/examples/pile_top_mask.png new file mode 100755 index 0000000000000000000000000000000000000000..e1b604df0f74993573ed3e3725c041d15c593dbd GIT binary patch literal 3492 zcmeHJ`8O2)_kNk73|X>GNVaIP3vbCT88sM#v6O5@mO&ZI2o187FtTOMR+<`$lt~5^ zS;o>R63TAU&{$%!e|*0G!1tWbkDv2-&bjBg_qpf!;ohH~J1A=lJ{}1k005sQ;w%~f z$X*Kt+|WI`N>_{8BTgR^D-!@}(s{Q$xB%d>^ffa>g`h1=Wh~9jbTm%u=%{F_Y5;(^ zQ)CzJl*tw|9AjS2O}bSun^(U8aKd$ zC`z6zIZp90xtr{g;&4g#xAbV`tLRA9!oud)%|=EcW2+b6CqOtTq?j>V<_Aur6ZG|l zq|t({Z?L=-X3DI+*bd+)>52EZZ|9}$ClVYJ2ZJZSHb-~rX6LhTc~@eL?NM<9~{ z#^r<c;CjAsUR6tZ{PuN^z_4m`8iU4Zsu9R$$nUf#+}ch%L7&vB zxuh(5cgg7v1tn+Ij(>yBI4j$kJt;JIOLIv&XB(Co&IibeHZ9h#_rIbAw8Z@8fTZv{ z^@joDm=%?K61*8e&__!X&jui~c|F}&i39ZHR!jlV`18=ECs}ahM{WSlmc*WXVY2_g zI}vgVf8jf+#uhllW8Ych14mo*1x@!&6Atr~9?CHhw{OuK;(YZ2Dp7JUe?qYCorW$S zV_fJ`EHfCs&;3yG`$LV1Fiw*WZeb5uz2t+QX+v_o-0-|KPB{;R)_p>S%v`dFtz28j zjS2|K)GxyoA-}A0L*#VoCF2d^NQaE(-L1Qg78+TxwYJbNkWk|{PoyR8ek=*091$lgoT!Q>PFq zwB8!wsaHdEI!9LgEqg?^SmE+9eqpyv#8Ljv`_tAuvI;~^OI}NV#0WyA!mh%wU(mM@ zsmLT^JAX`b*AmDwxf%6D0iw;wme6_at41@Nj~;58=9N6P z?I9I^;eMK|_t3=>TM~v+vr8fgYlY@3^c0Ah(JEBR2R`Nd`uMW1sm;%a%w$6TLt^E&L+ziLY6`8*JK5ai%= za_wZ4XhxS%wq7+{Gku1(1>wVOygvyi9WWbX{`qS@;`9Q+?^#urb2D?HyT+soH`Qw6GGq*62JSLIz5 zQ}xeqS&sxJbEZRS&Dj4uv)7G}tyIG>nAy{WARQ#l+9SR=ZaD z=f2Y|R<}2B9@Dfv* zi5+wdKuJ0l94OE$puK)lRZ!I$R)8H7nG{Js(SKsqE^t_5eruj*K7W4S!s#`SHH9_o zTAx(+VIir(Gq^KRH*lfdVFAI7%!&!E52RMohdDH5KdW%6f{?KFA4d#b3i zpz{3+k)d`u?6ULaP3jSwmxYSOgX-9hk=BdjRN75l zk#pUzxs%5&0+K1xzT$cC^=Oe3CpDj{{<5vJ!(GR*l?Tbw&YM2ew54>?y*Fn@{9fM( z^C9~MpaZ<-3n&E2Q%Y<^(qHqXTp^tBQP{$r>DHlEgF90p5Jo`$slw@e`y8BC^~`A5 z8~E&8FReIS-&xz47^R!=Kq$A?XeM696s2~^W~MUGN=kV7c0q!Tc~eG#~*4rW3 z16La9x0=>d*VEMl)%MU{xY+%gJhEJ>Q@$2ibjy{7*{*t4-}Fs!c(D0<_{|mmcq!2{ zKUi7wjjxm<9Q~>0(W`;|zvI5AOL$5)G|$F-r&xsBv=yh{d~|dFnmLQD ze!b>UvPwC&#GsVQ`gyOsNb0hbMN|E53`=9mE3{oZ-Lbf&=(xes`gC08ROOY*;v#D! zgD-ibcs6n?&)?P7mFi>GS8N2w{YKWiu{&3OFw$KY8-xGIu{}o=>Mv9oR2x{t1aD6u zo?i@NUS-o39QCVlf2^2i=&aATHvC!!Lcad}s_Oa>Pi1|am#hy|{C=3C(6IV9Y;6G^ z&hr~N>Kd6I@qDXkc>CA>4S`>`>UL_bwqVEqy0^IZeQ*|F@xzjecd~XrCr$KQbXyF_ zpOa5bFOrs)J~OG1e2VC#|XL{tuPsDa2=BT lKk5Jd#DDbq|1J~iCCA_o2oH|cwf*nIEX}RYR-3xV{R`#Sz;XZp literal 0 HcmV?d00001 diff --git a/region_based.py b/region_based.py old mode 100644 new mode 100755 index f512839..c0a347f --- a/region_based.py +++ b/region_based.py @@ -10,7 +10,7 @@ import argparse import numpy as np from PIL import Image - +from tqdm import tqdm def seed_everything(seed): torch.manual_seed(seed) @@ -115,7 +115,7 @@ def generate(self, masks, prompts, negative_prompts='', height=512, width=2048, text_embeds = self.get_text_embeds(prompts, negative_prompts) # [2 * len(prompts), 77, 768] # Define panorama grid and get views - latent = torch.randn((1, self.unet.in_channels, height // 8, width // 8), device=self.device) + latent = torch.randn((1, self.unet.config.in_channels, height // 8, width // 8), device=self.device) noise = latent.clone().repeat(len(prompts) - 1, 1, 1, 1) views = get_views(height, width) count = torch.zeros_like(latent) @@ -124,7 +124,7 @@ def generate(self, masks, prompts, negative_prompts='', height=512, width=2048, self.scheduler.set_timesteps(num_inference_steps) with torch.autocast('cuda'): - for i, t in enumerate(self.scheduler.timesteps): + for i, t in enumerate(tqdm(self.scheduler.timesteps)): count.zero_() value.zero_() @@ -175,20 +175,20 @@ def preprocess_mask(mask_path, h, w, device): if __name__ == '__main__': parser = argparse.ArgumentParser() - parser.add_argument('--mask_paths', type=list) + parser.add_argument('--bg_prompt', type=str, default='blurred image') + parser.add_argument('--bg_negative', type=str, default='') # 'artifacts, blurry, smooth texture, bad quality, distortions, unrealistic, distorted image' + parser.add_argument('--fg_prompts', nargs='*', type=str, required=True, help='one ore more prompts for masks') + parser.add_argument('--fg_negative', nargs='*', type=str, help='optional negative prompts for masks', default=None) + parser.add_argument('--fg_masks', nargs='*', type=str, required=True, help='one or more paths to mask images') # important: it is necessary that SD output high-quality images for the bg/fg prompts. - parser.add_argument('--bg_prompt', type=str) - parser.add_argument('--bg_negative', type=str) # 'artifacts, blurry, smooth texture, bad quality, distortions, unrealistic, distorted image' - parser.add_argument('--fg_prompts', type=list) - parser.add_argument('--fg_negative', type=list) # 'artifacts, blurry, smooth texture, bad quality, distortions, unrealistic, distorted image' - parser.add_argument('--sd_version', type=str, default='2.0', choices=['1.5', '2.0'], - help="stable diffusion version") + parser.add_argument('--sd_version', type=str, default='2.0', choices=['1.5', '2.0'], help="stable diffusion version") parser.add_argument('--H', type=int, default=768) parser.add_argument('--W', type=int, default=512) parser.add_argument('--seed', type=int, default=0) parser.add_argument('--steps', type=int, default=50) - # bootstrapping encourages high fidelity to tight masks, the value can be lowered is most cases + # bootstrapping encourages high fidelity to tight masks, the value can be lowered in most cases parser.add_argument('--bootstrapping', type=int, default=20) + parser.add_argument('--outfile', type=str, default='out.png') opt = parser.parse_args() seed_everything(opt.seed) @@ -197,15 +197,18 @@ def preprocess_mask(mask_path, h, w, device): sd = MultiDiffusion(device, opt.sd_version) - fg_masks = torch.cat([preprocess_mask(mask_path, opt.H // 8, opt.W // 8, device) for mask_path in opt.mask_paths]) + fg_masks = torch.cat([preprocess_mask(mask_path, opt.H // 8, opt.W // 8, device) for mask_path in opt.fg_masks]) bg_mask = 1 - torch.sum(fg_masks, dim=0, keepdim=True) bg_mask[bg_mask < 0] = 0 masks = torch.cat([bg_mask, fg_masks]) + # fill in optional negative prompts if none given + if opt.fg_negative is None: + opt.fg_negative = ['' for _ in opt.fg_prompts] prompts = [opt.bg_prompt] + opt.fg_prompts neg_prompts = [opt.bg_negative] + opt.fg_negative img = sd.generate(masks, prompts, neg_prompts, opt.H, opt.W, opt.steps, bootstrapping=opt.bootstrapping) # save image - img.save('out.png') \ No newline at end of file + img.save(opt.outfile)