I want to resize a video clip in python 2.7.
For example we give "movie.mp4" with 1080p quality
The result should be "movie.mp4" with 360p quality
I Think that there should be solutions with Moviepy. If you know a solution with it.
I would be grateful if you answer me.
Here is how you resize a movie with moviepy:
see the mpviepy doc here
import moviepy.editor as mp
clip = mp.VideoFileClip("movie.mp4")
clip_resized = clip.resize(height=360) # make the height 360px ( According to moviePy documenation The width is then computed so that the width/height ratio is conserved.)
clip_resized.write_videofile("movie_resized.mp4")
You can also tune the quality by adding the parameter bitrate="500k" or bitrate="5000k" in the last line.
As said above, you could also use ffmpeg directly, it will be simpler if you just need a quick script.
Why not ffmpeg?
ffmpeg -i movie.mp4 -vf scale=640:360 movie_360p.mp4
If you use 640:-2 then, in this example, the scale filter will preserve the aspect ratio and automatically calculate the correct height.
Look at the H.264 encoding guide for additional options.
Moviepy Resize function
>>> myClip.resize( (460,720) ) # New resolution: (460,720)
>>> myClip.resize(0.6) # width and heigth multiplied by 0.6
>>> myClip.resize(width=800) # height computed automatically.
>>> myClip.resize(lambda t : 1+0.02*t) # slow swelling of the clip
Related
First I note that there are many related questions, but after a day of trying pyvip and cairo and the rest none of them work for me, even after installing other software that they seem to depend on. The exception is svglib with reportlab, it comes close but doesn't quite get there! This is the best post I found and may help some.
I have all my source images in SVG files. Most app stores require you to provide a set of PNGs with specific sizes and qualities. So I need to take an SVG and produce a PNG with width w and height h and specific dpi. I want to do this programmatically in python.
I have written a function that almost works, but scaling and dpi interact with each other in weird ways. I use svglib to convert the SVG to a ReportLab drawing then use reportlab to manipulate the drawing. The install went smoothly on Windows unlike some of the other options.
pip install svglib
pip install reportlab
The code is as follows. I inspected the above libraries to get the arguments, but added stuff to get specific size.
from svglib.svglib import svg2rlg
from reportlab.graphics import renderPM
def svg_to_png(in_path,out_path,fmt="PNG",
scale=None,size=None,scale_x=1,size_x=None,scale_y=1,size_y=None,
dpi=72, bg=0xffffff):
# Convert SVG to ReportLab drawing.
drawing = svg2rlg(in_path)
# Work out scale factors
# Scale over-rides scale_x|y, ditto size
scale_x = scale if scale else scale_x
scale_y = scale if scale else scale_y
size_x = size if size else size_x
size_y = size if size else size_y
# Size over-rides scale
scaling_x = size_x/drawing.width if size_x else scale_x
scaling_y = size_y/drawing.height if size_y else scale_y
# Scale the drawing
drawing.width = drawing.minWidth() * scaling_x
drawing.height = drawing.height * scaling_y
drawing.scale(scaling_x, scaling_y)
# Render ReportLab drawing as a PNG
renderPM.drawToFile(drawing, out_path, fmt=fmt, dpi=dpi, bg=bg)
if __name__ == '__main__':
breakpoint()
in_path = 'C:\\Users\\...\\A.svg'
out_path = 'C:\\Users\\...\\A.png'
svg_to_png(in_path,out_path,scale=2,dpi=72)
The function works for resizing so long as I leave the dpi alone. The dpi=72 seems to be a universal default in reportlab and the source of the issues. I hoped that increasing the dpi would impove the quality of the PNG (reduce pixilation), but all it seems to do is expand the canvas.
The input SVG is of course pixel perfect. Here are four PNGs from the above function.
Scale 1 dpi 72 (dim 115x124 size 8.13kb), conversion with all defaults.
Scale 1 dpi 144 (dim 230x249 size 9.06kb), canvas doubled, pic in bottom-left quadrant, ignore black line bug.
Scale 2 dpi 72 (dim 230x249 size 17.5kb), scaled properly but pixelated (look at the eye)
Scale 2 dpi 144 (dim 461x497 size 19.8kb), canvas quadrupled? picture doubled.
Can someone please advise how to use reportlab properly to resize the picture to given scale or size and within that fixed size, increase the quality via the dpi.
Answering my own question, after lots of code inspections within svglib it appears it is impossible to do resize and increase dpi to predefined values. This is because svglib renders an SVG to a ReportLab drawing without any ability to tell it to render the SVG at the resolution required, which is a shame given the whole point of vector is arbitrary resolution. Once its in ReportLab you are stuck with the resolution of the drawing.
I switched to pyvips which makes use of libvips.
Its a bit fiddly to install, you can't just pip install pyvips. You have to download the libvips package and unzip it on disk somewhere you keep programs. You can then pip install pyvips in the usual way. In your python code you then add the libvips /bin to your path, assuming you don't want to do this permanently from the operating system.
The following function can then be used to convert an SVG to a PNG and set the resolution of the PNG and either scale it or set its horizontal width.
def svg_to_png(svg_path,png_path,dpi=72,scale=1,size=None):
# Documentation
# Ref: https://libvips.github.io/libvips/API/current/
# Ref: https://libvips.github.io/pyvips/
# Initialise
debug=False
import os
os.environ['path'] += r';C:\programs\vips\vips-dev-8.11\bin'
import pyvips
# Get the image
if size:
image = pyvips.Image.new_from_file(svg_path,dpi=dpi,scale=1)
if debug: print({field:image.get(field) for field in image.get_fields()})
scale = size/image.get('width')
image = image.resize(scale)
else:
image = pyvips.Image.new_from_file(svg_path,dpi=dpi,scale=scale)
# Write the image
if debug: print({field:image.get(field) for field in image.get_fields()})
image.write_to_file(png_path)
The function works properly when scaling. When setting a fixed output size it works well but a little bit of fiddling with the input dpi may be required to get the exact right output dpi.
Here's my solution which I also posted to a relevant Github Issue. This uses pymupdf to convert the intermediary PDF generated with svglib and reportlab to an SVG.
The advantage of this solution is that it doesn't need any fiddling with external dependencies like poppler, cairo or libvips, as pymupdf has prebuild wheels for Linux, Windows and MacOS.
Another advantage is the support for a transparent background.
import fitz
from svglib import svglib
from reportlab.graphics import renderPDF
# Convert svg to pdf in memory with svglib+reportlab
# directly rendering to png does not support transparency nor scaling
drawing = svglib.svg2rlg(path="input.svg")
pdf = renderPDF.drawToString(drawing)
# Open pdf with fitz (pyMuPdf) to convert to PNG
doc = fitz.Document(stream=pdf)
pix = doc.load_page(0).get_pixmap(alpha=True, dpi=300)
pix.save("output.png")
Cheers!
I am displaying an image of a molecule using IPython.display in Jupyter.
The resolution of the image is quite low. Is there a way to specify the width and height of the displayed image and its resolution?
I googled it and could not find anything. All I need is something like this:
display(moleSmilemol, format='svg', width=1000, height=1000)
Any pointers would be appreciated.
Update: I could add custom css, which will blow up the picture that was generate, but it is still low quality. I am interested increasing the quality of the picture and its size too. So something deeper than CSS is needed.
Try changing the variables in the rdkit.Chem.Draw.IPythonConsole module:
from rdkit.Chem.Draw import IPythonConsole
IPythonConsole.molSize = (800, 800) # Change image size
IPythonConsole.ipython_useSVG = True # Change output to SVG
mol = Chem.MolFromSmiles('N#Cc1cccc(-c2nc(-c3cccnc3)no2)c1')
display(mol)
Otherwise, you need to use the rdMolDraw2D module to create the drawings yourself with the parameters you require.
So I'm trying to create a clip with moviepy where five semi-transparent clips are overlaid on each other using the CompositeVideoClip.
The output should be a clip of length equal to the longest clip, where the all the layers of the composite clip are visible.
My code looks something like this:
from moviepy.editor import *
clip_1 = VideoFileClip('some\\path\\here.mp4')
clip_2 = VideoFileClip('some\\path\\here.mp4')
clip_3 = VideoFileClip('some\\path\\here.mp4')
clip_4 = VideoFileClip('some\\path\\here.mp4')
clip_5 = VideoFileClip('some\\path\\here.mp4')
list_of_clips = [clip_1, clip_2, clip_3, clip_4, clip_5]
for index, clip in enumerate(list_of_clips):
list_of_clips[index] = clip.set_opacity(.20)
output_clip = CompositeVideoClip(list_of_clips)
output_clip.write_videofile('some\\path\\here.mp4')
Code runs fine, except transparency is not applied.
Neither does this work:
clip = VideoFileClip(some\\path\\here.mp4).set_opacity(.30)
clip.write_videofile(some\\path\\here.mp4)
Export works fine, but clip is fully opaque.
Any suggestions for how to achieve transparency in clip outputs?
the mp4 (I'm assuming h264) format does not offer transparency. webM (vp9) and some variants of h265 do offer transparency.
Im not sure exactly what you are trying to do - but perhaps creating the overlaid videos as webm (transparency supported) - and then converting to h264 at the end might work for you.
I'm trying to convert EPS images to JPEG using Pillow. But the results are of low quality. I'm trying to use resize method, but it gets completely ignored. I set up the size of JPEG image as (3600, 4700), but the resulted image has (360, 470) size. My code is:
eps_image = Image.open('img.eps')
height = eps_image.height * 10
width = eps_image.width * 10
new_size = (height, width)
print(new_size) # prints (3600, 4700)
eps_image.resize(new_size, Image.ANTIALIAS)
eps_image.save(
'img.jpeg',
format='JPEG'
dpi=(9000, 9000),
quality=95)
UPD. Vasu Deo.S noticed one my error, and thanks to him the JPG image has become bigger, but quality is still low. I've tried different DPI, sizes, resample values for resize function, but the result does not change much. How can i make it better?
The problem is that PIL is a raster image processor, as opposed to a vector image processor. It "rasterises" vector images (such as your EPS file and SVG files) onto a grid when it opens them because it can only deal with rasters.
If that grid doesn't have enough resolution, you can never regain it. Normally, it rasterises at 100dpi, so if you want to make bigger images, you need to rasterise onto a larger grid before you even get started.
Compare:
from PIL import Image
eps_image = Image.open('image.eps')
eps_image.save('a.jpg')
The result is 540x720:
And this:
from PIL import Image
eps_image = Image.open('image.eps')
# Rasterise onto 4x higher resolution grid
eps_image.load(scale=4)
eps_image.save('a.jpg')
The result is 2160x2880:
You now have enough quality to resize however you like.
Note that you don't need to write any Python to do this at all - ImageMagick will do it all for you. It is included in most Linux distros and is available for macOS and Windows and you just use it in Terminal. The equivalent command is like this:
magick -density 400 input.eps -resize 800x600 -quality 95 output.jpg
It's because eps_image.resize(new_size, Image.ANTIALIAS) returns an resized copy of an image. Therefore you have to store it in a separate variable. Just change:-
eps_image.resize(new_size, Image.ANTIALIAS)
to
eps_image = eps_image.resize(new_size, Image.ANTIALIAS)
UPDATE:-
These may not solve the problem completely, but still would help.
You are trying to save your output image as a .jpeg, which is a
lossy compression format, therefore information is lost during the
compression/transformation (for the most part). Change the output
file extension to a lossless compression format like .png so that
data would not be compromised during compression. Also change
quality=95 to quality=100 in Image.save()
You are using Image.ANTIALIAS for resampling the image, which is
not that good when upscaling the image (it has been replaced by
Image.LANCZOS in newer version, the clause still exists for
backward compatibility). Try using Image.BICUBIC, which produces
quite favorable results (for the most part) when upscaling the image.
I'm building a Paint-like app Since I want the freedom to reposition and modify the shape properties later, I am using Tkinter to draw shapes on Canvas instead of PIL Draw or anything else. From other answers, I found how to save a canvas as PNG by 1st creating a postscript file and then converting it to PNG using PIL.
Now the problem is the EPS file has transparent spaces but the PNG file fills those voids with a White background color. I'm not sure where I am going wrong.
Below is the function I used.
def saveImg(event):
global canvas
canvas.postscript(file="my_drawing.eps", colormode='color')
imgNew = Image.open("my_drawing.eps")
imgNew.convert("RGBA")
imgNew.thumbnail((2000,2000), Image.ANTIALIAS)
imgNew.save('testImg.png', quality=90)
Looks like transparency is not supported. From the docs:
The EPS driver can read EPS images in L, LAB, RGB and CMYK mode, but Ghostscript may convert the images to RGB mode rather than leaving them in the original color space.
When you load in RGB (instead of RGBA) the alpha channel information is discarded and converting it to RGBA later will not recover it.
Your best shot is porting it to more recent toolkits like cairo or QT or converting the file using GhostScript directly as suggested by PM2Ring.
For the GS approach in order to set the width and height of the output file you must use the -rN switch where N is the resolution in PPI (pixels per inch). You must do the math in order to get target resolution from the EPS bounding box and the desired output size.
Or you can render to a fixed resolution first, lets say, 100 PPI, see the width you got and do the math in order to get the correct resolution. For example, if rendering with -r100 gives you a file 500 pixels wide but you want it to be 1024:
desired_resolution = initial_resolution * desired_width // initial_width
In order to get a file 1024 pixels wide:
>>> 100 * 1024 // 500
204
So you must render the EPS again using -r204.
Edit 1:
I got the solution from this Question
We can set custom width and height using -gNNNNxMMMM
but the dpi value crops only a small area. I tried with the usual 72dpi and I got a decent output(I'm not sure if it's perfect or not). Now I need to find how to execute this command every time when I run the program and provide the custom image size value. :\