From aa5f246c86167af777992a3709d3e256c886c111 Mon Sep 17 00:00:00 2001 From: Zulko Date: Sat, 22 Feb 2014 23:10:37 +0100 Subject: [PATCH] added version and many automatically generated doc files --- README.rst | 120 +++------------- docs/Makefile | 2 +- docs/conf.py | 6 +- docs/crash_course/explanations.jpeg | Bin 0 -> 43508 bytes docs/examples/logo.rst | 15 -- docs/examples/the_end.rst | 2 +- docs/index.rst | 8 +- docs/install.rst | 3 +- docs/ref/{ => VideoClip}/VideoClip.rst | 24 ++-- docs/ref/ffmpeg.rst | 2 +- docs/ref/ref.rst | 2 +- docs/ref/videofx.rst | 47 +++++- .../videofx/moviepy.video.fx.blackwhite.rst | 6 + docs/ref/videofx/moviepy.video.fx.blink.rst | 6 + docs/ref/videofx/moviepy.video.fx.colorx.rst | 6 + docs/ref/videofx/moviepy.video.fx.crop.rst | 6 + docs/ref/videofx/moviepy.video.fx.fadein.rst | 6 + docs/ref/videofx/moviepy.video.fx.fadeout.rst | 6 + .../moviepy.video.fx.freeze_at_end.rst | 6 + .../moviepy.video.fx.freeze_at_start.rst | 6 + .../videofx/moviepy.video.fx.gamma_corr.rst | 6 + .../ref/videofx/moviepy.video.fx.headblur.rst | 6 + docs/ref/videofx/moviepy.video.fx.loop.rst | 6 + .../videofx/moviepy.video.fx.lum_contrast.rst | 6 + .../moviepy.video.fx.make_loopable.rst | 6 + docs/ref/videofx/moviepy.video.fx.margin.rst | 6 + .../ref/videofx/moviepy.video.fx.mirror_x.rst | 6 + .../ref/videofx/moviepy.video.fx.mirror_y.rst | 6 + .../ref/videofx/moviepy.video.fx.painting.rst | 6 + docs/ref/videofx/moviepy.video.fx.resize.rst | 6 + .../ref/videofx/moviepy.video.fx.rotation.rst | 6 + docs/ref/videofx/moviepy.video.fx.scroll.rst | 6 + docs/ref/videofx/moviepy.video.fx.speedx.rst | 6 + .../videofx/moviepy.video.fx.time_mirror.rst | 6 + .../moviepy.video.fx.time_symetrize.rst | 6 + docs/ref/videotools.rst | 2 + examples/logo.py | 28 ++++ moviepy/Clip.py | 20 +-- moviepy/audio/AudioClip.py | 22 +-- moviepy/version.py | 1 + moviepy/video/VideoClip.py | 24 ++-- moviepy/video/fx/loop.py | 8 +- moviepy/video/fx/time_symetrize.py | 8 +- moviepy/video/tools/credits.py | 15 +- moviepy/video/tools/drawing.py | 136 +++++++++++------- 45 files changed, 397 insertions(+), 236 deletions(-) create mode 100644 docs/crash_course/explanations.jpeg rename docs/ref/{ => VideoClip}/VideoClip.rst (83%) create mode 100644 docs/ref/videofx/moviepy.video.fx.blackwhite.rst create mode 100644 docs/ref/videofx/moviepy.video.fx.blink.rst create mode 100644 docs/ref/videofx/moviepy.video.fx.colorx.rst create mode 100644 docs/ref/videofx/moviepy.video.fx.crop.rst create mode 100644 docs/ref/videofx/moviepy.video.fx.fadein.rst create mode 100644 docs/ref/videofx/moviepy.video.fx.fadeout.rst create mode 100644 docs/ref/videofx/moviepy.video.fx.freeze_at_end.rst create mode 100644 docs/ref/videofx/moviepy.video.fx.freeze_at_start.rst create mode 100644 docs/ref/videofx/moviepy.video.fx.gamma_corr.rst create mode 100644 docs/ref/videofx/moviepy.video.fx.headblur.rst create mode 100644 docs/ref/videofx/moviepy.video.fx.loop.rst create mode 100644 docs/ref/videofx/moviepy.video.fx.lum_contrast.rst create mode 100644 docs/ref/videofx/moviepy.video.fx.make_loopable.rst create mode 100644 docs/ref/videofx/moviepy.video.fx.margin.rst create mode 100644 docs/ref/videofx/moviepy.video.fx.mirror_x.rst create mode 100644 docs/ref/videofx/moviepy.video.fx.mirror_y.rst create mode 100644 docs/ref/videofx/moviepy.video.fx.painting.rst create mode 100644 docs/ref/videofx/moviepy.video.fx.resize.rst create mode 100644 docs/ref/videofx/moviepy.video.fx.rotation.rst create mode 100644 docs/ref/videofx/moviepy.video.fx.scroll.rst create mode 100644 docs/ref/videofx/moviepy.video.fx.speedx.rst create mode 100644 docs/ref/videofx/moviepy.video.fx.time_mirror.rst create mode 100644 docs/ref/videofx/moviepy.video.fx.time_symetrize.rst create mode 100644 examples/logo.py create mode 100644 moviepy/version.py diff --git a/README.rst b/README.rst index 963d1a81d..769cab4a4 100644 --- a/README.rst +++ b/README.rst @@ -1,12 +1,9 @@ MoviePy ======== -MoviePy is a Python module for script-based movie editing, which enables -basic operations (cuts, concatenations, title insertions) to be done -in a few lines. It can also be used for advanced compositing. - -See the full documentation and demo videos here_, or have a look at this page of `animated GIFs made with MoviePy `_. +MoviePy (full documentation here_) is a Python module for script-based movie editing. +It can read and write to many formats, `including animated GIFs `_, and enables basic operations (cuts, concatenations, title insertions) to be done in a few lines. It can also be used for advanced compositing. A typical MoviePy script looks like that: :: @@ -27,116 +24,33 @@ A typical MoviePy script looks like that: :: -Contribute ! -------------- MoviePy is an open-source software originally written by Zulko_ and released under the MIT licence. -The project is hosted on Github_ and everyone is welcome to contribute ! Please give feedback if you are using it and encounter difficulties. - +The project is hosted on Github_ , where everyone is welcome to contribute and give feedback. Download and Installation --------------------------- +MoviePy requires the Python modules Numpy_, Decorator_, and tqdm_. All of these will all be automatically installed during MoviePy's installation. +You will also need a **recent version** of the software ffmpeg_ , preferably downloaded directly from the ffmpeg website. +If you intend to use advanced features you will also need a few other dependencies like ImageMagick_ , Pygame_ etc. (see `the docs `_). -Dependencies -~~~~~~~~~~~~~ - -MoviePy cannot run without these dependencies: - -- The software ffmpeg_ is needed for writing, reading, converting the sound and the video. -- `Numpy`_ is needed for image and sound manipulation. -- The Decorator_ module is used in the MoviePy code for better code readability. - -**Make sure to use a recent version of ffmpeg**. -You can either install it or save the binary files in any folder and specify the path to these folders before installing MoviePy (for the latter, see `Manual installation`_ below). - -**Debian and Ubuntu users**, you certainly have an out-of-date version of ffmpeg, you *must* download a recent version from the ffmpeg_ website. - -Normally Numpy and Decorator will be automatically installed when you install MoviePy. In case of doubt/problem they can also be installed manually. -Numpy can be installed with most software managers on Linux distributions. Both Numpy and Decorator can be installed as follows with pip: :: - - (sudo) pip install decorator - (sudo) pip install numpy - - - -(Not so) Optional dependencies -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You are not obliged to install these but for many uses MoviePy will scream at you and say that the package or the software is missing. All these dependencies can be installed any time after the installation of MoviePy. - -- PyGame_ is needed for video and sound previews (really essential for advanced editing). -- imageMagick_ is needed for all text generation, GIF support, and much more in the future. - -There are many packages for image manipulation/processing in python. Most effects are coded such that none of these packages are needed, or such that having at least one of these packages is sufficient. For instance, the feature ``clip.resize`` will be available if you have either Scikit Image *or* the PIL *or* OpenCV installed (PIL or OpenCV are to be prefered). - -- Scipy is needed for many advanced functionalities (tracking, segmenting, etc.) -- `Scikit Image`_ may be needed for some advanced image manipulation. -- The Python Imaging Library can be used for resizing videos. -- `OpenCV 2.4.6`_ (which provides the python package ``cv2``) or more recent may be needed for some advanced image manipulation. See below for the installation of OpenCV. - -If you are on linux, these will surely be in your repos. +Installation +-------------- - -Installation with PIP -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -On Linux, if you have pip installed, just type this in a terminal :: - - sudo pip install moviepy - -Maybe it works with easy_install too, but it hasn't been fully tested yet. - - - -.. _manual_install: - - -Manual installation -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can install moviepy manually by downloading the sources, either on PYPI_ or (if you want the development version) on Github_ . - -Then just unzip everything in one folder, open a terminal and type :: +First method : if you have ``pip`` installed, just type this in a terminal (sudo is optional on some systems) :: - sudo python setup.py install + (sudo) pip install moviepy -Before doing that, you should make sure that MoviePy can locate ffmpeg on your computer. To do that, run the script ``moviepy/conf.py``. It it fails, then you must enter the path in the first line of this file :: +Second method : by hand. Download the sources, either on PyPI_ or (if you want the development version) on Github_, unzip everything in one folder, open a terminal and type :: - FFMPEG_BINARY = path/to/your/ffmpeg - -Installing OpenCV 2.4.6 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -OpenCV is very optional, its installation is not always simple and I found it to be unstable, be warned. The installation seems easy for Windows. On linux, here is what I found on the Internet: - -- Remove any other version of OpenCV if you installed it through a package manager. -- Unzip the source code of `OpenCV 2.4.6`_ in some folder. open a terminal in this folder. -- Make a new directory and go into this directory: :: - - mkdir release - cd release - -- Run ``cmake``. Here is the line I used: :: - - cmake -D WITH_TBB=ON -D BUILD_NEW_PYTHON_SUPPORT=ON -D WITH_V4L=OFF -D INSTALL_C_EXAMPLES=ON -D INSTALL_PYTHON_EXAMPLES=ON -D BUILD_EXAMPLES=ON .. - -- Run ``make``. This may take a few minutes (15 minutes on my computer). :: - - make - -- Finally, install. :: - - sudo make install - -And voilĂ  ! - -You can check if it worked by opeing a Python console and typing :: + (sudo) python setup.py install - import cv2 - print cv2.__version__ +Linking to ffmpeg +~~~~~~~~~~~~~~~~~~ -Advice: do not throw your ``release`` folder away. If later you have strange bugs with OpenCV involving ``.so`` files, just redo the ``sudo make install`` step. +If you put have a ffmpeg binary in you executable folder (on Linux it will be ``/usr/bin``) it will be detected automatically by MoviePy. Else make sure that MoviePy can locate ffmpeg on your computer by running the script ``moviepy/conf.py`` that is in the sources. It it fails, then you must enter the path to the FFMPEG executable in the first line of this file :: + FFMPEG_BINARY = path/to/your/ffmpeg @@ -153,4 +67,4 @@ Advice: do not throw your ``release`` folder away. If later you have strange bug .. _ffmpeg: http://www.ffmpeg.org/download.html -.. _imageMagick: http://www.imagemagick.org/script/index.php +.. _ImageMagick: http://www.imagemagick.org/script/index.php diff --git a/docs/Makefile b/docs/Makefile index c077a7607..a1f83541f 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -2,7 +2,7 @@ # # You can set these variables from the command line. -SPHINXOPTS = +SPHINXOPTS = -E SPHINXBUILD = sphinx-build PAPER = BUILDDIR = ../../docs diff --git a/docs/conf.py b/docs/conf.py index 8685a91cf..984809486 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -26,7 +26,11 @@ # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', - 'sphinx.ext.viewcode', 'numpydoc', 'sphinx.ext.autosummary'] + 'sphinx.ext.viewcode', 'sphinx.ext.autosummary','numpydoc'] + +numpydoc_class_members_toctree= False +numpydoc_show_class_members= False +autosummary_generate= True # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/docs/crash_course/explanations.jpeg b/docs/crash_course/explanations.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..cd71e58760489eea4ec30b0b5b89b38dbcf086bb GIT binary patch literal 43508 zcmeFYWmH_zvM$=V1#2Y1AvA%;g1Zx}3D&rW;7;QnAXo_6SkNHdxVr}n8e9U61b26d z-0Xe#Irr>4#(Vd?H^%$-R{!WR$E;aZtJYd`eDzh$KMQ}>03;Aa6-59V8X7?P?+@_j zH-Jpu-P*#_+KSP~-qVgzNkvWb&k{f$fQ^ZTjfIJgjfIVagN=(%f{%}fhfhvK{DkBw z`LkzF$tfwR=~x)3X_#p#DH%B#nP0Gi*}+r{Ts&NCJS=Qrwtpx=!@ zrle;3e|-Jv29V;SDWm_wKzj~ACq=^`Mf=kapaY-*&@ukr0~+8zE^Hi3EL;q9G`zpl zZ%F`X7#L`n76j27?=PoQfwR`6EB&p7CGf}J{=2KXc8_3GZda*`}CQh zoUXe^a!SwC^vpWHkg)t)udsr;`d$!=fP%KAp6B})udKZb`_4oZ^~0@F5r1t^|F!+s zh5s=BkADnIG;}QNzjH#Q0CY5TbZi{le>nM1k-rX*VlrU?$;f$SDfsxcps>*Aau!MX zwLNFrQ_LXwb?iSVz>~lF&`B{!0n&hjOlS&vO36U}Y?aciX2VKqk$IP@h5N+o0_r$A z5U8z2B)cRW$xJ1>!JCQUTH?Zua;n=M41K<~iOX?fcOa)%Bf3_pLW`{-RYfJiE{G8P zX?*fcyVAPP`w4x-J-!8Nk&&^zqq;8|T#i4!;^A<5e5aD9kwC07OZwG}Z3|+ld7425 z(%hU_R{)nF{k9t?)1=_tve5ZWue`DVWa!THDwRo1#ZL{CPt=BdgJd7>UK7DUu@Qvz zdBp|R^|bZui^DD1f~u0R@K-d4h9nZG$ICuy9*2 zh;~aNo<7BsFvJ<{SfIl%zbXMMT%-7Vn>yv`5iY#wu*0%c@|OV4R6y#Jq;4Wxs>3{P zd|jWi#?5bzrjkXR)k_8KJ~6Fv%dGk80#FIP$?#GvM}6sMZ@g$Xkp7AaJ8DVzvr~mi zZTa7g-4rKYH6XuYa#4Xo5aH#RyK!i(NU#hTqKW^HkRt*A_XDFG`F9_zUh7Dun`iKj zM}mX^+(Tw~OpJ*<08mLHfC{8n|N0=KS+#NChxd%WDo5gEy1$?J$M^$vSjz#qtOTEu zz9||QsXy+_QB^HIWeKN22bxn@z6q=2WnzQ1wIu&P{$?GUQjYw!L)B%dx zon27=&65z=+NqVTr8_D>s8iLpdHL#khpz6JiI-`GK@K<*U;Y(~W@!(n+!te~`$b62 zzNVUCLv)*mfzj1wlj^tDX1Yr{*dip$2Szi3C%y=B!(l|rh6Ba|?Sqg5Z3njkjT>X` zHRk?&kEFYpLs(}^QYx*?zG&3%FX?>zHtMF~UD~WU^BOD< zcB)kBHKcVz!>EY4o6eG}JD{OMZNPtG8P0BS!>kLv793<)WZ&2_1J+{Szn{m=dqEMs zGLF%#R9QI|+y0R&$|NQoYAQ#}LcYsLkBYYcmB)SgynAit7WXFWOyr^7X6vkPU6SILfAn{c2S!8xaMb4FM0i5jp zF1u(;h4b?}1Eyco`l=LEOS$uL;4vPF#LhZP;`5nB#|?zG)TU|1=Y{HT`HW7Apr3G8 zM2A+-J`8JtheZZ8DW2Cl(2$Q+uT*MXU48QZ(C+K4_Vq?HJ|m?iB_GW6s)Tlzm7eWr z4J!f?)mf>_ed^I{pY}6h7m_ksdY;9`N_D+CA){KVE1_23=ss2LV9cF7OICRV?!AZ5D7``vQ_z<8 zieRqOEDQqK2WEs@{F617PKCaLHFx>aQ5?@SG2NL<;+4kP2vfljGzc^VnvzG3WW)M@ zKhTwKz;^XlbtDm7sT&izw+miy*2kUHo>sQP5qs4V?iIV4ZZXbBXhNRH#Oa8&Dlj+g zH}iduC$UFfPKV`I{!o({*8)v-WkcdQj#RgrJ_^mfa&^b{sNVT$lPjT221&1Ol@g@b z^d)7l;x36SJ;@N&hJx8up6GYI(%FCnE4fsllW3yFtJp`rjh5c&hOT*EE@tL&K8b72 zIp}AU)Hv0QkgD~(#K&YOL9YEXy7*jn&SSC+t`PCRZ_TlI<$`CRP8 z)w^$dg7t;#S|}}yR_+cnU%_yNxt#XExVV3IvEcb$AMXxRO%#pw#sL~FA2~F8muia* z|I|+Jn4jnCbY`>qGDBT0T4PU}Ld@mYVp%AGg&JcDCkq>MsbLQ-KwK@NZ&uSgfGW=5 zsF(#O`^19(HxqCDnZ=KNOIDm{Oro-y|Nh1V(IlUr9xZr7@M5)lZh$n0G zTw)!Q%V4`RJV8#wT9w-3y9l3g<*SVl+K_|iFUanixiR)PE0G?b8^m;r64orp$zBnWV`i+ zin!{IUYF4gfC^|}$h=%EoDZwdm>aT*B-mN4>W8OSx7wEjreCunzotBDeHJfs<|b^M_=AkOlzo=OT&^!qd?vgd&V8w+X{iKm&?EJWyy~$S~`) z9e%aLcV(epjZSdSMWN7i!1|&)#BXt&S&}uG~?aYapxGAD-b9diCwqYUFD1Ga0`WBpMj6x0^YB zPvk15r9dG8U(6t~PisLbW=R=W`3kK!q^L<=u#=Ecqqk`)^hUt#Ob zkk*=y8y^m5ZeIc&C|X|c*wtvrUc+u*O=$drtRutT8V=&ZI8M%4!(DTS=}Y!5VQ+-L zSSJ!dS&r>$ zO)JGp5n{R{{7D%>-4Rmcv4q{eCFis_b`@DxcQLoxD_KB84l(DZkRS9S|1yS@i7egE zE=l?}TMXkPu-``yK(EdAEAJ6fg@W+#nq*bigBabEWHj|WI5A)b1BQ44zN!#G!_8ydcFwE>H9mnK z=u$4F3V+MZVfM3AC~Wz*m)y{lYDCN%-Z`@~v~E`YHQ_lpjwv!HUIq)92gW3l$BEKK zQ#t|$GE`ppsIr7C+4!h-)CkL^)bTGSI%uCbCeA!k%z&L|xzrjqD1mtp4M-PGUGaxn zBj8e8(HuZ%M|hgK5dW`Fut8lb+2-C5O2+<#XCkuZa?@HJN-j9m7%%>hVCBDwY5p(8 zIC@QnCTBXqHz=I6^Nk>ks)o$Sn@!?d!VV0!1VYD`jfPqn)K4Cj+Ol*`?glr%oc>ZwGa3u?sHp;N+wDF^({7>h;Lf^ZWF#`Y7}A@iG=uAQF|e*n%p(t6MS0PbuaFGwHe9)!Akwu@eVVfr#(DYT%m zt@w!l8|Q*+^}ia{Wtj8({F686p73{L5UXPx+q#(9PmNXKhuNid=?ub%cNq7S--E{P z34Wt|mVYFEIW9vS>tnD&%)8{@RT`TzH{d?R-?i&^3gZHS!|(XQWd6P|&sdv^!t#a7 zsaBF2Mao;!i2v*Q3W&~Q!6`F}D(F^t>*b^U507BEKY*rMu-cSQTz>k$hB^P7bOffX zJoWwmMgPBwz=yf>&^v=Vpu~b;4w=>xC{ZlUSHncIc-{YH7{#7k3;f$28~V`8Jw zYwY6S*S5=hqt35k1rih-EDedN#5lCANiv>IO3oZ{O#xLdW6t=K(YI6|LYn=bGFQo^ zq@!V4tAIje2Y3f31~KYLDz{in3k7hF|Ah=YR)f-LID%${LdR4!0saS~ZYb=8W+1D> zM`nE5>DIA`0RBvL4M@Cw4y?#aenPPL)UCMfd;AhNIwF4AWAJmT^4|XfnH-0(eS~Ua z7dKa5^5uMxULQ?PDfo^=YT=zR=Ux@zsyt=HW*(z+eodOpaO17iNhf~#ZO=x@p>&AYV zaLI-9yXFz^(9*01A`LKR3w3NGQ>%8p#)syqD_TTOzSg^Gd ze`ub)3QvJuV#PA(`5#X0S$~(Grh?Z`A;r(EFjGUsdN2E`Lk}g&pZROWDX_fc;l!evD(IUm+X~BAFElY&)(?5VG>jsLn7@qK?qiWMaTC?Ze+`AbAy)eXeQn3DZ@b{6Q z3jQ1mwERY{iJ{DXwvPh07f%PwhpRMiWleVb=G~T}gtQ4i;cBU>monxijf>!n865`4 z&hsDF+SI?3GlEak%a{y?g=k{h_UsO{dhwp*de~Dk%TAQVcmGdK#a|M;_xiI7kp*q7R@`t?s?`_rwLv0su+#7hik zy=e;aUUv)`4=d{#rL3zH7gL0P8$Fy5ns&KFPs>Yuoz6ReG}xJ&QEYEtw`~9S^+h?K z4v{3-1quC|zXksQW~)c5zBSG@2+tf)QhdpFQ472nbii?zYiMA=WA+Kp+=#FIx0{94 zXWHuJ8wf9d78rb|5Ueyq-Fi=0_i-H`CJV@jUjxDx4fmCOlj7!02dx9{2{vDD&HNZL zr+W9I)%jQQ;k*f(USW*#Q4aGp$Iwx;X&6qRR?8p2Z;wBKQlqO*X`s+@nXloT)Ni@F z(fj{+URqsF-%Sj2>|4cR#O61ALR>s{F(y)o{{XNY#mDEReWJD#f-CL0gcZ5R?N-Ia z8s;{!y@~%V&I#>ff8~U(@+-&m>sDrdIm$>bJq|!S}r>|G|?7Y0iLa zM>_GS-><}8lCIgBKmJ=NFzs@z-tF%FLuav{*r>E^(!Jx4a`oK4KY-_SPygz#{&)Qm ztBUoH9SNgmzl?sKvKsEv|1Q25{Gyj5`Jej#J=9O^f^2Vt{^|bxs#N{Km%@whU-W8c z7T#^i7o5{$d!7DUcoN$O=imJe&tD(R|MEO`jhG)F_|&C0|C;l>VYnyR>re{K@%o?d zpT;|;Ks_Nj%fM06YgABj&tt^`y{{YINeXpS<)l@|a5`@a) z5H324e_y;=ALl|$_i8{qpz*i#a&=#QjtlYmhxdEgf4`OaUtnUO;EL_CSNgv>nH*d! z_Jf)FLp(F{uf{(B+mGg~wwVj8teW-l@6P{zKVncRkg(w`-M_w*|L_y(bZh)%S5vUB zy50Pj(d1FrxkpmOY+BKX^~)K!q6CGZrtjtyqjl!An^{CBtudc_ue zcWxH@;6VK?*_cQQcfZ2*r<5vMMy=;~nyXuQ8;Xu!G?-23+s6wVS5CN>XpMS*b+EMRP~cAzmrsW2dSz4T~E~T!}V9Mz~C>hOD0OiYHGP$lWG@8c)0& zAIdeBEDL|oY*#ViQSEOx#RjOk$44ZNjT3G!rG`!M?p5Ms`ti`nt!m+mm-V`7poIZ; zwS~C6hi1#IEY-K+z zsbEYiQ_!>=hi>53+uK*&h)T)B;DLAQ51pkH=EJ<{zR}0?G~Wr{lGJSJr6hF}Xtyat zLm1yY@EauW82@PN*bW;%66vHi;w=y;hEu`|bPB>xkgwR#`ENS*2)_>6KTK~ejV7>D zG;zF16-P%MRZfb+4}KCoyKAJ&kk&LoD8!r~y2g@uC#P|I%J}dYbD0tovC$4HD+{*E z-xws@Hp#%s{k5hdN{gU%*_QYmh7xVPvL$2lw4dn615&snIG)+TQ}YdIL+Sof+z+xg+ylL*Rb{IygK0MlVb6_H3stJ z+(2;$=b{D=eVW&P7u#LE){GKMq;f!Xa=x4msEXDz)@K8-x#Ta7uM85gfBLLSS}&@X zFetAMj>+gGR`WTvMPeO?H-HWaD^R42}C6Stg>9m_Vzg~r4bG`R;~X_ATkCyLW= zdiQtN>!n`M>zde*zLPh`KhQjy!fZ+P?g;z#t9TKWSULM*ip6lk{Pob!>*i+rPZeQM zL`rgZ?(;$#gSKW{6k)D>Wo37{@!pCu=DA;TE{LdXTDT$Ot3t`Xy!q2Wt6zefp5NY~ zQZfz%bp8OoYiUS~%8uH~$%0(G*4_|nQ1`h9)gn_eoJ5q+2?DzPzv}Uw3ad`7qzZN~ zKI+^t@08l4xijLv-COWPTWe7a>TDd)wQqG;9n{mxwyf;$ofleiKIt!+C#dZD;kHWP zgt$^+k?0wuf3@{{_U%3!ID?V*s>IBY)*+9p&LnNSy9@+G7{12-mbT+ctZ68{5y~jwCa3z13C~N-kkpfU!WE!d4&rO5G6Qv!Z$J+=X7AtZ| zv@y17rQGS8`r#}CE?$C?6QOPSUNA42duMfyOzylH@k{y3RikUZtCOYRGNn;>IQw-^ z(nlvEuH0_yzP0$xINUGpa&Nn_rGt3CFyy+p?2D()isJmVi8of{>R|zTh_$xAftyo$dV^JNCs~Tz-Eea`=NSA|6)aW+Es>)s zRfs1qEcHte-+iITvdt3F=HoSI{G)49@bKQpUhVUIX?o2YETp+$Fo)fZ5L96->uaUK zAHWZ1cNzvZ#^T*lWMFl_enBVlv)`^d%Tgr@YghkOvP0UpB203|N~9GQ4lRhjuRp+H zb!v@6X^|LRJR;uwy_)XoUPEXV(;8ZqKi;5uuc&s8AmT5z)tL*)V`*LqADKVoFor;5 zfGR8?STz^IxtylHINVuf;ge9pjp$|x7>6!fAsF+llEYuW@FTD0seZ{)J_rQahx%SH zb(f)xm_|NQC})X!Wd9g~IU_MyF(QEg$-uzt;kg+7*Ly|xg~!U((R+UYzQi5B1k)OB z^po-a4wz5hsaa&O{i;H+8ny0PFyt0Y`o4~Tcfz_~8o!X#Uh#{Q^>SA1B_0?3*My>W z@<)+-3dtR>G6`5N>iqk#I@9(mnE6h%%}=Wb$4e6=vgF#Y%VO({FXi-4`SrFHi`A6q%yCtD&CXbFYWAV9Vz#4ufwVuq-}P zv5s>H?f(H#R?9qKIU+@<;+r)z_2lVITLj~y8PX|*mQ;PzoAi?3emjq3g1?6Tdmz;amfy&U&4x&SsfQ6#cw(jRu>v>AJOvF64U%@UgU8mwtk)zVF#Ch!yQ0~Kz zrw@MFlIl{PZ;`Y7>s*mNsMzQ=Vq@Q{oryJhs^40O*g_KTtMy}!(}#9@yry4TSjM9s zLCCgM%B0cH6Pl9w4guZ~bC*Z+HVj4vj9jzr;T73Hd{{hfxmuV>Sji7QwZQkj%|pr& zzWzpfZYUa6d_zh3_qv;|O%6UREFa?4<2NS4$d_TqcVZPxjwy<*^&W9luTx(7DIxSI zgV2k;Ze+>#T%^;}v5Q~@@@H(DoYy661HazAyULuwhnGTs z5D&BX_{z_oWC78on`Eo~la z2a#158}~oR08h=91clufdG1UcT|6yW z(2XnEdn?%+7>?)y@<=epZCkJ=4u1_6Z8#>MZTGYlq{aN=Chwx8Vo1~T228cSW(5Xe zS$@iUAyA>3GeS}J(sLQfPZ75k(yRx>PXNVF`-f~en!f#FAedTZ*zoP6M?^=0o-NojR@lgDudS$AMSPCawzQH&}yb?}QwL@{xm}a5z@xuk@3(ard zf+q7ca*F7hi3<>=Pxvh*SRC@DjS=rvE5i>6vUmu7QgcUG*w&Eneijz{+383_m{h`T zT7?&#(IYuF-BdeGHq2-_ApP`nX2_4}yy1*u>thK2)V21wSuz%^bKAi{TLz=c1eV8_ z8UjnTOkD3p_(y3PaXMl6eVPHo*jMbr8e4GU!a)>lL>>4TZ)RIgRs6*@8k!gJxSLYL zif-%U*1y@}P#p;#>$tY|FJ{jfLju%TQc(IZ%hm^Sa#jzREr5iFPveR1XRq(n4l@$+ zt)`nw0f;B?jw?I23%TWi108n}w)Zm1`vyl)2k@&s2pH? zs1Tbwrc#JCDKeft+WtOFXL@$$Zq>6o5C=`B~t~ zDH*bcP>`fHni5juemW^`_TlxdNheu1fJXAiVx+`v-N{GMdt%O~l64+4C@sw3>8r`R zTJUS88%Vymc_8OYy%X)#fW~7v3DQ^1pd84cr{6!37+B~pCp0m;3HdTU;6nu{HYNK2lu&8muZHf1VM zZZ{jTgRXEuh*1ODyQ&`&n}s^z4QxnR(CN<~s0e>1C5#zoqLWo{!H%^yM;dCD)ouy6nGoSk?teElPq)=oJgOsB zNdve&qAo|$=|m^I&Qb`Gz^g2JbzT|#1*6js>lJ$XCrzo~3|h&+PR~hgn&hIlJP~0> z0=N9p6XYju>Pk6`q&otbF$GIXjsfb(BkR{H_Lh&3y@_x2#*697B=0tD6%y|B;R%RY z4Z{Oft5N=%$_wMP!x2Tjsa0n@e0+9}?IAb7uE@bKz{Qgcxx(y%bB`z!xe_V}{Z0L2 zPOIoz;-jhtFCMS00(7L$`nY#GL4pm-jWwS4Tdt&T){np1wp(~D!G=Amyk^Pm`%q=T zg96&;hJ{e5(`C#6H8F3CU(Q*C?c5ZDvxzvatgE%ZAN$U8;oqbti~gM^RUzC~(?`Cg z+S_>cY>GGQ0Qc1`m5l(7(i=mPo=I=tgJ*2<;g5hk7SI2zqE|cpi_WYJ`rQBSEV7AW zHh-}I560HnH2^=UtZVzGLfrI8g9T!r$_YxRVHUxm{cx@8a-S*<~EiprM$?Q0z1>&>FC3LQ>QhSS4?M&EC;2M4QtU2U7ws+*|r@1l4&s&tTr?w zIiod938#G^PuXZV_fDu2>?Cr(q%$H?n-YrxbtEiXH!&Dw}Bz@cW zA0w(v@KoVVRzgLy#@1Weq=WQ?T6NxDR>BHZ5=)s}Oa1wwgx$6K9yn4XLlGr{P7MuP zk}APD3@~=Dz3rOF4+mOoYeBjK^{vlMTX(G;)s&|vMWjBwrks9<$c^&V$;#HR$A_Cm z$Gu%SWn;Y*Xo>?Pk4GTkN-G9yI*-KSpS9Fa$DqfUGv+!r+2MUdF(2Pf>KJI-;d5hz zCQ1s@eWcM_RvkC;iWoN$(v9e1;Y$p{C>$>Jo(Q`8Q2v}B_GP?vFx@5bR)MLXlSvtt zh`S=^yY6sY@une2%(r9?1yS=knGH11;02Z>p}Nzq}Al9qFkam`oVPWUWUHsr!m2kEgOZJz1BdSm6UpwjrHL8e(0 zJkvJK+vNkP!5i>}9hIo2jS6nvYGVcL_U;=P4oSsZE-IP%h8J@n>V`7O^JN`gWcls9 zwwy3>qr@9D5IpqEWU0&M^3%|gD8pwmH^4-ll%%0F&fG(^CM+0jcdHxBPe`I352nwK z-B}|)TZmS$>D+>lSMOd}mq^t$FDyw#xOxcQx;WiBDoHzW5MdY9Pe6?w9PQn*F~#ul zSj&3{f^=J)(VEfW*`x!7DZ6SYgb`nK{DrRgR1>Wd`!5al{>Y;U!3-{@X0b_%(WSoKiaZ?vyK`Uw z_U%+=ypByP4SUk5w7S^gKA5g?Bb{U;8WI-0gTs4Jt+RF^p`bXUxLxpP6_L!G%u7Wd5%A=m_eOVBy`YDay_U-dk@R|B=!(VY%t#reLi{KHRVdrio z-YB4N0Eaw1c-F%-g9TsSVXa^fREQB)=U*q~u~RLc{kI759n6VZAHV|B(-t)2t*Y|? zvu#Co9QoyHVU44!vqWqXCwe1>XSK81gQX^-7mWJllOh2uywN|+(ZFqLu)*KpQRiWN4*D_+O{Z{18jI+VjK0P~;JoFub$G0_D~1UFOgH24oG7!abc-Or!5# zgI}|-uN6a@6Fo))7=4vMBt+tV7eW=S>Efm7%f|WQ2Ou{MprnkgG>XE)F2|9FrQ{H; zXoX5!W>?ZeDS^-T&9#?aZT~>yrsK1eQoWCwT&B5pL;gb4i>l7;f|dlHAFC8EJ1wlx#Cw=E#bnN0-;kUztliSt3@X-^2l-@a83FHfZPs(~{1Q zHpq7I@qvk7rp1SJ967xY$i&11UVX!f8RM2EpN2*})wDxyksnwtVeDKbO=Ad~QmCz= zDSUHy@eP8-IK`HI8-G-b8NQ$+WnZCEpid9h-C6CfOlux@B-h+aE}By z?aRUj(BCrto!c>%%GX1_xXFW&z%xjqp+$8$U%HeDQiYY<8F#pwL4DEvzVkmTTA3cE zQ4M3EoO4-RCz4qA$BTx*kl-!vI19gX#!D$N)z{cl-I)pqbW3!7I4`@6fRQdhqy6rHTo z5CJI|8u>&*67<@fjaH43=Is z^-68A<@099ILx@hR}TOyZfPGhFLSo7u21PvbxVt#5uNmv+z}=bv0KkpmQSNS(r)W> z`aVAuL9ApM*xFNi_BWd3)HVQ926T4aG;>yF$+A<5J>QUp%Gi4;puP!GPAq1)xbK(! zkgq~bnsGHu?I}Svqtb-^=;mlIs45jlrocHZ$10^CVwcL*TEEl8<;Jqr6(;3^&Ntln z+}+qMd>4yyI*gA=t8lXqj@j8G*wnoEsBoyzCi5!ZpiPwMchKe9j|S}@Ha2la``{G% za3t`8XH$zYUD;eMR5tpvLAp9~r129QLDmuFmN{ncEX zSJeXZ%1hu&pwCD<@+QmKKq9#fCDVPSKb|$?-6^zuWw=o!W~=J%vBp64oj5$OF9hKJ zERQ1#Me^D6ca{+}vC)8Gl@TW5w(-SV!BbeW=$OY;_xD4b+iRP|`t|2PcWPW5xXMVp z*eu=-y9K|DVUfd*7yr0P(7lz5z=cgqVmKK*B+#1_Cx@doR_&KQP-Q2McZ+PUkH3$q$G_0m~jJFxAwF0eX(nmm| zEnT#Ht2g_HqRgV8-8cs;rvOFCfFuvK#+|1uL7-3P+hQ}s<)0&`b%07)&m`YWlB#Y@ z5x9yAHA8|l7L6JtxK2815Un6kaezcjGdfRzZeNa-v)S{ECH2u_l;ZK+?v_%b*4R)! zn0@tkj9gj}dpR?7Uz{7+JP_p_7DXM?&`zaRswPTjf3Aa?luJ~^dCsvjia1WoN=^si z@trt2@!6;)wh|#bc3~{9PFN+B)%kq0Gcmkroxh4K7*QhJjASum<7@ZIG~Ni60EJvD zo|)(oxVy|O26WR?ivW^Q015VyX1grGro$2!y(^VCu?Nl41fhqp56X<>=VyVIqfIpx z#v-+U+g1sj^G6YO9w~YkWRla6VVV)la?EiDYf?SAB$1f5e-t0PGgf%Wo}W6BiEuN49PTE?CaL@7d+fnI|IyJBr<==7%iik` z&h2g|Z?3{W14vsc@O7)S;WKrzvzZ%eZB&*vM<$ChOlIxTWL2Kno zOhC&Ylcq^~Yu|cM95fRrF0C$?!0YeF+11N!`rIhins7I)jkhB&cm_kVaWWM_{dPex zX^si`c=qOT)nuRORQ}7CxK+c|!^#yvnX5gD`>DDAbLd&#i;Lm-q>qa`!Bplhic& zry_aZOVRbVdwV+;s}!(!xD{K{M2TCq3N>o&5^X5$oT`z-i@R&4OfH_kJ27;KzJ8?f zqM@tW{w&>drHAjCA9q9)wHJq`-i^C{D!Sh;=5AASb5Db$HokzUzh!}qSlz|#QYk6t8NZ8;6BBo09=KdfMTr~0*{b(IIyc0Ps z<&b}+R%bIM*dL_&0`<`=_qmL9URZ8Q?7E4YGR??_%(zDgU_u^&i9j2AqRG(kf6TAh z=?dAG$LE~kE@Lf+yhjOQ;mpTr42o!%mV!95sNCDK6T3KY3csxvOVcN714#o=uI;Dp z!vn#FTbasP$H$i*{lS(+sG7745bf2lsK{navVAS7Uhn~Y#(}Me@XGk{BaVn34w03A zkVu(0c&>lgT~Ew_od((-S8pGa4%9S=jq97P45nIsrY8rBh4UyXqEA@65KYjrB5r$% zdJ>~@oRwGa$UPtuyPotS7&uK`jYRDUR`5$MF4d7)@?p0v5+0ce5+47--I6{UEi>bn zNb6W&_$fx4j`Hw&g8r3E`GimLk#GiTc~9G@&M(tcrQk&~*Qc7z#LMyg3KkF&BL28V z-!12Rwmb4gaD2km2slM;v{lbl9Di8q%*y$<@xQ_E;A+ySfaQ+=nIgmn1jaS~`Lv?| z7b9LqWCI{+qTuM&gp8&922N0jKje-QUPkK|ZK+}|kAx}_OM0B!Pv>lQZ*9~ZWTO=I zTeVf$1q+-W8rstAL)X+!@WWdixbi>p9@gbKN?YxZ4!w6R{9#nFGeSqetJ& zFHSV7(_Pod)ieR7Hu8oB?e_g=Uv+2<)I;Wl^{e=<9SWstgn{9uZ$Ly;Dk6icnGqu| zLlwcr8751W523t})$G3cjAw3@%{qx2QOw;$y7otP+C&bcCVD83Hf9vkvzp6V;afWm z?LFH2;}ZF4)Y~z-7baec&Wr+`e*o(wB37*9fxmQ_7y9&O7MQL*F(xJmaXY zH$^M1$UZ{IVsIv-x#p{%T&tC{`fBS<%)l6-=SbSRz1I!_H9?{K;yPIae2nUho6G8^ z6A%fxPNHZ8;aX6KgTr+OvO2~izP7jVsWGi04YwrAlj6p0%mn+du16Z+=d5xF{@CcB z(L(Jp9?uDglILTsg0O4ESGbZ+DOO(zdJs~uB^asvoI4kCMn>UVt~`U+1S;SAnf2Rr zDGY4U+!3&#M4JJ+r0KMZLq_j!*L)c%cm#uRKq79uq?O2;gVap`FFhzV2k8;+p&Q&%~Pg8PL0qny1y0@`*JAVh4V+Oul@j*40MBr62Dow zs(2M8qZ%<2atrV$(`-{y$8s4YQVM7`chH`9Xcw>;(`w&r0$pjtM`NRiMmnMw7(H^=L0+3zQioZ9dMt6>9!9KjD9 zdLMtyFQao)`yy+{U_?M1ms5tKrYS+))SqaAoUQXw)5gs6zJb>nnyG@mvZM2ddlnkz zV909PV}$aodxx|vL7%{=rgr{mMhD+ASB z?1Oe<x%1!k`rUyjhTnK{E7 z(9=SJozsv#%zqSDC*2pO)Vc4!^z3@(*ej7%I$Fg^b+vPE%y2c)$&acFFU$EBmiypc zT7K5-re)6$e(649CT9x9j3C|xDiVKlxc*#R<^FM8;6RvOj~rRDZT@N2!{De{OHMXr zr7bX>GH09e`BM@^uILEfefI#r>9RVrqfL@IX}oR$e5fqbZ!)L9RK(91adHkw8eNcr z=}C>eHLV6_tCbd|7~^XVL6k8!S!`*~0GQ_o>z$f%%PbxR1Z&V4~!lxTuxPg0y0h&lS1cIx`ziJ{e-a;{BbDV>*n223Lq)2NE)*!$DjcFp)9H~eXfj!NP!v_7-? zM(byR!58A+$$I0H(7yC=`lR_uj28Qs;0DWLpImH0oh7#4TAb!le*h|#XTR+I@jDH2 zX8yNUw95LR*o520x@l<}7Y2W+%!BVo8`@EZa1=E4b;>_Z*5#K;L(RPF(sS%fV(U_9 zakwfP+{I=6940D=70`U4EjPhaf|xdB;1wk=*lltL3M2GyE9)CT%Kcq%Ol#1D(duEj z6Lcy#5i}Nk?$p_mlKKSeNq7h!ddLul$hM5}|H4Gj6cAAx8U&sAIUyUC<;Z)Wg-SX_ z)CeEY)QN*?H}-+!c?wRpH&yJ2VeE{vz-J0V1PgO@2*v(?9tG(>D#EemP%XcvrF&_a z>e1SfR#+2;1S^Its%6oWv3skQIO9>VC{bu1BzO|V)OHkUNw=G+^t{$d$7yi8KS<{D z8f>G1N>k4_bNL%aoU~okPa)o^(u)mkwulf>4T`m0`Lu4z?%&Yx=W$;P5aZpCuz;@D zYr{kLG4JXEddXUHWH`Wx#|0)Po*WV@lCYkSTOA!~4+V~ zb0qztO`w}U)s-A!S){}~>j>Ou;jMrbblWddK|W^Kqp2F6D*`~FaLM-*hT=B(dUFB? zqzU+|8|vyJPqmFH1s*+6dt0S4%u*z08n$BjVi$$c$qGNYpX+deJXZ7Ln1o5L;7G9o z9cb-F35=P}GOGErsY79hkveI6n9l@}6fx7@wyEaoK*dAY5Hu?~Lb6w*S9to(HR)GY ziHApV_Aj$EjMV(l{hOB_z+ZKnj6z3g+O1Q?eoQi3v~)9{T_toO@$RX|{km0$YC}Jc z($!b!W-7;^n>C}uSh#?Qilp$Q&^8gTT-IFPFiWw)*Wa?rb)8!yX?O}-!PDuwVYw!)EEh#1OhC9Bt6dB&leMuD2 z+=CzR(BSY!wo-c|8JZDE#I;VJvnIE{(eHa9@^*XYnq|4j+a(NuuTptcS=Fx-j!k+o z=mo4TZn_56pR73>n{}hM&hiblxRihueWIR7YadbsuiQY`iat{pagz#uQR)1#?x5-Y z%7}bP!i)Jk$3PNt;2{;WbRau_{Q{7ivd?EuJXvjcHJfJ*@WyRb|4H`w`d^OA&fq0! z`&F|fc<LsCJzDe?4WCys+1r8XYI{{_m*zg9C6aQ?vR z#l-WB>eAD`?7a)TXmoDMq-80tb9F;T72?MY|J$SwDh}DqrS;RvoPc%GNjup;wRvVu zY6KzqCWFKbe_{;*+0r{)w5fv!guTU4FVy){Q!h9{CJ>FDQp_6+V-f$_PncBWOhpeP z(F~$xc}7+yW^_~R+nl)p6Q1@rjQEL5-Y`lnxhxxeTCD-oRkugo{VJ|f->xX<@t_~V zf6-J-T*1C)YeMidzpun=n^;oS@DQ~i@rradCcRGK66+G*avfPZoJaLi2}xaG^TMgO zu<-F{JOW``VE@XMsGEn+;a%~m* z{-bpVy=s;lHv=4n`}^8g++m(9l+25q&0g9bdzJV$8~UYBTQQLqqNpf3rM7mtJyNr4 z4Ocz9ZA~S*`7v|+7PAsRJ<`P|>*z;p#B0vHhfKWcc~9&d3*%#>=UhMHAN;KY^pmyq z&7JXBzOpcS?KEC+zh=FYCYZ>^pPKvZ$7$DlJ158%_O`gtq9}1;D+L51)-0@SYKYBO zG1&4i=VjT)V4EPz#T%S33nNcKl!Rw{&x+QK3{!U}wYMFhhaBu)>UVhdL*9kTc`U>X zLzYD47a)(s<^^5Z&JC=7ETDK3zxlF_;am%zo}pNsz~Sn*k*3xvq)*!ucz#~5mBtE% z=;e3LqgCK#=1Qo|vM@P7!(_&&Tbl~E6i;e@d|;99b{9(sOQmJ=YbPEv;y|{|aIxr% z*eelnv%LWYovleWN@q-&6jpGd$*S}w$8PChZ!Fy`Mi92u{SZinVS2xr&Ux1C+b$hy z4%}5SE$^S@P*AdVNP7GE1r5UEe-QSTQEh(Rx;GRkg%&MP+$mOQai>VJP~3`_;u74g zI1~vG5-6_0f)oo*ad!zti)(S$o;+Kg_rLde&p6+bjFF6SCoAiobFS<9U9v^we+1SF z_LW`h>+=>3XaI`XwB_>)^#TPM6~Z!2)1A>c7x;lqqv1S;-vBHO>-{Vz4f*@j5+N{f zMH4dZy6n-?TQz3Y`-W(%uG*Y%>2QIYs?|=$EAiIe%W)cMdshIzU^Q*zcACiI`V0IS6+o}Coq)>`h(UXoqj5(-C5=Qjlywb|iGmvZ6k9O@#AVB$+a z7F@|6JZHM;5bIysn%B->P5=D%aXFx$op~v?CRA1H=e9bD6KLO8(@T1gk#_qdIxtRv*?|mv@BG1avxk|m!^>3j@8Sb z_~biX%r;;sbS=J>3|I9qRJ~!GQ64VJRW6~*$KA}J9ZWO0h{0n(Y>FJ)Zs{gLKotTS zz1b3GT#Mw32|+l3h`p$Tj`FYpxC2Z5WN1sm5A`z0O`M1JMrwbxpyUoK&>&PzHW6zX zyzXcUKvO#dtmF>WGT+91YG$9tP~S;+4_}9&R3h87EK20e@wx#Xsvp(UDUgsl@ODS> z1?D=`24U!*YTYziTcI2UtOrCJil3|`#p~MX2y_pQpf;^cVDj|(5P&d?1t9#Sc4SYP zdXBsErFp}tH+!AOR!RC{QS(PDxi-eTJkBC__L&5i*5S~;(o*07j`B3uX7uWXqJ3DYnjdGcXFumkrH!U4D z1j>r!7z6)F`GroWwwecqs!In(B zn#}XK6RDdc0!c}h zsY>kE1#8V`Ynb4bz?arGnfX(UpE|klLy8ly+Zj-OX7J@njwtPsf9+iAuH%)ky216S zsj~Te9tWPwf=L`mJKew0D^g+J@Qd*A`-OIetJ2)eg2W=n{x2t%LEqweO&kGgA7Mdd zY@~0Tp73VXWZb}+KK4`_ZH^w5-U9nAJChl6Pbb*WKF2Mx>u>M(>7c($M+`OZF^=UY zFDUeQZj0d28BjfcFQH%sbTR;VeC%_b?2N>EP1<^6ul7Cj4}pQbtTW--;zoECiO+L;vEeFRnMJyU`wK z$SoGHgw!MZC4YGg8hYV)xb00X)WQ?mC9+}udc{1xIF^W0nY;vMSulfi^j-)v^ z$OSSFX!OgfRu^_DNgb|pWU3b(K)CLpwjYVz7b~B$m}gvRQ%Vj`nVafOTn$SNkgnQZW5o74QXcV*&ozua zxT}y}RDZ4tRT73h%2he5lzXx1pE{$haC|MfY>Vj~s*}62#{B78BMcG$_Hk8($=gY+ z5h)}SHP$0S&7=LQVb8D1v%#D`3uJ+v{%P+Rpi6KS=v(bR&35~B30VBcC0(smuV2}4 zqA>@GNYQB#@$MMYJeie}zlfe+(Cvnn5f9<*(+EIDcjS)%{m0+>tag9XgmYqK8qsdG z+H6(K-MjJlotb5AdnfhC33ptC5LA?mxw^5%)y4r~ziHkGnKdnZ+k!?csIwVQXdY2cpgI5A1o2s!FSB9R7-@H)z+Bp8*MrN9{NhhhLAl8hv z0{*_DQubkRIy-I80(xDdf)8@fNIUsz$S5=gXKa8$Iy21FvP( z9AMj!W3KF;140-KRjL_2lX4(;q*!TRea!xC>iK++5Gv#OCL&X5k%N_qsk8+F8qKIN zIY7jOM;sLw2|O?SkWx_r>qfvJD{#MflKYDit0M>5=VqBNi;0KAND-M**MKMy%(zDZ z6T~Z9jnSLibPh!Hx6d^m9^VH%-YVK81+`>2p&QWnaTq^#@6iSfJhxt@lu5~)Oav=r z#70jPx(mrajhN=3yYKvqwm+Nyw$~jZlr#`F8-%GXo_1svhCyT53%HZkwzfIPGA z8G5F+Fs?9`x=jTi3uk8hn%Vv0wubqhfjaEZVuF07?kGLJjoD{{XS6kahNQ4o@g$G?RbU`C*UtyzNIebx4Omy z{4>9rxW|}$cPuOf{;6*~ZcHv2JVewF+W2;3qomtR>N12gu$8}<9Y>7kCx6x_m4|Uj zYtCKW$nR20bG`X8Iy1j9+UXu)vf}&&<&{^DaZtHeV1~)JQwOtA35-sI^@;}S3v!tk zydVrTP=}TTXQ6`@0z6a=bl%Kea62So`Ee8xNsj(WU6`J2eaeJuLAxs)>FHIhzQxlL zCv&VTp=woc$eZXuIsbvE@K86V#+hVCyEzG5J>Y^CM3VPZ`{k*fgCtF_i!1mmk7g;@ zQw~PVYy)&xU$3F5x{RZz_rvo2m#L@?z8T2aN;=DtVSO0)C@%9@>2%-6p$|)wao;RH z4E-MW@)YaC!^F?NDASpPzi8h#)oKgqTi;q~jFCHJnU0gLyN_u~TIWpQG!8EHDz;M# z`*QMP4SZ~whxjvRgzLf8HOmT^LubRnguZcP5&3bSHY#Ac*pdI)`D~-F=6DVT?fjY3 z%Jmto-QD3iYw%cgO}qFo&I{pf&|vVHOBsdWVf731)g;8yu%MEG zcab^0aWj@z^s4!jH&!ka+W@AHTAc#8`NK{%`g6Qo#W8){{s&STwvuVZtd{8=<^DTu z!E6UE7>km)4A_;RqADX|P25C4V}X12xspaO%g(9-3lkRe&7`;g%-h-n99z{*B%7&u z)it%xq}q6Z&P*15fg8v=1H||i^Z|zJD{@Ryi$7{UHyD%Nnw1(>mKah?9Db)jXGVGh z{AqA_^HE{E4#a{2ui_WO1ub=h3TY7@pVOJV6z4lqh7NvGI_Y)Fm^1r& zxt$^Dk$P;{o+q%iH~3TsypR9}nKHqRHY#+a`q6o+`ucX~544N4n_+fG_IOND5kxH{ z+icsgl_fg5&U?wLaCYuH2pjLsZf(otS62}SWRsQFXHljP9!3kkY#5uEYXyW!w%`5o zo(uLtyw78+CqWYQx`@rZ11*SGKFf1)-i%NU<%m(**ef-g?+ERrmFo{g`=pfJQ}CHe z;rB7XfRQU}wK%q+0cL&UF=~q|ep8VhqFc@r=g*Bj>So0kPcvqRc^M}upBEE9Pn#HWI;l|g0k;IaULvSRZzBur<-pSd%c$Wqi!(&fX$yD!WE zpb+fOpx_($raBWo7jM^x`%edt<+kNb%Uu%Ec%yhtffE}fbWU%x3TzC>g0#Y!L73E` z+NEE3YK=<)$|Kd?HfDC#ejgd>$M9oliO+8h$w^~50qVl zGm298Du=^~iSu~o^dpkG`+k4rc|(eQ_%`v=X!zF;Vd5hLThsGinqdrVQWUJ+XsoHM zDaKNQ|2@(-CRy`JY+BQ-+9n;S`t8Zr?<)qkou-D_XaK8wYzNS%d&y=_TGf&R6~t^IDResP+Hme*YqNrfdJHRfM5-&O%Y zsf$KEMo7N3+pQbm(b8+X^!*9T{^|BtgUp+~_)w{vMsvO z&zRx*mCeprdszlHp~zuE0hD#^y2VWoFKz$Hm7`Tap7~2Nao))9pA2;NX#(vY&5gOQ zII96r8b0jiMDF{}vTh)K>=pvlpnTqtm;JAIjpZ{keS8IVH8_OSFulk-1trfqZy zhHDWFTCva9X7Bw)dvW2ih#jtG-|-~HYuySbehJ*@)A(yF^id%R3~pu}#YB(Vbc4xf z$@MX|?Bd@riMm}o1VrtwY~o)J33AO(i0EEI`hhpLGDgfyovSU<$0Ftjae=$uvF`)I zi7vYmbG&>&^VcvI;wFA-PZ%|}mC7f-d_O-7@fs<)N`ti3QqpIIg8)WR6We#|CDZ_t z_|;4{KvH-X8a}($+Z(BTbD1;MN)vF;yK~lhMn$tG1bA2I0 z4XeEG3)GVV$d-L17>zNwWZAS#@!{sB-*?G`0ty_XZ@AKHxXCc*_Y>>UznJ8Cy$6~W zGY=HGa=vXiE{LrKEI>CZr5%3+>OC z5J44!sVu5?oFAVgju-Mx;_P!Q%U}=d9Kh`t7k5pDfi@Zgwm*IktlpGX&uhK;=9$X! zo*+^m_wn131~xMBY$p5A-9CS=wc*|!%g_7K_sImRA7cOS<_P_C%_|{yo-?t$5|7}p zL8frEAw?%S#pO6@ zMcx&Uz0lDtcKMmZXt&2j!{lOsjfRp2leZ%^t+;nu$z@zkt6a@~jEe_}RG2vLaI)$h z_hlFyBWfQTds@1p`|3T=leV?03fQCw%+w;Kis#J_Wnri5%&&j%fX$RurjuIAe*xE? zvoLTPpHIa)2^XyDzhGxg(-w_dW5bufBAx$Gp0iY8FZqHT3x{sfaLC=EQVMuFF>4gyy@00}IYkGBt z4NNHUt1fC@G^Xs&C6_Ie$(h!L1#@*zjNVbRy8wVAJ@f?|Y;;1|;poG(TihwCwgEo1 zhIvwE-v@2ODekF@eYqy#K1JlO01R6#Hc&(oQ83!9l60b zL12p zbx`tuu{bsEKK@d^lbuyverr&n7eyqnTf(9=z}d3s={_%qm@4U=>{W=H=$? zais0|5@6sg#%cYOZ^fTpg8U3Q^4hLKsi>beBiZHXgwTA=2&o-U+On5g!&>V5yU7 zl8^8-9op526fn9p6V*~%n@&nubiSOUtdTBhtZ!-fL{o>5;76}l6HAOCDdS?&y~l7< zz+ShTv3B3eOg?@sgOp=@LV%#H*4g}U4gY}ujESd z(5=^1FH65PWWNx8e?D`|dHA}l7K?hsjG3CSrbz)&x8_h+4sI6wW_HfniEdtzdwZuW zH$zs$R8l>hxXOP`p=I}gok+^*q&=6V0pM02yu%n6n=-Xc$5~oSNz;&sE}z-0V@rGs z6kv8GJ^H9t`_UymMvc`&j-j&>J=m(njfvIE@&;x{cGjp3yU>Md zAfbg}dQEcDo1>DINvUI1p4RPt$tmL&L7r(lE-N<|;b&8!#aW6aLW=6x!P_m^=zq%V z;9AC9`&hgrXXcUekJ=In-^-mmTNlpszG*mbAix1no49cSlL{;9cv`chka z#m`5lDM)kV5fES=kr&CM4z4%x`=@$pkJGfq!X}5)B~BlW4l{+d(@SmJB~qU{wu&idkD9R+ zWWmcnaIwMwo7bEt8;S8)qnNkaS*EXBwV2UfM3~y#3iZ{Aw4bjM z(ml2nX@Dhh;&pTmao5E7yxQd-b;j_?nj`%HJOpX(-1N`XBjAj#UkG}f^Jg}cO>eF9 z)gJ+A8!8L?-XC=f+c_0)BE6^q+M0?T`=k~|2(%r2~PzidB$u>k3{Fodxq+S#8N zhE`7L()_RDx1^k;q^H37%hDX~?V_N|6}RbT#ba84BbD%7%G=Y~uPR2`rD2i8_5q2E zf6;0iDZMO{y)+yVLEZadk;p>go3$L>vg@4SrUadY3Q|7@_-9IcIj+>JHzr4lXy=oJ zqGZtd1%L1OSS1N_Qs+Li?;}t1YZp{PRQHI9c~zC#517-=b4ZO3!dowFN&S_Z;{T$Z z07$=RKX+*IbRdi`Z@$ zWk_ES7!`1_B86unb>MsHmW@)H2Tq-Vj8PwLadsxxEm>IDJ`Jk!;-gEljfx;w54>o_ zSOzp+1dc+_3;b+wpmGSXCjY#);;t4uDAprs63yiSoWEC29p@ov=z|c#D!6y8ehf%& zxY^gBNxv*hRPpQjs}F(&zxBTace>disN-0 z5nuCbFV@DtUC}7uPj9m_A`tpJP-IezP)+uG$|o93`oAcY*c%w-Me|wItdR+Fnzv*6 zvRA$SDIt^hDa435nKq&vNC7>V0Lz=f==x=saG6BT9_D;T;?w!lEQ2hUH%$t}zHeg6X&-mqRhZACcjxnP+*NgT$& zz^+Iggb_pvn-Xp7D%o6O`o+zq>N>w;rDQ`4VGEHbjpa;S!=c7Q@8H@ODQ-e!%BQY< z0IoJO%#x_*@vO03i}UsCS=?n?cKcZ&!{h?+A@9P7ld|a10;3c5m$G+Ty_|UJNb8D_ z#G2C(yxF<4TyxYZXTQYfS@r>Qjk`?Gpe{$rKL;7T%enuu;~;HG_jmU5 z;^bCJm;Hmsg`XJM=Su^VEHShZ|V_UP#b*OWB+XB8| zF5zFaWB0FFo3;!g1s1x;+go}iNEXk`ZE75~cf!u)aK(gu)tr8pYWXKqq0I+nTzqkp z&!~QF*^4V``{3d%F<RJwqD#J`ZWhDl*fFdqO|gxgT%G^jP?pq^9CeI#uz%B3nmz9^L-}p% zb^YfQAQVjF(!C&5Yr5^^`;ELI1SsxfL*?YlcA6`m+H1ziS$F^0CoBG9VX{80 zMwVfNAAI3NSTSDa+kgZu@bZr|w-tk6D9MuyZqks%qzG+W>2{26ZBw@`7Yfs;Iw}9e zcs<@!v__EBN2s+}#lOBm!-^~6J$~rT-je8#j2URE@ni;>Pkyfbf6Q_(OgT?Tci>x* z6nFVw;|YJl7yhF03^tux6cOb*NDxOY)yt!K(1oaIX6^i5?|!>?`4?^2wW5`TFSqu( zf9V|)2n9+ud9QrIwB^f%H?Q2k)CpdjSqaSo2((5$TWdkt5GQ*L3|HRq&87{>brfD> z#omm#5G`l^e)3^JCEyLaLhtHsIlo`1B~?#HU%cpr&Qchw*!qG#@&h-d(#>H~H^YgI zPA8z`vzq|5n>ufHy&A!;Zt^O8oj*@hN+(MSDfkU5GHFfq6*~^~zP{iQMUbnrebI5LUAv#XA`QvxhyKwd~W-zCo?#7QUmhNh9$~t_};u9m9*TOe&^|hGww@vFs zqj^|T^)iD>gW4i*+(?ECA0Ry1DujXp|-8sH#>3i`F;M6hb%X}%7(dlQL8c@YDS?-j8HSbn(d{wZ_$ zVxHnAwx@7dqC*nf^|JJcxYTn2ShJ9k$o&GxRiou+sFVTp7vI8+`hs9xCV_f2Jl%K< z&j&CWR%|XFg_+f&C!aS&gR@H7>A(9hQ`EGRZaE?~QqR80{F&4Zqo$`MXVsblo-kN8 zUAM}~E8^zi_NRiIxh@}O0}%~Rj0NFh>WNdlv}~w1{u-cvoZ-BYZR~z}&Qkc>K5Y-^ zBtny{yBmv=kP1nQ<%%^hKl*M{75G^4`ij}dxeswH0%2wa+ro4TIX-*Hr$pfwiY^y3 z_LlbkDn7D*(Jm+?$0np$@Bt3y6Q(@#${N%@HcIRmeX7#Tr{`yq`SQlvAzoil#<~G{ zH25~}Vq>ej{(eG(F0E>oLUCZ1(lnKGY?2cIDVg49vYE~h z<~^>lGnCtuCSqn$^FS0pIDh%v!W{|;Tf!1|dfY%H1YsGz!pO&g(!)zA+HlC7)Mx%1 zPDNTr5mG>5Ji>j>ioEs|%x(R}G3{6jSJn4^hO!1o%d`*RcFBlo?Se_~67BEPNqk%x z4R&>C#JkK^(+8S;I=|FmpDIx+iy?wo&sfzvBg!|97!=A`vA4T`@VTGeZ&8gUW{ zRsr~%a`A@QR8<0!l?&P?q`R49bz=+Xpg2V}P1 zj{lYW;t&j!%$#Yq(8sI}Io+%|eY;9QE#L{$CR5^O-y0j87h^ zTrhU+0XY<0+8|Dka-X}4y<9?qko!BX*=8-Zs`Tg{*f;*o8AX42D;vJntZ&;5qs1r6 z&02$v9@N@XaR-XG$=N!Cp~sscJV^_@WXPw_j++`V z+Aq@fsWMCPCbFVeNmKF8mR!(fx61+U@)=54f>EDAKPGMP&1U91XNxOamv9yQl4+&yXHZ2as%5Z7vtFI#X~#E)&V2mE6}z4v zHt6y|IcmqpMG$I>vdCeiWKI~$P1h`C^1hL0mXtP}T;^t5K@-PBOKeA}-)pD{nqD(i zS2o3Mz;Kq?A|~}|f8&@+xPM1;B_*5ptKiG6WUhyS5o+ zva0P)bNgl{RObMcb_S>Y&@}&|RTYdKg0na@p~G36bE8rui<~XGiEKk~l^!r!3KRO3bkT zS|eQYg*EZAOUt{R+rUI4m@&p7r_c0(C{5CYmb06Bwg&UQz9(u;_39ViDFZR(@{tasYRXKm+$Odbf0a%&nNn^P)6g zP9!#kxqxU0=PS+){KtQz$aOKN9)ATtV4O#fmHUrfZv?)0U<``3BNc@Ys?&KOy66+a ziIDarZ&+_;$6zyoX}2?u?p{HD`f8K*A1%^apDnW$3EA>Fnqu7aeI>7)GJUP^@E;Eh zLK$>UO>}_8wiUqG(7$LJzkA{5vuW9ST4V9Dr4IsdZaCtn(9DAVG*20zwnM9WdD&-{ zm}tPRe^gG1(xt!j*#Gqy|6_DTt`T!5Cr`#AXFIDqU196U>4xe@m_Ot(Is%3R(EBi6 zmRB|;ouYm!lrBPJUZ%Gs4|Fx*metUEMLeg`Cmx(#STLgjC(~;C0)f5wPr5w1$=tod zU$kfTW(IffqfnkWJO|Da*_sc;Yi^GV(B|6fuhS@IqsU-a3~nXI+c?* zHxw}Ez_g_*`k~uVD8Q>rvK7|^ukR^rY7EEulpy1>R=yIB4)XXJtxL=oo7k;w{OvXo zr(?p)A78ektnaO8uu1_Qfj6LO5Flr_?PmEjuDj1X*NDpDZP{UL8e9MgiF5YQELTAc z$AW9ogMN;rC8+3@_)HeohOaPRaC-ijrPC>He^mNvX&Mu?xQCC_pJg7u?3>d3>9&hm*J|EfuB1Pk7lRU#Hz4 z@~~~UXOIeVwa__Z>b`#*vyV>A;%J<61-B2pC}h^HAD|WrAgSVbHsVttqA0S9UQCVi~-M5fqUSR)o$*l}~S7tOuRs ziOr|69zNKa8zfFHa&Z%(K(DrJgM|pk9-{wQKSSpEw@S8anx+!fnN!nJbxribi(*sR zaFe*TR%L}!JPt9OLsQPT5mgs#JTzBAXd#Ah> zCmdg}pctma)QO#4SUKM7nkq{a`m^pyr;Ideqe+j?iXgGfRd7)>@rGn$-lBYsx-q3j z9JVLnhad@ss1gisEjcCnkgxZ*_C*xzRv@E%Ht}K7h3TNoE!`&(nt({6c{YX&XPf&f zeHgl}>a9!=HAFIW#MMBcfkc$ zN-YipYIG%Bau((+?08G3wilc<7?F2ho5JxpGZYl>&Tctzb$zh2>3)~U7+o?CG6=8! zY#!%@iW+{f;8MT=oP%r*<*-;JLP-xb_`C zTUh^rw8C@3!C<;>_mZTJSQ{n)R?`>fI1o2(nuBgCXQ%hk4J<`>Pr zjP`lGW<<-n!%AWBh^b$QjvD2?_5A6}wyYVb@}h9;acBG-_vSG99@^w5U$5}XijI%P zq#*G2-Xkhp5)*3SI`}>y&`UW$BkfXe2ZFXrlb!Hh&YGtzK4Our|F;r#JZ`^jH-nN# z5y?=AI9XCG<=IPBVIH~@r3cdE`m1}ZbXtob$>^@;jhFRLKLVTf&PbR=3$1H~z0IFr z#*gBul#5=D@w4z6X$ygPQ>Iq7D{-lLwuMXeIT@sK9J6^UbvK-dX{GeC;M$#2S)QyB zj4q<=P=)W~o?^u=xEqLRlB9jo(+wGgH_^EV_3R1HbgKO(Zm*tzcF#cyb6R4m6EJxh zs^$-eK_V>^I2w;*Z!>g6R<>y?ETh&KxDg*t`h#3o?Bf5e|20Aa^{iVmf1PSZ`naok zKbH)i-5b#o`}h10?kmX)ssS1c3v*Aj(^?dC>9}BjV`RI##n3JD=iVA-*Fb)Bc?S6yRv=- z+H4V+>~zoC%$QXBaQznz(5&elFWe!d)&O1v?aa(N&$(*7sG%qbjSVL)kwvOU@8Kz# zA?ytKV{}fbwCE+@Au>=biFXPb)N(R{Le;&3UoLY#aKcSew zjVrD>?B$ei(-`qv=8=s-_sW0DcM=%-9STyY)z_Q651$ZXpKDeMnE9owWpcslK$Azz zuQ@eRXhPbsHlL8S`07$A9*EHBOs6#sSWZ(0YU8OOvI3)_SwL?+`LH=OKDfnB{z6vVwP@U9y6#uz=;9!gj&=rjCZM*%&E5&}1k5)DF>NuB3@Z`yr zIm)fA!J^YkE_|2_@gpuj%4-YT*|1>$bg-zmQ;zFF41Sj&_2$^m-m4>c zy8Ja~_F4{L$655vb;;I6fkW#BTh>%l8dN(4IsB(*nH{y31^+V@;7RL$x$NpL|BJ(} zrx|G##nP<_DN*@X>tUj4!AFtzC&kb1SV)#~Rb^x#_ zS3xQ5Vi=X^Z0d&4`2|UAtJzvYySIB7_tN(op|WXO)SPEg0i__5gK?-9rO?1GA)MbY zeZ1uJk;#6c2Scugue?XTX}#u_9Rw`+C-^YGKxCmaO&Kc}n0wJes(X7y$v@VEh6NLE z;0A89!%CkPG@&nN?WZSS63@aY9YqUCabD>uvWEr7Lh{p+-MZkBWq>8rx(4lu@g+Emr(NzolWg}iU{#QtJSk|a&uB0y^meOIJMp;i{3^#(!W2|C zLIndhsm?BXc5^Aa7&Cw4&fG3&@F}<3XWc4a`&ZkZ-esrC!wB+?4_hYEB z;Jo}OS0}40Un`5)LMZ(Owcg}*_g}P%B?ot44?F`;`KNtduHXXMxy$04;z;%NXS=x3 zt}sp?i)~a0m|knuuiH>L(^;-&xhmj$Cx;;-;-HL38Hgx9Dc(W1B|ACuOQc;=d*{6} zoJ!1@=};~9Yo;7|WA#w5k`MVl#kM}3*4@vaDO9dQem_hu{WrVoZ zQti~Zg_m}F%nt&s$m9;0dtFhM<>>}-&(iCId9$O24b_E8as_dJa88EpNv5ZG^XdH?- z)WnN9JNl2cdh2$g+#JRLYTyskrseDy4~Q(b&v*UzCg5jn*Npg7g1 zkRo+(W_N##{pD;Oivso+L)#C?=*{R=V5jV**52X4IdAIv(AqS+vww7%yp1 zZAV&)t%^XRpEJpwyIzzW{cU@6PFd_f(-qL)X<$b}7ET??GZDRmdFJ>xH9;54e ztde;S*LuTCLR`C53!tbT#^q^PBN0BkSR@XvI+J%Xm@hasD>uoo zh1!%@Aj&=%m}yuj`KSK$eSi;KXBYZ)WS(g>Bha__^={-sM3OOTDBX$jeiQl&Gt9J=+qb{;g!i(7G8 zHF4Tbe9ZEe>8W7tH48==DwKNtyFoCfhdEOUEh(+Jxo^qTah%L&f?F{9Uo?FCGVS~G z(}W-~nQ_jcrq|`4McCyi)*JTR;)N)ag&TYH$un8FS?TS{GZ*>01ud3dFFLd!u|Bu5L_G_3l0dWb>vx_sE*CXY!5V@?{WD0|d1-9sJKZv7*AAjYO;usy~x;g~fY2qmyt)eC?td;;17);a)7bfcnqHD|{+ zr5_TUpGcv(K54w*pZ|-N;k0E`l$YH$sEIO(bZ`C3oC@xmmy`Q9h{5=^ zOjF-Wqq%>hc5LRjt0{>IXTjaoqQNH{B%Pn{Be@mvYg-^XPe$GIQ?3-8l31u&aDO?Oy z>rWk($3z1)z_?pRxLzqVK{y8KYKD<$tTiI*v6O~7_BaN_IIaY4zcvOH07EZ{;o(S4 zHNp*OiP@Xbu8uW@o>lc}6vLIzk}B@IUF4fHfsdaIH23`*IPTcHTPYytK-La)mKi$C zede}Ueb=&bH-j?Q8yZ! zCMPuq{V2#G#N_!M}0kMC00?CFogya z$5utnR?j#Z21ZaZ_f+7Jjp;t>gl)jL$b*?!rN9hz-HpNjH88p{jjEJG?+yINIb2@C zz@fb4$$L4#KTQ|rhTLSm2ZWQT6y)w7K{>D@`Y;e}+L?C~2uI&&&y2osX(>LFDZpxo z>~>*%5$xzf`rPMj#4SHG*YMYphzL-!?6&P7rQ!H|W^(de$e3^0a`M#l+2T(9k0)NL zZf#{5m!2+n@sh|zPq3wIzv8Honz*n1^dgtBX0G*dR4H!{;6dI5L?*b;Wa~VuUZKfd z-1zC%&;4|$725;SFf<~Ff?}+w87N_~FjI!MA@tZ)S#lO|O?;WzuGz<;aRxS#Zkp)M z2#c+cOeo!H{FEt2s_px`X*I2HWloy@Uq+J2X!*VOmCr=M_wY2cMiMSWx}mYKi7!73 zT4)nbLgKXd_Jm<-(@DvsV}BsB3fNdjLGX_G+QuaeQ*iB7V^@SU7OPm_NStyKml@h? zZNB)sZboH)31tu_{|jAEzX`X%CZwakCtxq6}< zxDX$vX3#962N^MUg%bD9M}LUX9EwWtE}y~1o%p%Xq@5$)jidqHIDmHSWRGu2vniC- zLhnk|(+jh)C9mhd(*E8ph4OfOK0sw$;ir!oQ%;ovOJ?4^O?-nBeMdYcurKDe~uK|EkKSu-cl@nMs44W4;Sy2X7R)%{d>y|K}v^TpkAoR(^SHRfTUBS0~N9=s^V{J$yung=Byh9o^j7lKRKm^{trjA(8LYH~N^&Y^KLM z(?SGK<*mN01j}eY)aSQNpfAWkz7#c;LtU4FWcKOX-iP*>&=cOWrfhCH1g5oAschwGxDyZ5WZ%+DGf4Tw zi|+9bnWwk-4+u~n+83bj6!p3I{YZPIkOlLZ*8 z-e$Fe=-=9VP(%u+t-C2IOP-j(;R^1x<|yDyMW1 zu10Med$wnHRFQ<8-t12BJNiK1nEibMe_s#JUr9zGFk!MHWi!J8v`x$>`1lH@dK{OT zwGwPyB32g`KGtBrR_ujUZC%HzDn;j#C22bmk;RX_S16w~E_73Flk9h*1P2eiSJ%YS z*mchG0!NF{xVT7!L%CF}3x2>WL6@VsR4p_JhC>G+=p!%a;gU7+d1Y>;wKto7wJ7-* z;`eT4Qb`%+L)O2ZHT>Q!E`sq?>&60iK1E>TW^Nqq*&p08cHuGkl7ri*O;}8YIDYp= z(sKGGn)7#xQAsJ@z=>MripEgy<-(yUw3mI*In0#%Kka>IRFmtrZfMe^h;$Mb2nYdD z>45|Uqy{O5P5?!Ugb*O~CL)3;(jg!nl+fFvg(kftAWeGjgd$247R#G6&faUg&)E0e zJI=j7?zs2dGUZF`lv5u z#_;e?3SkZo=sqaU8nmqXYxlG@;WP*-AC24XmLCOrP%F<#)0o2c zvG~YZ|FS=xQ+xggiV*F*Xe)5J=Ug2#off_jllQSSXh=ByS9z!xlZC#;D}$h zC~qUqp{XX>9~WLn;)N_dHc0JU8kti)^YR2k(E#^>HnOH{aRETW^k%7#ufNBikq1sc z0VX9yMtV`Q`y*E?j&k=`B4(S!<$nS+d}^M*&satcpG!59I-ct0(k0H|vxiYLF=1>i zz6$^L-Nf<{>)sJ|)~i=GuT8;Y&YjxvPi^$uU+w9%M;u%HreB;UrC4;ywm}kQ5kBKu z1V4IT;$--@3!-4G{7I{tHjI?g%%KN@XN9dBnvYAnz=(ve|4t_T+k=&t_H8{%YCnU7 zGinZ+A6JaaYX0`Tv9yy`phpwYvt3H>F^WsV=RANxm~$dNs#be>nX|4Ocs@lmGp_)- zR7Z0*WtLU*OGgotKe<;sW#&wK-uy8G4%=)JN8;-jP zWlS*R+2hiDrtJ9>5U=$|!PpK({vYw0>l)?UhF-xllF{eEB=a`GxE~Mm$Eo#y2*&hB zSxe^;$*S!^$7ExFY72b(p#P6(x?d<9`=Ngik!1k7w-$d-RK|BSt>nr`qR-zWC}k@= z?-L9%ayrDznK672Irw{ozm_qv3U{gC(RjG{w-C~QEu*+QasIJvshIP33`(0q&xTfZ3xQiO^_Is zk_dxqn@6~hPsPC}`bWlb-zVRUieHv&wjR+Xj3!e)`pEht;LqVjy(7c*gNu95;9&E{ zfAOJCh5E&aesat1<%zSFdckJ_vsOsk11kVtH7#xS)QL3eBlJHp^71XKSh8Ts zj%K~0E@pM(2P!(GoU7)eG_4*;r%~ka9(?;7o{})8km&j% za#c7+VvnSqXvVmJajqN~w#<pa_OGos z6gP3a6I(c+QI?Cv@mtfGSTNF5Er*G}I*j~7h>sTEwe+t;Xv}=v%ItKIu#@lE%B6IsBRxNKR^4YPc`H7k`29gca4gS56_rIPg6FX^dYojE5 z>NQo_k07pEsvH>;U>Z@tYP6(Syi!Ak{V847Sw5){ds~S`{etz&^39*M{~iQBm@xn) z!wVFLXH_PUT48Pt3pq167OT*?!91uQHORwLB zbZhQMA+E7DaO*hd23ui$p@j?l0A&EKpEpUfVJut_^#-6uQXfSem9)R~T%)nmM&FOkn_&W$Pb(x=5FxGOh*Mu677@d={+u^;~5e`AIP6 zjsF{)B*YHuj%@+Om`LcMnZ{1V6}f16a;@*977b_nqnE3Oqpqnz4+Qu*gpj73UB1_o z5=%eh1YdN#@_n&QK7lu6K7N%y?k{YzL$vBkbHXt7KhSYs&2h94h|Js){o0Ouxl+gv zlOI2@Cm=H})MoFPrrd&E_9epMT#;rvV1(w2kQ%nfo9)BQnq`j?54L|1w_PcJmVvLXDI$n3H0E-CcJ6_3iVjL@(mw2*i&ob#>@Gpt=KS;FI zUlPxdSor0UTgjt{fRjXO`_a0wU+^!%|LZqfzf20q@!~C;tW5(gfFkDUkYQraKX?K| z<5}V@yT84r=z)0j*L6Ru-kiliVoiD4RG0rMZ(f9UGN@6FKl zr|E}oMwau$!R|t-;I$)+H@ssi`!=B9_~IVb{L?Ex^tQm-u-;*Q1W30^rQir?C4?R4 zUf3tm+@t6Y4kz|XSe$C?wj;6AYAFnsFNg2|@L=x(;~}wIPpZG>JXvR2C?Kz-H_kb- zHqV?lNGIT(T3A?I^7XzzC0UOb(}-#4EhmSZibaQ=XUdlgpMeZ;!^ri3C2~4{#a4+X zh~V{)o#}K#S=q)?jt{;3TJ&5zO&&PB)6w-*iPt81S(ECCM=Ci*-VrWZY4O+|C)>O5 zP0=|DHtR#}el*(#oo9NjFDVIO(JC2?GcWyGbMi&H!(i$(to|WLjV0Jz|3QC5tL7~? z3Q(}OQ9t7gOWo}L36gM-q6c15K%qSq{r*-6odG~k<1S=VdeooF!pGEFhTL@w-`B)G zA)lY>$lpE#e;73$l1{~|8Dm9;bmmb(q4z^uBz%5Xd6yM*Qv*Kl2*%XX0QfCOVz%PU zEI71UN>3OHJ-gbhrq(N0Z?h#Krx5KHVDV+m*aa$>(quS4k|Eek*wXVP*IyQ2uM&E> z{{$Gt#o)$&zg=}aB$ovO^w(hGuD6x39ZAK$+p^R#&g7P3wsPaA0qV1f916b?OixHq zHu4wUW^z*SP(j}8j3&lP-QJyzq8JgCYr|7sTRMtnEeD}g!z`1}80S3q`sc5xg#Pim zOcQl-#?G3{ioXv^p`I|9kDe<1UEooTP^E+ochb_@N&72WTR~auINLIW+Q0*3{O0lJ zH)(D&u)=7)C<-G;k_841_NvZ#AEuCTz=|tW=Zn_XUc)NiKk*Qck}r@>ikVFHlFhj9 z%qaJ?LB0LM9JNNSi0g}3Je6JUcN5w+J}%jzDV!}2j&NKFLhbC^<~>a;OV!;SN(YU(uTW%!i-lE6Pki^dZ;s@PVM83 z(3oc<^rQRMx1-tuH;_9P)^UOdRQ5V%5P6DNAml1p#EXk;3}eT1Z9=u8XZ%h=P#8!` z4iS#HTR}4qa3z~0S@H~E&h@5qhj8QPx98c%f?|AQ*vPNj#DN)E=$+thZ0U^8%-L{d zHX5G;u%qW6leiqFpDk2b8Zna_e5s%7aLCynLv1XJkSrzsazFEy<=Q2=K8h4}?o*K06k<@#i;TDH&4xLUkOnD`ihi(NM zpRKTUbuYzYxp{rnG*5t`w?+GoWWTVm{B9eYtpPJ?a+`4JfgMc( z_t(BPa>&(BpVh*Sm(C^4D4T(KX+%ZAuvUtcf>*CyAENV*fym~vddZ=zrn0$?lTJA{ zcO5u9$r=Fs$`9cL4l$rTt)nU|y;o~Et1#Psue;}J1%6;tJW8Q(J!un z$8$NfH`GAJO43is$LV#WJ_Anw1NA7B(aXRJ{3(_?(kU5~W+B$Cug;Kr)QFKK*>S236eFuOBC4$D){uO-W} zp(c_B@C!Hd2~%eEGSZeHB>`Hj?mn9fWh_lAMM3&uFNhwVn~#3m_2*(06pdxAw}|H6 z)I-&SK3VHvs91$a+du@a3ESfZSu0v_6c;bpO+6Xw+b(~;;4~IXM5yPL|OaUBFvhD*CzqSd|wN$ee zdPfO4#|ROSsd>!1)s6hF#rX(L?d4LqaQm<|RD_mFEKBydcJ^fGI88R-7WBavj%zF& zW3}7Z8a5G^c-)IVzVn^`>3xj(ifHIeQuE}6aDAR-!S6CR6JncWgu2f;ri6ds3wfDx z)M3x$i(3A%X~DhmKGoSlch%}PZ|8X)6(9_dUdiUZc$A{%vIu-EmS!&UhG4$%t7Ag?uMpl8AqIfQRej<$I4K{ z43+e~aO@bmy92W6s@-8kREvZp%+O@tD`G9*EW^zPeU{!>5m;PI1Ks;H2Z*KFT`&!$ z&QWa;vu|{p@Gq%;lcrcVfHC_t`!r3g+TUq&2F!*o6|c$EoR zIm^*V+%ZfL6&O0QhrZF~G}-CXhCyu0N+S%|c%x(d-qt>Q8kmw_=4?^6tD*LEoCju( zrd!Z57Z8RlPxrUTS`6+C7s>1r%hDS(qrB3eszFfdj? zZk3KcGbKt7cvNQA&2byzYxs5R7ReF|&MX<#eRordL3O*b6ZSQz>8K%Zx(`wcsUp zupllIcG!7HEKG_W_m95-lW*oes_4Q9=UMh?VRarMkZ`Fuk`{6gSu61CyUC6{{u7}6 zXB`*89A%*0wU^z&8v%iDtG;_792^5mzp>7~Es8H@Yn5|Nx^lgZf~SYJ{Ve`N6Waj! z?sRlz4N;6fdd5bZAWD(P`kKXjD?MTb@Rd&UDp{oMNC#)BZ1{-(2lbijkE<8=Vy31L zpVU8v)b!fWJv3RK$AYbCAuvVGWkZm}}r)EnWy-d!C>;6dpakzO} zcHDBrTa7^8vEvM3VPR$A>6@Pe2>TSn9O+--2K`qJt-MU@E4zvY`xkIJyNiY* z0m}noqFPHT^9w!v>F%W))4Nr&erU8c>h1ixVRj$7@xws+>Zd^znih;;)uI;VoCg5( z0DO<>SDG9&9Gp#UwKXno=X;_0xOn5bi}oHq&+wnIsBS_!_tkF6j_cyM-k>7#EwN@= z77_7qmbjIdoy9Ty<~)!w?xb6a51m_j)940nDqH1<)N$CWIjvl`14gfEznCm{ASy@B%YN8^ktA-4?ZTkj+sE0~puI`6Zt)LuatlNzJH@brQAgge3KS>bd7$WwCE-P(~& z2RdE$_h7$NfXurku)^vAueY+3Gn6DLrX(=*7# zzGc8rM$-T@{pAo>0^7Q8=zhjT?%{9<69q8}BRYTMlprpXcN>w4bE;GKHe>2Up`#6e z{NnNHUW|g*I}Zy7i5iy2-UYu~%IH+s&g@n~D;1l$zij&}ADXO2zNY3&BQHE=4z|Ug}&hJc}0Kt44OHN%o@E+t`M1Z=J&~YZCqwus1O{knt}m=D%oZ zlk=zQJ7Thp<1;uPq#?06Cwmvm*7+)Nz@A~hIB?B{RL6ta{|H!u*(xA+ep2MLc z@qGt2X*BIwyW|xUchW#e1xneJTS|S00a_%ZyFQCAgC7j&5FORAWXa4pAx`KfctvWm z;}y7So}-}JgFA}0VP@m@9x04bK^dR)1s>WO^*zxBRtG$uh=G$Qnnc!Zi3byTfW$~- zsP|qK$o|VaL!JK5H6kKx%)XhNUG|O)63@qx{9{RP*EcvT`fIuuA87TfCEFHO$M&a- zfwkt_jk41V+~;9V{S*E{Hw!9Toa*I;?q*TlxdFvju4wV)#`Sn^;)1r(SFNP%W1I+$ zV7Jv={={C-I;1jylH7Mc7(z!bQ!Sp+Gye5f_lS(Q*vF`Vllr(cx@+C!a5@Ni2uoZv zFx*7tq_T6I9b#BELo}3;c^#K-?!v0{B9~vlPfv`p5Z>-<3&1YvUy~_MM&2J3&o{u4 zv>6LUx#+0h2~-AemZon%3))Y6vWNFl%&!wF?7M5~MZou=F=$b$O~ZsHcaJVo84?$d z+tKMR7H@(ZulM-Yb8Op19*DduX!?MHKJMnnwe~|dhKY~`$_U!(sl3gM+`zdJ1}h% z>g$`*)O4tiFZMVL73W#RGf(-~*>0<0j*2_qUis3`o8}u zaoOH%v0}aZ9piJe!OVlpkVyd2Z*0IwT(wnd?l{svD&hJSSdFjIc(U}JUUE9x zT~1DA4iTRaTFiHqiE<(XP9A-#Z1x28F<8#7GC%t*!&zk2$IeYW@3lR_waignT?drT zP=U+*Z%BA;*&@T5w)?Fsla1oz!0b8`Up^qH6%q@(rcbez%br~sG zt#|?*s~P=WJaQA;87)9v;@3#VGkZ(ioO?`ySTi)QaP(CRl{aN5!3j9VBF6Q!1v4Q4 zvd$EB5xOeXEk$*_tht!hP5YwzY0(7Uhm3-NZF6%3_LHky+v;S*;hW0j4d+*~uUqu( zZ!3c>c_Nv!YvFtf2{6kKe4}W94-}0Uq+0B%^PyRQyH9!c%(BmdLh-G5Xue%C)s=9VZu8 z8SZ#^Q%zhHQwn{E#)ZaP(Gln1fdX5!7mpd)$2@g^0*rSsQdq)+T8$W+cPXX(Uu(d} zA@PV3H?6N)%b8yGkL`e0qq*>8o4^L!YCB5mQ%x-%!CWz*n?BH?Soi*WFHi@XJ<68d z7|&oahK!`h=CLj@}P9YIh agV0dC0Dvr*DrhwEpVUkKF9c*ir~ezn`m=oi literal 0 HcmV?d00001 diff --git a/docs/examples/logo.rst b/docs/examples/logo.rst index 270cdc9c4..1ee4c5fe0 100644 --- a/docs/examples/logo.rst +++ b/docs/examples/logo.rst @@ -15,19 +15,4 @@ MoviePy logo with a moving shadow Here the logo is a picture, while the shadow is actually a black rectangle taking the whole screen, overlaid over the logo, but with a moving mask composed of a bi-gradient, such that only one (moving) part of the rectangle is visible. See :ref:`gradients` for the code of the function `biGradient`: :: - def f(t,size,duration, a = np.pi/3, thickness = 20): - w,h = size - v = thickness* np.array([np.cos(a),np.sin(a)])[::-1] - center = [int(t*w/duration),h/2] - return biGradientScreen(size,center,v,0.6,0.0) - w,h = moviesize = (720,380) - im = ImageClip("./logo_descr.png", transparent=True) - im = im.resize(width=w/2) - - duration = 1 - shade = ColorClip(moviesize,col=(0,0,0)).with_mask() - shade.mask.get_frame = lambda t : f(t,moviesize,duration) - cc = CompositeVideoClip(moviesize,[im.set_pos(2*["center"]),shade]) - - cc.subclip(0,duration).to_videofile("moviepy_logo.avi",fps=24) diff --git a/docs/examples/the_end.rst b/docs/examples/the_end.rst index 8308c773a..ef129b202 100644 --- a/docs/examples/the_end.rst +++ b/docs/examples/the_end.rst @@ -21,4 +21,4 @@ that circle is so large that you see all the actual movie and you don't see the "The End" clip. Then the circle becomes progressively smaller and as a consequence you see less of the actual movie and more of the "The End" clip. -../../examples/the_end.py +.. literalinclude:: ../../examples/the_end.py diff --git a/docs/index.rst b/docs/index.rst index a1cd7e7ad..98fa014a1 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,7 +1,9 @@ MoviePy ======= -MoviePy is a Python module for script-based movie editing. It enables basic operations (cuts, concatenations, title insertions) to be done in a few lines, and can be used for advanced compositing and special effects. It can read and save to many formats, including `animated GIFs `_. +MoviePy is a Python module for script-based movie editing. It enables basic operations (cuts, concatenations, title insertions) to be done in a few lines, and can be used for advanced compositing and special effects. + +It can read and write to many formats, `including animated GIFs `_. Let us put together a few demonstration clips (you will find the code for most of these in the :ref:`examples`): :: @@ -58,10 +60,6 @@ User's Guide ref/ref -.. raw:: html - - - .. _PyPI: https://pypi.python.org/pypi/moviepy .. _Github: https://github.com/Zulko/moviepy .. _Zulko: https://github.com/Zulko/ diff --git a/docs/install.rst b/docs/install.rst index d64dded18..e299b025f 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -15,7 +15,7 @@ First method : if you have ``pip`` installed, just type this in a terminal (sudo (sudo) pip install moviepy -Second method : by hand. Download the sources, either on PYPI_ or (if you want the development version) on Github_, unzip everything in one folder, open a terminal and type :: +Second method : by hand. Download the sources, either on PyPI_ or (if you want the development version) on Github_, unzip everything in one folder, open a terminal and type :: (sudo) python setup.py install @@ -95,6 +95,7 @@ Advice: do not throw your ``release`` folder away. If later you have strange bug .. _`Scikit Image`: http://scikit-image.org/download.html .. _Github: https://github.com/Zulko/moviepy +.. _PyPI: https://pypi.python.org/pypi/moviepy .. _`OpenCV 2.4.6`: http://sourceforge.net/projects/opencvlibrary/files/ diff --git a/docs/ref/VideoClip.rst b/docs/ref/VideoClip/VideoClip.rst similarity index 83% rename from docs/ref/VideoClip.rst rename to docs/ref/VideoClip/VideoClip.rst index 061cea7dc..7555e9b07 100644 --- a/docs/ref/VideoClip.rst +++ b/docs/ref/VideoClip/VideoClip.rst @@ -1,22 +1,28 @@ .. ref_VideoClip: -************ -VideoClip -************ +*********************** + Classes of Video Clips +*********************** + -.. currentmodule:: moviepy :class:`VideoClip` ========================== .. autoclass:: moviepy.video.VideoClip.VideoClip + :members: :show-inheritance: + + :class:`VideoFileClip` ------------------------ .. autoclass:: moviepy.video.io.VideoFileClip.VideoFileClip + :members: :show-inheritance: + + :class:`ImageClip` ---------------------- @@ -24,6 +30,8 @@ VideoClip .. autoclass:: moviepy.video.VideoClip.ImageClip :members: :show-inheritance: + + :class:`ColorClip` ------------------ @@ -39,15 +47,13 @@ VideoClip .. autoclass:: moviepy.video.VideoClip.TextClip :members: :show-inheritance: - + + + :class:`CompositeVideoClip` ------------------------------- .. autoclass:: moviepy.video.compositing.CompositeVideoClip.CompositeVideoClip :members: :show-inheritance: - -Useful Methods --------------- -.. automodule:: moviepy.video.compositing.concatenate.concatenate diff --git a/docs/ref/ffmpeg.rst b/docs/ref/ffmpeg.rst index df2edfacd..ff6a102ca 100644 --- a/docs/ref/ffmpeg.rst +++ b/docs/ref/ffmpeg.rst @@ -3,7 +3,7 @@ FFMPEG tools ---------------------------- -.. automodule:: moviepy.video.io.ffmpeg +.. automodule:: moviepy.video.io.ffmpeg_tools :members: :undoc-members: :show-inheritance: diff --git a/docs/ref/ref.rst b/docs/ref/ref.rst index e736e56de..4d846187d 100644 --- a/docs/ref/ref.rst +++ b/docs/ref/ref.rst @@ -7,7 +7,7 @@ The documentation may be a little messy for the moment, it will get better with :maxdepth: 3 Clip - VideoClip + VideoClip/VideoClip AudioClip videofx audiofx diff --git a/docs/ref/videofx.rst b/docs/ref/videofx.rst index 20e7d14bd..d858de353 100644 --- a/docs/ref/videofx.rst +++ b/docs/ref/videofx.rst @@ -1,11 +1,44 @@ .. ref_videofx: -************ -video.fx -************ -This module regroups functions meant to be used with ``videoclip.fx``. When you type ``from moviepy import *``, the module ``video.fx`` is loaded as ``vfx`` and you can use ``vfx.colorx``, ``vfx.resize``, etc. +*********************** +moviepy.video.fx (vfx) +*********************** +The module ``moviepy.video.fx`` regroups functions meant to be used with ``videoclip.fx``. When you type :: + + from moviepy.editor import * +the module ``video.fx`` is loaded as ``vfx`` and you can use ``vfx.colorx``, ``vfx.resize``, etc. -.. automodule:: moviepy.video.fx - :members: - :show-inheritance: + +.. currentmodule:: moviepy.video.fx + +.. autosummary:: + :toctree: videofx + :nosignatures: + + blackwhite + blink + colorx + crop + fadein + fadeout + freeze_at_end + freeze_at_start + gamma_corr + headblur + loop + lum_contrast + make_loopable + margin + mirror_x + mirror_y + painting + resize + rotation + scroll + speedx + time_mirror + time_symetrize + + + diff --git a/docs/ref/videofx/moviepy.video.fx.blackwhite.rst b/docs/ref/videofx/moviepy.video.fx.blackwhite.rst new file mode 100644 index 000000000..e8f10b087 --- /dev/null +++ b/docs/ref/videofx/moviepy.video.fx.blackwhite.rst @@ -0,0 +1,6 @@ +moviepy.video.fx.blackwhite +=========================== + +.. currentmodule:: moviepy.video.fx + +.. autofunction:: blackwhite \ No newline at end of file diff --git a/docs/ref/videofx/moviepy.video.fx.blink.rst b/docs/ref/videofx/moviepy.video.fx.blink.rst new file mode 100644 index 000000000..5fd8ac8aa --- /dev/null +++ b/docs/ref/videofx/moviepy.video.fx.blink.rst @@ -0,0 +1,6 @@ +moviepy.video.fx.blink +====================== + +.. currentmodule:: moviepy.video.fx + +.. autofunction:: blink \ No newline at end of file diff --git a/docs/ref/videofx/moviepy.video.fx.colorx.rst b/docs/ref/videofx/moviepy.video.fx.colorx.rst new file mode 100644 index 000000000..f6d4a82b6 --- /dev/null +++ b/docs/ref/videofx/moviepy.video.fx.colorx.rst @@ -0,0 +1,6 @@ +moviepy.video.fx.colorx +======================= + +.. currentmodule:: moviepy.video.fx + +.. autofunction:: colorx \ No newline at end of file diff --git a/docs/ref/videofx/moviepy.video.fx.crop.rst b/docs/ref/videofx/moviepy.video.fx.crop.rst new file mode 100644 index 000000000..fff5c7220 --- /dev/null +++ b/docs/ref/videofx/moviepy.video.fx.crop.rst @@ -0,0 +1,6 @@ +moviepy.video.fx.crop +===================== + +.. currentmodule:: moviepy.video.fx + +.. autofunction:: crop \ No newline at end of file diff --git a/docs/ref/videofx/moviepy.video.fx.fadein.rst b/docs/ref/videofx/moviepy.video.fx.fadein.rst new file mode 100644 index 000000000..15a632c4b --- /dev/null +++ b/docs/ref/videofx/moviepy.video.fx.fadein.rst @@ -0,0 +1,6 @@ +moviepy.video.fx.fadein +======================= + +.. currentmodule:: moviepy.video.fx + +.. autofunction:: fadein \ No newline at end of file diff --git a/docs/ref/videofx/moviepy.video.fx.fadeout.rst b/docs/ref/videofx/moviepy.video.fx.fadeout.rst new file mode 100644 index 000000000..4295baf74 --- /dev/null +++ b/docs/ref/videofx/moviepy.video.fx.fadeout.rst @@ -0,0 +1,6 @@ +moviepy.video.fx.fadeout +======================== + +.. currentmodule:: moviepy.video.fx + +.. autofunction:: fadeout \ No newline at end of file diff --git a/docs/ref/videofx/moviepy.video.fx.freeze_at_end.rst b/docs/ref/videofx/moviepy.video.fx.freeze_at_end.rst new file mode 100644 index 000000000..2e2172e52 --- /dev/null +++ b/docs/ref/videofx/moviepy.video.fx.freeze_at_end.rst @@ -0,0 +1,6 @@ +moviepy.video.fx.freeze_at_end +============================== + +.. currentmodule:: moviepy.video.fx + +.. autofunction:: freeze_at_end \ No newline at end of file diff --git a/docs/ref/videofx/moviepy.video.fx.freeze_at_start.rst b/docs/ref/videofx/moviepy.video.fx.freeze_at_start.rst new file mode 100644 index 000000000..e35aeb044 --- /dev/null +++ b/docs/ref/videofx/moviepy.video.fx.freeze_at_start.rst @@ -0,0 +1,6 @@ +moviepy.video.fx.freeze_at_start +================================ + +.. currentmodule:: moviepy.video.fx + +.. autofunction:: freeze_at_start \ No newline at end of file diff --git a/docs/ref/videofx/moviepy.video.fx.gamma_corr.rst b/docs/ref/videofx/moviepy.video.fx.gamma_corr.rst new file mode 100644 index 000000000..b63dd2288 --- /dev/null +++ b/docs/ref/videofx/moviepy.video.fx.gamma_corr.rst @@ -0,0 +1,6 @@ +moviepy.video.fx.gamma_corr +=========================== + +.. currentmodule:: moviepy.video.fx + +.. autofunction:: gamma_corr \ No newline at end of file diff --git a/docs/ref/videofx/moviepy.video.fx.headblur.rst b/docs/ref/videofx/moviepy.video.fx.headblur.rst new file mode 100644 index 000000000..615111a83 --- /dev/null +++ b/docs/ref/videofx/moviepy.video.fx.headblur.rst @@ -0,0 +1,6 @@ +moviepy.video.fx.headblur +========================= + +.. currentmodule:: moviepy.video.fx + +.. autofunction:: headblur \ No newline at end of file diff --git a/docs/ref/videofx/moviepy.video.fx.loop.rst b/docs/ref/videofx/moviepy.video.fx.loop.rst new file mode 100644 index 000000000..b7d6c565d --- /dev/null +++ b/docs/ref/videofx/moviepy.video.fx.loop.rst @@ -0,0 +1,6 @@ +moviepy.video.fx.loop +===================== + +.. currentmodule:: moviepy.video.fx + +.. autofunction:: loop \ No newline at end of file diff --git a/docs/ref/videofx/moviepy.video.fx.lum_contrast.rst b/docs/ref/videofx/moviepy.video.fx.lum_contrast.rst new file mode 100644 index 000000000..f5d7b289e --- /dev/null +++ b/docs/ref/videofx/moviepy.video.fx.lum_contrast.rst @@ -0,0 +1,6 @@ +moviepy.video.fx.lum_contrast +============================= + +.. currentmodule:: moviepy.video.fx + +.. autofunction:: lum_contrast \ No newline at end of file diff --git a/docs/ref/videofx/moviepy.video.fx.make_loopable.rst b/docs/ref/videofx/moviepy.video.fx.make_loopable.rst new file mode 100644 index 000000000..2d92922d8 --- /dev/null +++ b/docs/ref/videofx/moviepy.video.fx.make_loopable.rst @@ -0,0 +1,6 @@ +moviepy.video.fx.make_loopable +============================== + +.. currentmodule:: moviepy.video.fx + +.. autofunction:: make_loopable \ No newline at end of file diff --git a/docs/ref/videofx/moviepy.video.fx.margin.rst b/docs/ref/videofx/moviepy.video.fx.margin.rst new file mode 100644 index 000000000..3d59c3102 --- /dev/null +++ b/docs/ref/videofx/moviepy.video.fx.margin.rst @@ -0,0 +1,6 @@ +moviepy.video.fx.margin +======================= + +.. currentmodule:: moviepy.video.fx + +.. autofunction:: margin \ No newline at end of file diff --git a/docs/ref/videofx/moviepy.video.fx.mirror_x.rst b/docs/ref/videofx/moviepy.video.fx.mirror_x.rst new file mode 100644 index 000000000..46af93237 --- /dev/null +++ b/docs/ref/videofx/moviepy.video.fx.mirror_x.rst @@ -0,0 +1,6 @@ +moviepy.video.fx.mirror_x +========================= + +.. currentmodule:: moviepy.video.fx + +.. autofunction:: mirror_x \ No newline at end of file diff --git a/docs/ref/videofx/moviepy.video.fx.mirror_y.rst b/docs/ref/videofx/moviepy.video.fx.mirror_y.rst new file mode 100644 index 000000000..f7b80ed75 --- /dev/null +++ b/docs/ref/videofx/moviepy.video.fx.mirror_y.rst @@ -0,0 +1,6 @@ +moviepy.video.fx.mirror_y +========================= + +.. currentmodule:: moviepy.video.fx + +.. autofunction:: mirror_y \ No newline at end of file diff --git a/docs/ref/videofx/moviepy.video.fx.painting.rst b/docs/ref/videofx/moviepy.video.fx.painting.rst new file mode 100644 index 000000000..4713d7655 --- /dev/null +++ b/docs/ref/videofx/moviepy.video.fx.painting.rst @@ -0,0 +1,6 @@ +moviepy.video.fx.painting +========================= + +.. currentmodule:: moviepy.video.fx + +.. autofunction:: painting \ No newline at end of file diff --git a/docs/ref/videofx/moviepy.video.fx.resize.rst b/docs/ref/videofx/moviepy.video.fx.resize.rst new file mode 100644 index 000000000..bc50a104a --- /dev/null +++ b/docs/ref/videofx/moviepy.video.fx.resize.rst @@ -0,0 +1,6 @@ +moviepy.video.fx.resize +======================= + +.. currentmodule:: moviepy.video.fx + +.. autofunction:: resize \ No newline at end of file diff --git a/docs/ref/videofx/moviepy.video.fx.rotation.rst b/docs/ref/videofx/moviepy.video.fx.rotation.rst new file mode 100644 index 000000000..99efe5041 --- /dev/null +++ b/docs/ref/videofx/moviepy.video.fx.rotation.rst @@ -0,0 +1,6 @@ +moviepy.video.fx.rotation +========================= + +.. currentmodule:: moviepy.video.fx + +.. autofunction:: rotation \ No newline at end of file diff --git a/docs/ref/videofx/moviepy.video.fx.scroll.rst b/docs/ref/videofx/moviepy.video.fx.scroll.rst new file mode 100644 index 000000000..736325d5f --- /dev/null +++ b/docs/ref/videofx/moviepy.video.fx.scroll.rst @@ -0,0 +1,6 @@ +moviepy.video.fx.scroll +======================= + +.. currentmodule:: moviepy.video.fx + +.. autofunction:: scroll \ No newline at end of file diff --git a/docs/ref/videofx/moviepy.video.fx.speedx.rst b/docs/ref/videofx/moviepy.video.fx.speedx.rst new file mode 100644 index 000000000..2546cebff --- /dev/null +++ b/docs/ref/videofx/moviepy.video.fx.speedx.rst @@ -0,0 +1,6 @@ +moviepy.video.fx.speedx +======================= + +.. currentmodule:: moviepy.video.fx + +.. autofunction:: speedx \ No newline at end of file diff --git a/docs/ref/videofx/moviepy.video.fx.time_mirror.rst b/docs/ref/videofx/moviepy.video.fx.time_mirror.rst new file mode 100644 index 000000000..6a1a5c8cb --- /dev/null +++ b/docs/ref/videofx/moviepy.video.fx.time_mirror.rst @@ -0,0 +1,6 @@ +moviepy.video.fx.time_mirror +============================ + +.. currentmodule:: moviepy.video.fx + +.. autofunction:: time_mirror \ No newline at end of file diff --git a/docs/ref/videofx/moviepy.video.fx.time_symetrize.rst b/docs/ref/videofx/moviepy.video.fx.time_symetrize.rst new file mode 100644 index 000000000..41c97be0b --- /dev/null +++ b/docs/ref/videofx/moviepy.video.fx.time_symetrize.rst @@ -0,0 +1,6 @@ +moviepy.video.fx.time_symetrize +=============================== + +.. currentmodule:: moviepy.video.fx + +.. autofunction:: time_symetrize \ No newline at end of file diff --git a/docs/ref/videotools.rst b/docs/ref/videotools.rst index 9338a7769..ad5bdf5ec 100644 --- a/docs/ref/videotools.rst +++ b/docs/ref/videotools.rst @@ -6,6 +6,8 @@ video.tools This module regroups advanced, useful (and less useful) functions for editing videos. + + Drawing -------- .. automodule:: moviepy.video.tools.drawing diff --git a/examples/logo.py b/examples/logo.py new file mode 100644 index 000000000..2886f6014 --- /dev/null +++ b/examples/logo.py @@ -0,0 +1,28 @@ +from moviepy.editor import * + +import numpy as np + +w,h = moviesize = (720,380) + +duration = 1 + +def f(t,size, a = np.pi/3, thickness = 20): + w,h = size + v = thickness* np.array([np.cos(a),np.sin(a)])[::-1] + center = [int(t*w/duration),h/2] + return biGradientScreen(size,center,v,0.6,0.0) + +logo = ImageClip("../../videos/logo_descr.png").\ + resize(width=w/2).\ + set_mask(mask) + +screen = logo.on_color(moviesize, color = (0,0,0), pos='center') + +shade = ColorClip(moviesize,col=(0,0,0)) +mask_frame = lambda t : f(t,moviesize,duration) +shade.mask = VideoClip(ismask=True, get_frame = mask_frame) + +cc = CompositeVideoClip([im.set_pos(2*["center"]),shade], + size = moviesize) + +cc.subclip(0,duration).to_videofile("moviepy_logo.avi",fps=24) diff --git a/moviepy/Clip.py b/moviepy/Clip.py index bb3e76fca..5a4ab208a 100644 --- a/moviepy/Clip.py +++ b/moviepy/Clip.py @@ -82,8 +82,8 @@ def fl(self, fun, apply_to=[] , keep_duration=True): audio or the mask of the clip, if any. keep_duration - Set to True if the transformation does not change the clip's - ``duration``. + Set to True if the transformation does not change the + ``duration`` of the clip. Examples -------- @@ -215,8 +215,9 @@ def set_start(self, t, change_end=True): @time_can_be_tuple def set_end(self, t): """ - Returns a copy of the clip, with the ``end`` attribute set to ``t``. - Also sets the duration of the returned clip's mask and audio, if any. + Returns a copy of the clip, with the ``end`` attribute set to + ``t``. Also sets the duration of the mask and audio, if any, + of the returned clip. """ newclip = self.copy() newclip.end = t @@ -237,8 +238,8 @@ def set_duration(self, t, change_end=True): """ Returns a copy of the clip, with the ``duration`` attribute set to ``t``. - Also sets the duration of the returned clip's mask and audio, - if any. + Also sets the duration of the mask and audio, if any, of the + returned clip. """ newclip = copy(self) newclip.duration = t @@ -294,8 +295,9 @@ def subclip(self, ta=0, tb=None): If ``tb`` is provided or if the clip has a duration attribute, the duration of the returned clip is set automatically. - The resulting subclip's ``mask`` and ``audio`` will be subclips - of the original clip's ``mask`` and ``audio``, if they exist. + The ``mask`` and ``audio`` of the resulting subclip will be + subclips of ``mask`` and ``audio`` the original clip, if + they exist. """ newclip = self.fl_time(lambda t: t + ta, apply_to=[]) @@ -322,7 +324,7 @@ def cutout(self, ta, tb): Returns a clip playing the content of the current clip but skips the extract between ``ta`` and ``tb`` (in seconds). If the original clip has a ``duration`` attribute set, - the returned clip's duration is automatically computed as + the duration of the returned clip is automatically computed as `` duration - (tb - ta)``. The resulting clip's ``audio`` and ``mask`` will also be cutout diff --git a/moviepy/audio/AudioClip.py b/moviepy/audio/AudioClip.py index c4ce8b63e..70ad95df4 100644 --- a/moviepy/audio/AudioClip.py +++ b/moviepy/audio/AudioClip.py @@ -14,19 +14,18 @@ class AudioClip(Clip): - - """ + """Base class for audio clips. + + See ``SoundClip`` and ``CompositeSoundClip`` for usable classes. - Base class for audio clips. See ``SoundClip`` and ``CompositeSoundClip`` - for usable classes. - An audio clip is a special clip with a ``get_frame`` attribute of - the form `` t -> [ f_t ]`` for mono sound and ``t -> [ f1_t, f2_t ]`` - for stereo sound (the arrays are Numpy arrays). + An AudioClip is a Clip with a ``get_frame`` attribute of + the form `` t -> [ f_t ]`` for mono sound and + ``t-> [ f1_t, f2_t ]`` for stereo sound (the arrays are Numpy arrays). The `f_t` are floats between -1 and 1. These bounds can be trespassed wihtout problems (the program will put the sound back into the bounds at conversion time, without much impact). - Attributes + Parameters ----------- get_frame @@ -34,7 +33,7 @@ class AudioClip(Clip): for a sound, it is just a float. What 'makes' the sound are the variations of that float in the time. - nchannels: + nchannels Number of channels (one or two for mono or stereo). Examples @@ -67,6 +66,7 @@ def to_soundarray(self,tt=None,fps=None, nbytes=2): nbytes Number of bytes to encode the sound: 1 for 8bit sound, 2 for 16bit, 4 for 32bit sound. + """ if tt is None: tt = np.arange(0,self.duration, 1.0/fps) @@ -104,7 +104,7 @@ class AudioArrayClip(AudioClip): An audio clip made from a sound array. Parameters - ------------- + ----------- array A Numpy array representing the sound, of size Nx1 for mono, @@ -146,7 +146,7 @@ def get_frame(t): class CompositeAudioClip(AudioClip): - """ + """ Clip made by composing several AudioClips. An audio clip made by putting together several audio clips. diff --git a/moviepy/version.py b/moviepy/version.py new file mode 100644 index 000000000..5d053a1d6 --- /dev/null +++ b/moviepy/version.py @@ -0,0 +1 @@ +__version__ = '0.2.1.7.06' diff --git a/moviepy/video/VideoClip.py b/moviepy/video/VideoClip.py index 619dd0297..6b39269d1 100644 --- a/moviepy/video/VideoClip.py +++ b/moviepy/video/VideoClip.py @@ -86,7 +86,7 @@ def __init__(self, ismask=False, get_frame=None): self.relative_pos = False if get_frame: self.get_frame = get_frame - newclip.size = newclip.get_frame(0).shape[:2][::-1] + self.size =get_frame(0).shape[:2][::-1] self.ismask = ismask @@ -182,7 +182,7 @@ def to_videofile(self, filename, fps=24, codec='libx264', temp_audiofile=None, rewrite_audio = True, remove_temp = True, para = False, verbose = True): - """ Write the clip to a videofile. + """Write the clip to a videofile. Parameters ----------- @@ -195,14 +195,18 @@ def to_videofile(self, filename, fps=24, codec='libx264', Number of frames per second in the resulting video file. codec - Codec to use for image encoding. Can be - - 'rawvideo','png' : will produce a raw video, of perfect - quality, but possibly very huge size. 'png' is still - lossless but produces smaller files. - - 'mpeg4' : For nice quality, very well compressed videos. - - 'libx264' : A little better than 'mpeg4', a little heavier. - - Any of the (many) other codec supported by ffmpeg. - + Codec to use for image encoding. Can be any codec supported + by ffmpeg. + + ``'rawvideo'``, ``'png'`` will produce a raw video, + of perfect quality, but possibly very huge size. 'png' is + is lossless and produces smaller files than 'rawvideo'. + + ``'mpeg4'`` produces nice quality, very well compressed videos. + + ``'libx264'`` (default) is a little better than 'mpeg4', + a little heavier. + audio Either ``True``, ``False``, or a file name. If ``True`` and the clip has an audio clip attached, this diff --git a/moviepy/video/fx/loop.py b/moviepy/video/fx/loop.py index 995942996..1aa7ba2de 100644 --- a/moviepy/video/fx/loop.py +++ b/moviepy/video/fx/loop.py @@ -10,8 +10,12 @@ def loop(self, n=None): """ Returns a clip that plays the current clip in an infinite loop. Ideal for clips coming from gifs. - :param n: number of times the clip should be played. If `None` the - the clip will loop indefinitely (i.e. with no set duration). + + Parameters + ------------ + n + Number of times the clip should be played. If `None` the + the clip will loop indefinitely (i.e. with no set duration). """ result = self.fl_time(lambda t: t % self.duration) if n: diff --git a/moviepy/video/fx/time_symetrize.py b/moviepy/video/fx/time_symetrize.py index e81add434..0cbea186c 100644 --- a/moviepy/video/fx/time_symetrize.py +++ b/moviepy/video/fx/time_symetrize.py @@ -6,11 +6,11 @@ @requires_duration @apply_to_mask def time_symetrize(clip): - """ - Returns a clip that plays the current clip once forwards and + """ + Returns a clip that plays the current clip once forwards and then once backwards. This is very practival to make video that loop well, e.g. to create animated GIFs. This effect is automatically applied to the clip's mask and audio if they exist. - """ - return concatenate([clip, clip.fx( time_mirror )]) + """ + return concatenate([clip, clip.fx( time_mirror )]) diff --git a/moviepy/video/tools/credits.py b/moviepy/video/tools/credits.py index 7ca7875c5..d64e71eaf 100644 --- a/moviepy/video/tools/credits.py +++ b/moviepy/video/tools/credits.py @@ -17,7 +17,7 @@ def credits1(creditfile,width,stretch=30,color='white', Parameters ----------- - creditfile: + creditfile A text file whose content must be as follows: :: # This is a comment @@ -34,13 +34,13 @@ def credits1(creditfile,width,stretch=30,color='white', ..Music Supervisor JEAN DIDIER - width: + width Total width of the credits text in pixels - gap: + gap Gap in pixels between the jobs and the names. - **txt_kw: + **txt_kw Additional argument passed to TextClip (font, colors, etc.) @@ -49,9 +49,10 @@ def credits1(creditfile,width,stretch=30,color='white', Returns --------- - An ImageClip instance that looks like this and can be scrolled - to make some credits : - + image + An ImageClip instance that looks like this and can be scrolled + to make some credits : + Executive Story Editor MARCEL DURAND Associate Producers MARTIN MARCEL DIDIER MARTIN diff --git a/moviepy/video/tools/drawing.py b/moviepy/video/tools/drawing.py index 1f10277af..afb35c303 100644 --- a/moviepy/video/tools/drawing.py +++ b/moviepy/video/tools/drawing.py @@ -6,7 +6,8 @@ import numpy as np def blit(im1, im2, pos=[0, 0], mask=None, ismask=False): - """ + """ Blit an image over another. + Blits ``im1`` on ``im2`` as position ``pos=(x,y)``, using the ``mask`` if provided. If ``im1`` and ``im2`` are mask pictures (2D float arrays) then ``ismask`` must be ``True``. @@ -49,38 +50,66 @@ def blit(im1, im2, pos=[0, 0], mask=None, ismask=False): def color_gradient(size,p1,p2=None,vector=None, r=None, col1=0,col2=1.0, shape='linear', offset = 0): - """ - Makes linear, bilinear, or radial gradients. + """Draw a linear, bilinear, or radial gradient. - The result is a picture of size ``size``, whose color varies gradually: from - color `col1` in position ``p1`` to color ``col2`` in position ``p2``. + The result is a picture of size ``size``, whose color varies + gradually from color `col1` in position ``p1`` to color ``col2`` + in position ``p2``. If it is a RGB picture the result must be transformed into - a 'uint8' array to be displayed normally: + a 'uint8' array to be displayed normally: + - >>> grad = colorGradient(blabla).astype('unit8') - + Parameters + ------------ - :param size: size of the final picture/array + size + Size (width, height) in pixels of the final picture/array. - :param p1, p2: coordinates (x,y) of the border point for - ``col1`` and ``col2`` + p1, p2 + Coordinates (x,y) in pixels of the limit point for ``col1`` + and ``col2``. The color 'before' ``p1`` is ``col1`` and it + gradually changes in the direction of ``p2`` until it is ``col2`` + when it reaches ``p2``. - :param vector: can be provided instead of ``p2``. ``p2`` is then - defined as (p1 + vector). + vector + A vector [x,y] in pixels that can be provided instead of ``p2``. + ``p2`` is then defined as (p1 + vector). - :param col1, col2: can be floats to create gradient masks, or - [R,G,B] arrays to create gradient images. + col1, col2 + Either floats between 0 and 1 (for gradients used in masks) + or [R,G,B] arrays (for colored gradients). - :param shape: 'linear', 'bilinear', or 'circular'. In a linear gradient - the color varies in one direction, from point ``p1`` to point ``p2``. - + shape + 'linear', 'bilinear', or 'circular'. + In a linear gradient the color varies in one direction, + from point ``p1`` to point ``p2``. + In a bilinear gradient it also varies symetrically form ``p1`` + in the other direction. + In a circular gradient it goes from ``col1`` to ``col2`` in all + directions. + + offset + Real number between 0 and 1 indicating the fraction of the vector + at which the gradient actually starts. For instance if ``offset`` + is 0.9 in a gradient going from p1 to p2, then the gradient will + only occur near p2 (before that everything is of color ``col1``) + If the offset is 0.9 in a radial gradient, the gradient will + occur in the region located between 90% and 100% of the radius, + this creates a blurry disc of radius d(p1,p2). + + Returns + -------- - :param offset: percentage of the vector at which the gradient actually - starts. for instance if offset is 0.9 in a gradient going from - p1 to p2, then the gradient will only occur near p2. If the offset - is 0.9 in a radial gradient, the gradient will occur in the region - located between 90% and 100% of the radius. + image + An Numpy array of dimensions (W,H,ncolors) of type float + representing the image of the gradient. + + + Examples + --------- + + >>> grad = colorGradient(blabla).astype('unit8') """ @@ -145,12 +174,41 @@ def color_gradient(size,p1,p2=None,vector=None, r=None, col1=0,col2=1.0, def color_split(size,x=None,y=None,p1=None,p2=None,vector=None, col1=0,col2=1.0, grad_width=0): - """ - Makes an array of size ``size`` divided in two regions by a - straight line, the two regions having different colors. - provide either x, or y, or (c1,c2), or (c1,vector). See below. + """Make an image splitted in 2 colored regions. + + Returns an array of size ``size`` divided in two regions called 1 and + 2 in wht follows, and which will have colors col& and col2 + respectively. + + Parameters + ----------- + + x: (int) + If provided, the image is splitted horizontally in x, the left + region being region 1. + + y: (int) + If provided, the image is splitted vertically in y, the top region + being region 1. + + p1,p2: + Positions (x1,y1),(x2,y2) in pixels, where the numbers can be + floats. Region 1 is defined as the whole region on the left when + going from ``p1`` to ``p2``. + + p1, vector: + ``p1`` is (x1,y1) and vector (v1,v2), where the numbers can be + floats. Region 1 is then the region on the left when starting + in position ``p1`` and going in the direction given by ``vector``. + + gradient_width + If not zero, the split is not sharp, but gradual over a region of + width ``gradient_width`` (in pixels). This is preferable in many + situations (for instance for antialiasing). - Use: + + Examples + --------- >>> size = [200,200] >>> # an image with all pixels with x<50 =0, the others =1 @@ -159,25 +217,6 @@ def color_split(size,x=None,y=None,p1=None,p2=None,vector=None, >>> colorSplit(size, x=50, col1=[255,0,0], col2=[0,255,0]) >>> # An image splitted along an arbitrary line (see below) >>> colorSplit(size, p1=[20,50], p2=[25,70] col1=0, col2=1) - - - :param x (int): If provided, the image is splitted horizontally in x, - the left region being region 1. - - :param y (int): If provided, the image is splitted vertically in y, - the top region being region 1. - - :param p1,p2: Positions (x1,y1),(x2,y2), where the numbers can be floats, - of two points. Region 1 is defined as the region on the left when - going from p1 to p2. - - :param p1, vector: p1 is (x1,y1) and vector (v1,v2), where the numbers - can be floats. Region 1 is then the region on the left when starting - in position p1 and going in the direction given by ``vector``. - - :param gradient_width: if not null, the split is not sharp, but gradual - over a region of width ``gradient_width`` (in pixels). This is - preferable in many situations (for instance for antialiasing). """ @@ -209,7 +248,8 @@ def color_split(size,x=None,y=None,p1=None,p2=None,vector=None, raise def circle(screensize, center, radius, col1=1.0, col2=0, blur=1): - """ + """ Draw an image with a circle. + Draws a circle of color ``col1``, on a background of color ``col2``, on a screen of size ``screensize`` at the position ``center=(x,y)``, with a radius ``radius`` but slightly blurred on the border by ``blur``