引言
在真实项目中,往往要进行图片的传输和视频的传输,但因为用户上传的图片和视频所占用的大小问题而导致占用服务器的空间多,并且用户访问这些图片的时候因为图片或视频太大而长时间加载,所以要对用户上传的视频和图片进行压缩,减少服务器的占用空间和加载时间。
1.新建一个类
我们先新建一个保存图片或视频的一个类
import platform
class Compress_Pic_or_Video(object):
def __init__(self,filePath,inputName,outName=""):
self.filePath = filePath #文件地址
self.inputName = inputName #输入的文件名字
self.outName = outName #输出的文件名字
self.system_ = platform.platform().split("-",1)[0]#判断是那个操作系统,因为我的服务器是Linux的但我电脑是windows的,所以只设置了这两个系统的判断
if self.system_ == "Windows":#对windows的文件格式进行设置
self.filePath = (self.filePath + "\\") if self.filePath.rsplit("\\",1)[-1] else self.filePath
elif self.system_ == "Linux":#对Linux的文件格式进行设置
self.filePath = (self.filePath + "/") if self.filePath.rsplit("/",1)[-1] else self.filePath
self.fileInputPath = self.filePath + inputName#拼接输入的路径
self.fileOutPath = self.filePath + outName#拼接输出的路径
2.设置判断文件是否是图片或视频
@property
def is_picture(self):
picSuffixSet = {"BMP","GIF","JPEG","TIFF","PNG","SVG","PCX","WMF","EMF","LIC","EPS","TGA","JPG"}#常用后缀,集合的搜索速度快,所以用集合而不用列表,还能做到去重,避免输入了重复的后缀名
suffix = self.fileInputPath.rsplit(".",1)[-1].upper()
if suffix in picSuffixSet:
return True
else:
return False
@property
def is_video(self):
videoSuffixSet = {"WMV","ASF","ASX","RM","RMVB","MP4","3GP","MOV","M4V","AVI","DAT","MKV","FIV","VOB"}
suffix = self.fileInputPath.rsplit(".",1)[-1].upper()
if suffix in videoSuffixSet:
return True
else:
return False
3.对上传的图片进行压缩
import zlib#压缩的库
import threading#多线程
from PIL import Image
def SavePic(self):
fpsize = os.path.getsize(self.fileInputPath) / 1024 # 获得图片多少K os.path.getsize(self.picPath)返回的是字节
if fpsize >= 50.0: # 是否大于50K
im = Image.open(self.fileInputPath) # 打开图片
imBytes = im.tobytes() # 把图片转换成bytes流
imBytes = zlib.compress(imBytes, 5) # 对图像字节串进行压缩,第二个参数是压缩率有-1,0-9,按个人情况进行设置,我都试过了感觉没多大变化,所以随便设置了5
im2 = Image.frombytes('RGB', im.size, zlib.decompress(imBytes)) # 压缩成新的图片
if self.outName:
im2.save(self.fileOutPath) #不覆盖原图
return (self.fileOutPath,os.path.getsize(self.fileOutPath))
else:
im2.save(self.fileInputPath) #覆盖原图
return (self.fileInputPath,os.path.getsize(self.fileInputPath))
else:
return True
def Compress_Picture(self):
#异步保存打开下面的代码,注释同步保存的代码,我选用的是异步保存,因为如果图片太大用同步的话会阻塞程序的运行,按自己的情况去选择,也可以设置一个变量,然后用判断来看是否启动多线程保存
thr = threading.Thread(target=self.SavePic)
thr.start()
#下面为同步保存
# fpsize = os.path.getsize(self.fileInputPath) / 1024 # 获得图片多少K os.path.getsize(self.picPath)返回的是字节
# if fpsize >= 50.0: # 是否大于50K
# im = Image.open(self.fileInputPath) # 打开图片
# imBytes = im.tobytes() # 把图片转换成bytes流
# imBytes = zlib.compress(imBytes, 5) # 对图像字节串进行压缩
# im2 = Image.frombytes('RGB', im.size, zlib.decompress(imBytes)) # 压缩成新的图片
# if self.outName:
# im2.save(self.fileOutPath) # 不覆盖原图
# return (self.fileOutPath, os.path.getsize(self.fileOutPath) / 1024)
# else:
# im2.save(self.fileInputPath) # 覆盖原图
# return (self.fileInputPath, os.path.getsize(self.fileInputPath) / 1024)
# else:
# return True
4.对上传的视频进行压缩
import os
def SaveVideo(self):
fpsize = os.path.getsize(self.fileInputPath) / 1024
if fpsize >= 150.0: #大于150KB的视频需要压缩
if self.outName:
compress = "ffmpeg -i {} -r 10 -pix_fmt yuv420p -vcodec libx264 -preset veryslow -profile:v baseline -crf 23 -acodec aac -b:a 32k -strict -5 {}".format(self.fileInputPath,self.fileOutPath)
isRun = os.system(compress)
else:
compress = "ffmpeg -i {} -r 10 -pix_fmt yuv420p -vcodec libx264 -preset veryslow -profile:v baseline -crf 23 -acodec aac -b:a 32k -strict -5 {}".format(self.fileInputPath, self.fileInputPath)
isRun = os.system(compress)
if isRun != 0:
return (isRun,"没有安装ffmpeg")
return True
else:
return True
def Compress_Video(self):
# 异步保存打开下面的代码,注释同步保存的代码
thr = threading.Thread(target=self.SaveVideo)
thr.start()
#下面为同步代码
# fpsize = os.path.getsize(self.fileInputPath) / 1024
# if fpsize >= 150.0: # 大于150KB的视频需要压缩
# compress = "ffmpeg -i {} -r 10 -pix_fmt yuv420p -vcodec libx264 -preset veryslow -profile:v baseline -crf 23 -acodec aac -b:a 32k -strict -5 {}".format(
# self.fileInputPath, self.fileOutPath)
# isRun = os.system(compress)
# if isRun != 0:
# return (isRun, "没有安装ffmpeg")
# return True
# else:
# return True
我这里使用了是ffmpeg对视频进行压缩,这个是一个非常好用的工具,Linux和windows都有,在Linux中可以使用apt install ffmpeg进行安装(我用的是Ubuntu的系统,所以使用apt进行安装,如果用的是Centos的用对应的yum进行安装就行了)
ffmpeg命令解释
ffmpeg -i 1.mp4 -r 10 -pix_fmt yuv420p -vcodec libx264 -preset veryslow -profile:v baseline -crf 23 -acodec aac -b:a 32k -strict -5 147fss.mp4
-i 输入的视频文件
-r 每一秒的帧数,一秒10帧大概就是人眼的速度
-pix_fmt 设置视频颜色空间 yuv420p网络传输用的颜色空间 ffmpeg -pix_fmts可以查看有哪些颜色空间选择
-vcodec 软件编码器,libx264通用稳定
-preset 编码机预设 编码机预设越高占用CPU越大 有十个参数可选 ultrafast superfast veryfast(录制视频选用) faster fast medium(默认) slow slower veryslow(压制视频时一般选用) pacebo
-profile:v 压缩比的配置 越往左边压缩的越厉害,体积越小 baseline(实时通信领域一般选用,画面损失越大) Extended Main(流媒体选用) High(超清视频) High 10 High 4:2:2 High 4:4:4(Predictive)
-level:v 对编码机的规范和限制针对不通的使用场景来操作,也就是不同分辨率设置不同的值(这个我没有设置,因为这个要根据不同的分辨率进行设置的,具体要去官方文档查看)
-crf 码率控制模式 用于对画面有要求,对文件大小无关紧要的场景 0-51都可以选择 0为无损 一般设置18 - 28之间 大于28画面损失严重
-acodec 设置音频编码器
完整代码
import sys
import os
import zlib
import threading
import platform
from PIL import Image
class Compress_Pic_or_Video(object):
def __init__(self,filePath,inputName,outName=""):
self.filePath = filePath #文件地址
self.inputName = inputName #输入的文件名字
self.outName = outName #输出的文件名字
self.system_ = platform.platform().split("-",1)[0]
if self.system_ == "Windows":
self.filePath = (self.filePath + "\\") if self.filePath.rsplit("\\",1)[-1] else self.filePath
elif self.system_ == "Linux":
self.filePath = (self.filePath + "/") if self.filePath.rsplit("/",1)[-1] else self.filePath
self.fileInputPath = self.filePath + inputName
self.fileOutPath = self.filePath + outName
@property
def is_picture(self):
picSuffixSet = {"BMP","GIF","JPEG","TIFF","PNG","SVG","PCX","WMF","EMF","LIC","EPS","TGA","JPG"}
suffix = self.fileInputPath.rsplit(".",1)[-1].upper()
if suffix in picSuffixSet:
return True
else:
return False
@property
def is_video(self):
videoSuffixSet = {"WMV","ASF","ASX","RM","RMVB","MP4","3GP","MOV","M4V","AVI","DAT","MKV","FIV","VOB"}
suffix = self.fileInputPath.rsplit(".",1)[-1].upper()
if suffix in videoSuffixSet:
return True
else:
return False
def SavePic(self):
fpsize = os.path.getsize(self.fileInputPath) / 1024 # 获得图片多少K os.path.getsize(self.picPath)返回的是字节
if fpsize >= 50.0: # 是否大于50K
im = Image.open(self.fileInputPath) # 打开图片
imBytes = im.tobytes() # 把图片转换成bytes流
imBytes = zlib.compress(imBytes, 5) # 对图像字节串进行压缩
im2 = Image.frombytes('RGB', im.size, zlib.decompress(imBytes)) # 压缩成新的图片
if self.outName:
im2.save(self.fileOutPath) #不覆盖原图
return (self.fileOutPath,os.path.getsize(self.fileOutPath))
else:
im2.save(self.fileInputPath) #覆盖原图
return (self.fileInputPath,os.path.getsize(self.fileInputPath))
else:
return True
def SaveVideo(self):
fpsize = os.path.getsize(self.fileInputPath) / 1024
if fpsize >= 150.0: #大于150KB的视频需要压缩
if self.outName:
compress = "ffmpeg -i {} -r 10 -pix_fmt yuv420p -vcodec libx264 -preset veryslow -profile:v baseline -crf 23 -acodec aac -b:a 32k -strict -5 {}".format(self.fileInputPath,self.fileOutPath)
isRun = os.system(compress)
else:
compress = "ffmpeg -i {} -r 10 -pix_fmt yuv420p -vcodec libx264 -preset veryslow -profile:v baseline -crf 23 -acodec aac -b:a 32k -strict -5 {}".format(self.fileInputPath, self.fileInputPath)
isRun = os.system(compress)
if isRun != 0:
return (isRun,"没有安装ffmpeg")
return True
else:
return True
def Compress_Picture(self):
#异步保存打开下面的代码,注释同步保存的代码
thr = threading.Thread(target=self.SavePic)
thr.start()
#下面为同步保存
# fpsize = os.path.getsize(self.fileInputPath) / 1024 # 获得图片多少K os.path.getsize(self.picPath)返回的是字节
# if fpsize >= 50.0: # 是否大于50K
# im = Image.open(self.fileInputPath) # 打开图片
# imBytes = im.tobytes() # 把图片转换成bytes流
# imBytes = zlib.compress(imBytes, 5) # 对图像字节串进行压缩
# im2 = Image.frombytes('RGB', im.size, zlib.decompress(imBytes)) # 压缩成新的图片
# if self.outName:
# im2.save(self.fileOutPath) # 不覆盖原图
# return (self.fileOutPath, os.path.getsize(self.fileOutPath) / 1024)
# else:
# im2.save(self.fileInputPath) # 覆盖原图
# return (self.fileInputPath, os.path.getsize(self.fileInputPath) / 1024)
# else:
# return True
def Compress_Video(self):
# 异步保存打开下面的代码,注释同步保存的代码
thr = threading.Thread(target=self.SaveVideo)
thr.start()
#下面为同步代码
# fpsize = os.path.getsize(self.fileInputPath) / 1024
# if fpsize >= 150.0: # 大于150KB的视频需要压缩
# compress = "ffmpeg -i {} -r 10 -pix_fmt yuv420p -vcodec libx264 -preset veryslow -profile:v baseline -crf 23 -acodec aac -b:a 32k -strict -5 {}".format(
# self.fileInputPath, self.fileOutPath)
# isRun = os.system(compress)
# if isRun != 0:
# return (isRun, "没有安装ffmpeg")
# return True
# else:
# return True
if __name__ == "__main__":
b = sys.argv[1:]#测试压缩
savepic = Compress_Pic_or_Video(b[0],b[1],b[2])
print(savepic.Compress_Picture())
测试情况
对图片进行压缩
左边为没压缩过的原图,右边是压缩过后的原图,两张图片像素都是一样的,两张图片用肉眼几乎分辨不出差别
对视频进行压缩
左边为未压缩过的原视频文件,右边为压缩过的视频文件,可见帧宽度与帧高度都没发生过改变,只是修改了数据速率,总比特率等的这些信息,源文件帧速率为每秒12帧,现在压缩成了每秒10帧,人眼是没办法分别出12帧与10帧的区别的,我测试过原视频和压缩后的视频观看上是没有却别的,声轨也听不出。
参考的文章