您现在的位置是:网站首页> PY&Rust
Python OpencV
- PY&Rust
- 2023-05-22
- 825人已阅读
Python OpenCV
OpenCV+FFmpeg 实现人脸检测Rtmp直播推流(Python快速实现)
1. 简介:OpenCV的全称是 Open Source Computer Vision Library,是一个跨平台的开源计算机视觉库。由Gary Bradsky于1999年在英特尔发起并创立。这里使用的OpenCV版本为:4.5.2
查看当前的Python版本的OpenCV版本:
# 导入OpenCV模块
import cv2
# 打印版本信息
print(cv2.__version__)
2. 基本语法:
读取一张图片并显示:
# 导入模块
import cv2
# 读取图片
# cv.IMREAD_COLOR: 加载彩色图像。任何图像的透明度都会被忽视。它是默认标志。
# cv.IMREAD_GRAYSCALE:以灰度模式加载图像
# cv.IMREAD_UNCHANGED:加载图像,包括alpha通道
img=cv2.imread("./A.png",cv2.IMREAD_UNCHANGED)
# 显示图片
cv2.imshow("Open img",img)
# 等待用户按下按键
k==cv2.waitKey(0)
# 判断并打印
if k==ord('a'):
print('a')
elif k==ord('b'):
print('b')
# 销毁窗口
cv2.destroyAllWindows()
修改颜色通道的像素值: B\G\R 通道分别表示:0\1\2
img[100,100,1]=255 # 给100行,100列的绿色通道的像素值赋值为:255
print(img[100,100]) # 打印100行,100列的所有通道像素值
print(img[100,100,2]) # 打印100行,100列的红色通道像素值
获取图像分辨率、像素、位图类型
print(img.shape) # 获取xy方向的像素分布个数--分辨率
print(img.size) # 获取图像的像素值总数
print(img.dtype) # 位图类型:8位、24位
图像裁剪:
import cv2
import numpy as np
img=cv2.imread("contours.BMP",cv2.IMREAD_UNCHANGED)
b=np.ones((150,150,3)) #创建一个 150行150列3维 数组
b=img[100:200,150:300] # 裁剪原图 x(startx,endx) y(starty,endy)
cv2.imshow("Original",img) # 原图
cv2.imshow("CutImg",b) # 裁剪后的图
cv2.waitKey()
cv2.destroyAllWindows()
5. 通道分离与合并:
import cv2
import numpy as np
a=cv2.imread("gril.png",cv2.IMREAD_UNCHANGED)
# 拆分通道:b,g,r
b,g,r=cv2.split(a)
cv2.imshow("b",b)
cv2.imshow("g",g)
cv2.imshow("r",r)
# 合并通道
m=cv2.merge([b,g,r])
cv2.imshow("merge",m)
cv2.waitKey()
cv2.destroyAllWindows()
运行
6. 图像加法运算:
区别:
a+b:(a+b)%256 如果大于255,则除以256,然后结果取余
cv2.add(a+b):如果(a+b)大于255,则等于255,如果小于255,等于原值
import cv2
a=cv2.imread("lena.bmp",0)
b=a
result1=a+b
result2=cv2.add(a,b)
# 显示
cv2.imshow("original",a)
cv2.imshow("result1",result1)
cv2.imshow("result2",result2)
cv2.waitKey()
cv2.destroyAllWindows()
图像加权和:img1 x weighted1 + img2 x weighted2 + brightness
可以弱化或者加强两张图片的权重值,比如:将img1的人脸弱化,将img2的人脸加强,就可以实现换脸。
# (a=img1,1=weighted,b=img2,0.3=weighted,100=brightness)
result=cv2.addWeighted(a,1,b,0.3,100)
import cv2
lena=cv2.imread("lena512.bmp",cv2.IMREAD_UNCHANGED)
dollar=cv2.imread("dollar.bmp",cv2.IMREAD_UNCHANGED)
cv2.imshow("lena",lena)
cv2.imshow("dollar",dollar)
face1=lena[220:400,250:350] # 截取小姐姐脸上x方向220-400,y方向250-350之间的像素
face2=dollar[160:340,200:300] # 截取美刀x方向160-340,y方向200-300之间的像素
add=cv2.addWeighted(face1,0.8,face2,0.3,0) # 分配权重,得到新图
dollar[160:340,200:300]=add # 替换
cv2.imshow("result",dollar)
cv2.waitKey()
cv2.destroyAllWindows()
7. 按位逻辑运算(与运算:与255得原值,与0得0)
import cv2
import numpy as np
# 创建一个5x5的8位数组,元素值在0-255之间随机
a=np.random.randint(0,255,(5,5),dtype=np.uint8)
# 创建一个5x5的8位数组
b=np.zeros((5,5),dtype=np.uint8)
# 全白
b[0:4,0:4]=255
# 按位与运算:加边框
c=cv2.bitwise_and(a,b)
print("a=\n",a)
print("b=\n",b)
print("c=\n",c)
运行:
8. 图像转换:
import cv2
a=cv2.imread("gril.png",cv2.IMREAD_UNCHANGED)
b=cv2.cvtColor(a,cv2.COLOR_BGR2GRAY) # color=>gray
cv2.imshow("RGB",a)
cv2.imshow("GRAY",b)
cv2.waitKey()
cv2.destroyAllWindows()
图像几何变换:
# 缩放
import cv2
img=cv2.imread("D:\OpenCV\OpenCV\gril.png")
rows,cols=img.shape[:2] # 取图像的行、列 [:3]表示取行、列、通道
size=(int(cols*0.5),int(rows*0.5)) # 行列缩小2倍
rst=cv2.resize(img,size) # 缩放
# rst=cv2.resize(img,None,fx=0.5,fy=0.5) 另一种写法
print("img.shape=",img.shape)
print("rst.shape=",rst.shape)
cv2.imshow("o",img)
cv2.imshow("res",rst)
cv2.waitKey()
cv2.destroyAllWindows()
运行
img.shape= (480, 370, 3)
rst.shape= (240, 185, 3)
# 翻转
import cv2
img=cv2.imread("lena.bmp")
x=cv2.flip(img,0) # 沿x轴翻转
y=cv2.flip(img,1) # 沿y轴翻转
xy=cv2.flip(img,-1) # 沿xy轴翻转
cv2.imshow("img",img)
cv2.imshow("x",x)
cv2.imshow("y",y)
cv2.imshow("xy",xy)
cv2.waitKey()
cv2.destroyAllWindows()
运行
# 平移
import cv2
import numpy as np
img=cv2.imread("lena.bmp")
height,width=img.shape[:2]
x=img.shape[0]/2 # x一半
y=img.shape[1]/2 # y一半
# 建立一个2x3的矩阵,结构如下:
# [[ 1. 0. x.]
# [ 0. 1. y.]]
M = np.float32([[1, 0, x], [0, 1, y]])
# 仿射变换:将原图的xy分别平移到图像中心
move=cv2.warpAffine(img,M,(width,height))
print(M) # 打印 M 矩阵
cv2.imshow("original",img)
cv2.imshow("move",move)
cv2.waitKey()
cv2.destroyAllWindows()
运行:
[[ 1. 0. 256.]
[ 0. 1. 256.]]
# 旋转
import cv2
img=cv2.imread("lena.bmp")
height,width=img.shape[:2]
# getRotationMatrix2D(旋转中心,旋转角度,缩放比例),返回一个矩阵
# 旋转角度:+为逆时针 -为顺时针
M=cv2.getRotationMatrix2D((width/2,height/2),-45,0.5)
# 仿射变换
rotate=cv2.warpAffine(img,M,(width,height))
print(M)
cv2.imshow("original",img)
cv2.imshow("rotation",rotate)
cv2.waitKey()
cv2.destroyAllWindows()
运行
[[ 0.35355339 -0.35355339 256. ]
[ 0.35355339 0.35355339 74.98066402]]
# 仿射变换
import cv2
import numpy as np
img=cv2.imread('lena.bmp')
rows,cols,ch=img.shape
# 创建p1矩阵,3个点分别是原图的左上角、右上角、左下角
# [[ 0. 0.] 左上点
# [512. 0.] 右上点
# [ 0. 512.]] 左下点
p1=np.float32([[0,0],[rows,0],[0,cols]])
# 创建p2矩阵,3个点分别是:
# [[ 0. 256.] 左上点
# [256. 256.] 右上点
# [256. 512.]] 左下点
p2=np.float32([[0,cols*0.5],[rows*0.5,cols*0.5],[rows*0.5,cols*1]])
# 获取转换矩阵M
M=cv2.getAffineTransform(p1,p2)
# 通过转换矩阵M,完成从原始图像到目标图像的仿射变换
dst=cv2.warpAffine(img,M,(cols,rows))
cv2.imshow("origianl",img)
cv2.imshow("result",dst)
cv2.waitKey()
cv2.destroyAllWindows()
运行:
# 透视变换:将矩形映射为任意平行四边形
import cv2
import numpy as np
img=cv2.imread('demo.bmp')
rows,cols=img.shape[:2]
# 原始矩形的四个顶点坐标
pts1 = np.float32([[150,50],[400,50],[60,450],[310,450]])
# 目标矩形的四个顶点坐标
pts2 = np.float32([[50,50],[rows-50,50],[50,cols-50],[rows-50,cols-50]])
# 获取转换矩阵
M=cv2.getPerspectiveTransform(pts1,pts2)
# 透视转换
dst=cv2.warpPerspective(img,M,(cols,rows))
cv2.imshow("img",img)
cv2.imshow("dst",dst)
cv2.waitKey()
cv2.destroyAllWindows()
运行:
先写到这里,Over!!!
接前面
1. 阈值处理: threshold()
import cv2
import numpy as np
# 创建一个8位的4行5列的矩阵,元素0-255之间随机
img=np.random.randint(0,256,size=[4,5],dtype=np.uint8)
# thrshold(图像,
# 阈值,
# 当后面的分割类型为Binary或者Binary_Inv时,这里表示最大阈值,
# 分割类型)
t,rst=cv2.threshold(img,127,255,cv2.THRESH_BINARY)
print("img=\n",img)
print("t=",t)
print("rst=\n",rst)
分割类型:
THRESH_BINARY: 大于阈值为255,小于阈值等于0
THRESH_BINARY_INV: 大于阈值为0,小于阈值为255
THRESH_TRUNC: 截断处理,大于阈值等于阈值,小于阈值为原值
THRESH_TOZERO: 低于阈值为0,高于阈值为原值
THRESH_TOZERO_INV: 低于阈值为原值,高于阈值为0
运行:
低阈值0处理:THRESH_TOZERO 示例
import cv2
img=cv2.imread("lena.bmp")
t,rst=cv2.threshold(img,127,255,cv2.THRESH_TOZERO)
cv2.imshow("img",img)
cv2.imshow("rst",rst)
cv2.waitKey()
cv2.destroyAllWindows()
运行:
2. 自适应阈值处理: adaptiveThreshold() 通过计算每个像素周围邻近区域的加权平均值获得阈值,可以很好的处理明暗差异较大的情况。
import cv2
img=cv2.imread("computer.jpg",0)
t1,thd=cv2.threshold(img,127,255,cv2.THRESH_BINARY)
# adaptiveThreshold(原图,
# 最大值,
# 自适应方法,
# 阈值处理方法:THRESH_BINARY 或者 THRESH_BINARY_INV,
# 邻域大小:必须是基数,不然无法确定中心点)
athdMEAN=cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,3,5)
athdGAUS=cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,3,5)
cv2.imshow("img",img)
cv2.imshow("thd",thd)
cv2.imshow("athdMEAN",athdMEAN)
cv2.imshow("athdGAUS",athdGAUS)
cv2.waitKey()
cv2.destroyAllWindows()
运行:
3. 均值滤波: 使用当前像素点周围的N*N个像素值均值,来代替当前像素值。 dst = mean(x,y)
这里不对算法做具体的分析了,详细的可以看:点击
import cv2
o=cv2.imread("image\\lenaNoise.png")
# blur(原图,
# 滤波核(x,y))
r=cv2.blur(o,(5,5))
cv2.imshow("original",o)
cv2.imshow("result",r)
cv2.waitKey()
cv2.destroyAllWindows()
运行:
4. 方框滤波: 与均值滤波唯一的不同点在于,方框滤波还可以选择不归一化,返回邻域像素相加之和。
import cv2
a=cv2.imread("./gril.png")
# 原图,
# 图像深度:-1= 与原图一致,
# 滤波核大小(x,y)
# normalize=1: 归一化处理,类似均值, mean(sum(x,y))
# normalize=0: 不归一化处理,只求和,sum(x,y)
m=cv2.boxFilter(a,-1,(5,5),normalize=1)
r=cv2.boxFilter(a,-1,(2,2),normalize=0)
cv2.imshow("o",a)
cv2.imshow("m",m)
cv2.imshow("r",r)
cv2.waitKey()
cv2.destroyAllWindows()
运行:r 图像是非归一化处理的,所以2行2列像素值相加很容易超过255,所以图像偏亮。
5. 高斯滤波: 离中心点近的权重高,离中心点远的权重低。
import cv2
a=cv2.imread("./gril.png",cv2.IMREAD_UNCHANGED)
# 像素值 权重比
# 15 65 84 0.05 0.1 0.05
# 65 32 14 x 0.1 0.1 0.1
# 15 54 88 0.05 0.1 0.05
# 注意:GaussianBlur 中的卷积核权重可以默认:归一化后的权重比例
r=cv2.GaussianBlur(a,(5,5),0)
cv2.imshow("rsc",a)
cv2.imshow("result",r)
cv2.waitKey()
cv2.destoyAllWindows()
运行:
6. 中值滤波: 与均值滤波不同,中值算法不需要求均值,而是对邻域像素排序,取中间值代替当前像素。由于要排序,该算法运算量较大。
import cv2
o=cv2.imread("image\\lenaNoise.png")
# 中值滤波
# 3:核尺寸
r=cv2.medianBlur(o,3)
cv2.imshow("original",o)
cv2.imshow("result",r)
cv2.waitKey()
cv2.destroyAllWindows()
7. 双边处理: 能有效的保护图像的边缘信息。
import cv2
o=cv2.imread("image\\lenaNoise.png")
# o:原图
# 25:滤波空间距离参数,表示以当前像素点为中心的直径
# 100:选取的颜色差值范围,表示低于该值的像素可以参与到滤波中
# 75:与上面类似
r=cv2.bilateralFilter(o,9,100,75)
cv2.imshow("original",o)
cv2.imshow("result",r)
cv2.waitKey()
cv2.destroyAllWindows()
运行:
今天先写到这里,Over!
python opencv将图片合成视频,并插入音频
一、统一图片大小及类型,并按数字排序
import cv2
import os
path = './test' #源目录
out_path = './image' #修改之后的目录
dirs = os.listdir(path)
i=1
# 输出所有文件和文件夹
for item in dirs:
image_path = './test/{}'.format(item)
#print(image_path)
image = cv2.imread(image_path)
image = cv2.resize(image, (480, 320), interpolation=cv2.INTER_CUBIC)
image_path = './image/{}.jpg'.format(str(i))
cv2.imwrite(image_path, image)
print(image_path)
i+=1
print(item)
#打印原始名字
print(i)
#打印处理后照片名字
print("################## Resize done ########################"
二、图片合成视频
import cv2
import os
import sys
from itertools import cycle
from ffmpy import FFmpeg
frame_path = "./image" #图片目录。 图片像素大小需一致
filenames = os.listdir(frame_path)
# 通过itertools.cycle生成一无限循环的迭代器,每次迭代都输出下一章图像对象
img_iter = cycle([cv2.imread(os.sep.join([frame_path, x])) for x in filenames])
# 定义编码方式并创建VideoWriter对象
fourcc = cv2.VideoWriter_fourcc(*'MJPG')# *'XVID' 视频编解码器
outfile = cv2.VideoWriter("./output.avi", fourcc, 5, (480, 320)) #大小必须和图片大小一致,且所以图片大小必须一致 -- photo_resize.py
for i in range(100):
outfile.write(next(img_iter)) # 视频文件写入一帧
#cv2.imshow('frame', next(img_iter))
if cv2.waitKey(1) == 27: # 按下Esc键,程序退出(Esc的ASCII值是27,即0001 1011)
break
cv2.destroyAllWindows()
print("################## Video done ########################")
其中包含了幻灯片播放的功能:
# OpenCV窗口循环显示
frame_path = "./test"
filenames = os.listdir(frame_path)
print(filenames)
# 通过itertools.cycle生成一无限循环的迭代器,每次迭代都输出下一章图像对象
img_iter = cycle([cv.imread(os.sep.join([frame_path, x])) for x in filenames])
print(img_iter)
key = 0
# 按ESC键结束循环播放,ESC的ASCII码为27,即0001 1011
while key & 0xFF != 27:
#cv.namedWindow可以使窗口统一,但是图像会失真,不加则窗口大小不同
cv.namedWindow('Kawaii Small Animals',cv.WINDOW_NORMAL)
cv.imshow('Kawaii Small Animals', next(img_iter))
key = cv.waitKey(500)
# cv.waitKey()参数不为0的时候则可以和循环结合产生动态画面
cv.destroyAllWindows()
三、音频的合并与剪裁
可以根据需要自己修改合成自己需要的音频:拼接,合并,叠加,添加静音,音频切割等。
from pydub import AudioSegment
#拼接音频
print("-----")
sound1 = AudioSegment.from_wav('a.wav') #绝对路径
sound2 = AudioSegment.from_wav('b.wav') #绝对路径
output = sound1 + sound2 # sound1拼接sound2
output.export("c.wav", format="wav") # 保存文件
#添加静音
silence_ring = AudioSegment.silent(int(3*1000))
output = sound1 + silence_ring + sound2
output.export("d.wav", format="wav") # 保存文件
#音频切割
sound= AudioSegment.from_wav("c.wav")
duration = sound.duration_seconds #音频时长s
cut_wav = sound[0:10*1000] #以毫秒为单位截取[begin, end]区间的音频
#cut_wav.export(filePath+ 'test.wav', format='wav') #存储新的wav文件
cut_wav.export('f.wav', format='wav') #存储新的wav文件
print("----contact done----")
############## 音频合并 音频叠加 ############
#音频合并: 先确认通道数,采样率,音频时长是否一致
sound3 = AudioSegment.from_wav('t.wav') #绝对路径
sound = sound3
print("")
print("t.wav")
#取得音频的声道数
channel_count = sound.channels
print(channel_count)
#取得音频文件采样频率
frames_per_second = sound.frame_rate
print(frames_per_second)
#取得音频的持续时间,同 len()
print(sound.duration_seconds)
print((len(sound) / 1000.0))
t3=sound.duration_seconds
sound = sound1
silence_ring = AudioSegment.silent(int(sound3.duration_seconds*1000-sound1.duration_seconds*1000)) #ms
output = sound + silence_ring
output.export("a1.wav", format="wav") # 保存文件
print("")
print("a.wav")
print(sound1.channels)
print(sound1.frame_rate)
print(sound1.duration_seconds)
sound_1 = AudioSegment.from_wav('a1.wav') #绝对路径
print("")
print("a1.wav")
print(sound_1.channels) #通道数
sound_1.frame_rate = 8000 #修改采样率
print(sound_1.frame_rate) #采样率
sound_1 = sound_1[:(t3*1000)] #截取指定时长音频
print(sound_1.duration_seconds) #音频时长
sound_1.export("a2.wav", format="wav") # 保存文件
sound_2 = AudioSegment.from_wav('a2.wav') #绝对路径
print("")
print("a2.wav")
print(sound_2.channels)
print(sound_2.frame_rate)
print(sound_2.duration_seconds)
# 把sound1和sound2合并成两通道音频
output = AudioSegment.from_mono_audiosegments(sound_2, sound3) #1,2的时长,帧率必须相同,且为单通道
output.export("mono.wav", format="wav") # 保存文件
print("")
print("mono.wav")
print(output.channels) #合并后变双通道
print(output.frame_rate)
print(output.duration_seconds)
# 叠加: 把sound2叠加到sound1上面
output = sound1.overlay(sound3)
# output = sound1.overlay(sound2,position=5000) # 把sound2叠加到sound1上面,从第5秒开始叠加
output.export("overlay.wav", format="wav") # 保存文件
print("")
print("overlay.wav")
print(output.channels) #还是sound1的音频信息 channels frame_rate duration_seconds
print(output.frame_rate)
print(output.duration_seconds)
output = sound3.overlay(sound1)
output.export("overlay2.wav", format="wav") # 保存文件
print("")
print("overlay2.wav")
print(output.channels) #还是sound3的音频信息 channels frame_rate duration_seconds
print(output.frame_rate)
print(output.duration_seconds)
四、音视频融合
测试发现:音频与视频融合时,以时间短的为最终视频长度(len_audio, len_vedio),所以最好两者长度一致。
from ffmpy import FFmpeg
video_path="./output.avi"
audio_path="./f.wav"
_codec = 'aac'
result="va.avi"
ff = FFmpeg(executable='C:\\Users\\***\\Python\\Python39\\Lib\\site-packages\\imageio_ffmpeg\\binaries\\ffmpeg-win64-v4.2.2.exe',inputs={video_path: None, audio_path: None},outputs={result: '-map 0:v -map 1:a -c:v copy -c:a {} -shortest'.format(_codec)}) #若修改环境变量,则不需要指定ffmpeg程序路径
print(ff.cmd)
ff.run()
Python-混音、叠加音频、拼接音频及批处理
一.首先我们来了解混音、叠加音频、拼接音频的概念
1.1 混音:把单声道音频a和单声道音频b(可以是两个单声道音频或更多单声道音频)合并成一个多声道音频c。需要注意的是音频a和音频b的时长要相同且都为单声道音频。(音频c的时长=音频a的时长)
1.2 叠加音频:把音频a和音频b叠加成音频c,单声道音频a和单声道音频b叠加成单声道音频c,双声道音频a和单声道音频b叠加成双声道音频c,双声道音频a和双声道音频b叠加成双声道音频c。(如果把音频b叠加在音频a上,那么音频c的时长和音频a的时长相同,反之如果把音频a叠加在音频b上,那么音频c的时长和音频b的时长相同)
1.3 拼接音频:把音频a和音频b拼接起来成一个长音频c(即音频c的时长=音频a的时长+音频b的时长)
二.代码示例:
2.1 混音代码:
from pydub import AudioSegment
sound1 = AudioSegment.from_wav(r'F:\误入测试集\dianshiju-0.wav')
sound2 = AudioSegment.from_wav(r'F:\误入测试集\dianshiju-14.wav')
# sound3 = AudioSegment.from_wav(r'F:\误入测试集\dianshiju-16.wav')
output = AudioSegment.from_mono_audiosegments(sound1, sound2) # 把sound1和sound2合并成两通道音频
# output = AudioSegment.from_mono_audiosegments(sound1, sound2,sound3) # 把sound1、sound2、sound3合并成三通道音频
output.export("output.wav", format="wav") # 保存文件
2.2 叠加音频代码:
from pydub import AudioSegment
sound1 = AudioSegment.from_wav(r'F:\误入测试集\dianshiju-0.wav')
sound2 = AudioSegment.from_wav(r'F:\误入测试集\dianshiju-14.wav')
output = sound1.overlay(sound2) # 把sound2叠加到sound1上面
# output = sound1.overlay(sound2,position=5000) # 把sound2叠加到sound1上面,从第5秒开始叠加
output.export("output.wav", format="wav") # 保存文件
2.3 拼接音频代码
from pydub import AudioSegment
sound1 = AudioSegment.from_wav(r'F:\误入测试集\dianshiju-0.wav')
sound2 = AudioSegment.from_wav(r'F:\误入测试集\dianshiju-14.wav')
# sound3 = AudioSegment.from_wav(r'F:\误入测试集\dianshiju-16.wav')
output = sound1 + sound2 # sound1拼接sound2
# output=sound1+sound2+sound3 # sound1拼接sound2拼接sound3
output.export("output.wav", format="wav") # 保存文件
三、敲重点!!!批处理
3.1 一个文件夹的批处理,即一个文件夹内的音频文件和一个音频文件进行批处理
import os
from pydub import AudioSegment
input_path = r"E:\untitled1\audio_test\a" # 第一个文件路径
output_path = r"E:\untitled1\audio_test\output" # 输出文件路径
for file in os.listdir(input_path): # 遍历第一个文件路径下的文件
path1 = input_path + "\\" + file # 拼接第一个输入路径和对应文件名
path3 = output_path + "\\" + file # 拼接输出路径和输出文件名,这里我是以第一个输入的文件名命名
sound1 = AudioSegment.from_wav(path1) # 第一个文件
sound2 = AudioSegment.from_wav(r"E:\untitled1\audio_test\b\爱惜花草不践踏_高清_1.wav") # 第二个文件
output = sound1.overlay(sound2) # 把sound2叠加到sound1上面
output.export(path3, format="wav") # 保存文件
3.2 两个文件夹的批处理,即两个文件夹分别有相同数量的音频文件进行批处理
import os
from pydub import AudioSegment
input_path1 = r"E:\untitled1\audio_test\a" # 第一个文件路径
input_path2 = r"E:\untitled1\audio_test\b" # 第二个文件路径
output_path = r"E:\untitled1\audio_test\output" # 输出文件路径
for file1, file2 in zip(os.listdir(input_path1), os.listdir(input_path2)): # 遍历两个文件路径下的文件
path1 = input_path1 + "\\" + file1 # 拼接第一个输入路径和对应文件名
path2 = input_path2 + "\\" + file2 # 拼接第二个输入路径和对应文件名
path3 = output_path + "\\" + file1 # 拼接输出路径和输出文件名,这里我是以第一个输入的文件名命名
sound1 = AudioSegment.from_wav(path1) # 第一个文件
sound2 = AudioSegment.from_wav(path2) # 第二个文件
output = sound1.overlay(sound2) # 把sound2叠加到sound1上面
output.export(path3, format="wav") # 保存文件
四、处理上面问题可能涉及的相关操作
① 对音频文件进行批处理切割,可以看我以前的这篇博客Python-将一整段音频批量切成一小段一小段的
② 对音频文件进行批处理修改通道数,可以看我上一篇博客Python-批处理修改音频文件的通道数
③对音频进行批处理补齐,可以看我新更的这篇博客Python-音频补齐(即对不同长度的音频用数据零对齐补位)
Python 视频添加音频(附代码) | Python工具
fmpy安装:
pip install ffmpy -i https://pypi.douban.com/simple
代码
不废话,上代码。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/12/5 1:49
# @Author : 剑客阿良_ALiang
# @Site :
# @File : video_add_audio_tool.py
import os
import uuid
from ffmpy import FFmpeg
# 视频添加音频
def video_add_audio(video_path: str, audio_path: str, output_dir: str):
_ext_video = os.path.basename(video_path).strip().split('.')[-1]
_ext_audio = os.path.basename(audio_path).strip().split('.')[-1]
if _ext_audio not in ['mp3', 'wav']:
raise Exception('audio format not support')
_codec = 'copy'
if _ext_audio == 'wav':
_codec = 'aac'
result = os.path.join(
output_dir, '{}.{}'.format(
uuid.uuid4(), _ext_video))
ff = FFmpeg(
inputs={video_path: None, audio_path: None},
outputs={result: '-map 0:v -map 1:a -c:v copy -c:a {} -shortest'.format(_codec)})
print(ff.cmd)
ff.run()
return result
代码说明
1、video_add_audio方法参数有视频地址、音频地址、输出目录。
2、音频支持mp3以及wav格式,其中wav格式ffmpeg命令会有所差别。
if __name__ == '__main__':
print(video_add_audio('data/999.mp4', 'data/2dc9fa4f-802d-4076-b2c3-b1da886a7cc0.wav', 'data/'))
Python快速实现视频播放器
解决方案
安装
pip install pyglet
代码
import sys
import time
import pyglet
from pyglet.window import key, mouse
filename = '1.mp4'
source = pyglet.media.load(filename)
video_format = source.video_format
width, height = video_format.width, video_format.height
title = 'Video Player'
window = pyglet.window.Window(width, height, title, resizable=True)
player = pyglet.media.Player()
player.queue(source)
player.play()
@window.event
def on_draw():
window.clear()
if player.source and player.source.video_format:
player.get_texture().blit(0, 0, width=width, height=height)
@window.event
def on_resize(_width, _height):
global width, height
height = _width * height / width # 按比例缩放后的高
width = _width
def set_fullscreen():
if window.fullscreen:
window.set_fullscreen(False)
else:
window.set_fullscreen(True)
@window.event
def on_key_press(symbol, modifier):
window.key_begin = time.perf_counter()
if symbol == key.SPACE:
if player.playing:
player.pause()
else:
player.play()
elif symbol == key.ESCAPE:
sys.exit()
elif symbol == key.ENTER:
set_fullscreen()
@window.event
def on_key_release(symbol, modifier):
def get_key_value():
key_duration = time.perf_counter() - window.key_begin
key_value = 1 if key_duration < 0.1 else int(key_duration / 0.1)
return key_value
if symbol == key.UP:
player.volume = round(player.volume + 0.1 * get_key_value(), 2)
print(player.volume)
elif symbol == key.DOWN:
player.volume = round(player.volume - 0.1 * get_key_value(), 2)
if player.volume < 0:
player.volume = 0.0
print(player.volume)
elif symbol == key.LEFT:
source.seek(player.time - get_key_value())
elif symbol == key.RIGHT:
source.seek(player.time + get_key_value())
@window.event
def on_mouse_release(x, y, button, modifiers):
if button == mouse.LEFT:
window.last_mouse_release = (x, y, time.perf_counter())
elif button == mouse.MIDDLE:
set_fullscreen()
@window.event
def on_mouse_press(x, y, button, modifiers):
if button == mouse.LEFT and hasattr(window, 'last_mouse_release'):
if (x, y) == window.last_mouse_release[:-1]:
if time.perf_counter() - window.last_mouse_release[-1] < 0.2:
if player.playing:
player.pause()
else:
player.play()
pyglet.app.run()
操作
左键双击播放暂停
中键全屏
空格播放暂停
上下方向键调节音量
ESC 退出
FIXME: 等比调整画面(容易有黑边)
FIXME: 左右方向键调节进度(容易卡顿,且长按会音画不同步)
python播放音频的方法
1.使用pygame模块
pygame.mixer.init()
pygame.mixer.music.load(self.wav_file)
pygame.mixer.music.set_volume(0.5)
pygame.mixer.music.play()
缺点:pygame模块播放音频时,有时候会产生失真,且无法通过修改播放器的频率来矫正音色。
2.使用pyaudio模块
import pyaudio
import wave import sys
chunk = 1024
wf = wave.open('gyh.wav', 'rb')
p = pyaudio.PyAudio()
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()), channels=wf.getnchannels(), rate=wf.getframerate(), output=True)
data = wf.readframes(chunk)
while len(data) > 0:
stream.write(data)
data = wf.readframes(CHUNK)
stream.stop_stream()
stream.close()
p.terminate()
缺点:无,就是代码多点。
OpenCV+FFmpeg 实现人脸检测Rtmp直播推流(Python快速实现)
准备工作
需要先安装OpenCV的python包以及FFmpeg。
对于ffmpeg有两种调用方式,但这两种方式都需要先安装ffmpeg,调用的具体区别是:
使用管道通信的方式,调用FFmpeg可执行文件,通过管道写入视频帧数据,交给FFmpeg编码、推流;
也可以安装ffmpeg-python包,这个包封装了对FFmpeg的调用,最终也是通过管道通信实现数据传递的。
推荐直接用第一种方式。
人脸检测实现
首先要区分说明一下,人脸检测与人脸识别是不一样的。检测只是将图像中的人脸框出或作其他突出显示,人脸识别则需要预先将人脸录入,当图像、视频中出现人脸时,对人脸进行检测,再将得到数据与录入的进行匹配,识别判断,人脸考勤机是人脸识别最常见的应用。
因此,人脸识别要比人脸检测更复杂一些。
利用Python opencv的Haar特征检测,可以很方便实现人脸检测的效果。
示例代码:
import cv2
# 人脸检测器模型文件路径
# face_cascade = cv2.CascadeClassifier('C:/Users/ACER/AppData/Local/Programs/Python/Python37/Lib/site-packages/cv2/data/haarcascade_frontalface_default.xml') #pip安装opencv包路径下的模型文件
face_cascade = cv2.CascadeClassifier('D:/opencv/opencv/sources/data/haarcascades/haarcascade_frontalface_default.xml')
# 打开摄像头
cap = cv2.VideoCapture(0)
while True:
# 读取一帧
ret, frame = cap.read()
# 将图片转换为灰度图像
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 检测人脸
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
# 在检测到的每张人脸周围画一个矩形
for (x,y,w,h) in faces:
cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2)
# 显示帧
cv2.imshow('frame',frame)
# 如果按下q键,退出循环
if cv2.waitKey(1) & 0xFF == 27:
print("Press Esc, to exit.")
break
# 释放摄像头
cap.release()
# 关闭所有窗口
cv2.destroyAllWindows()
代码流程:
构建一个Haar级联分类器,调用CascadeClassifier即可,传入的参数为人脸检测器模型文件路径:
cv2.CascadeClassifier('C:/Users/ACER/AppData/Local/Programs/Python/Python37/Lib/site-packages/cv2/data/haarcascade_frontalface_default.xml')
cv2.CascadeClassifier('D:/opencv/opencv/sources/data/haarcascades/haarcascade_frontalface_default.xml')
可以使用pip安装opencv包的路径下的模型文件,也可以使用自己安装的opencv中的模型文件,效果相同。
'C:/Users/ACER/AppData/Local/Programs/Python/Python37/Lib/site-packages/cv2/data/haarcascade_frontalface_default.xml'是使用pip安装opencv-python时下载的;
'D:/opencv/opencv/sources/data/haarcascades/haarcascade_frontalface_default.xml'则是我自己安装opencv库时下载的。
调用VideoCapture采集视频帧,并将其转为灰度图,传递给人脸级联分类器face_cascade;
得到分类后的矩形坐标后,在原有数据上绘制出矩形框,即可将人脸框出,调用imshow方法将处理后的图像显示。
调用FFmpeg实现RTMP推流
前面说了有两种方式。
1、直接调用ffmpeg命令
实现思路:调用FFmpeg,在后台开一个子进程,视频帧数据通过这个子进程标准输入写入,数据经过子进程处理后推流到RTMP服务器。
ffmpeg安装后需要添加到windows环境变量,确保在命令行可以直接调用。
import cv2
import subprocess
# 打开摄像头
cap = cv2.VideoCapture(0)
# 设置摄像头分辨率
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
cap.set(cv2.CAP_PROP_BUFFERSIZE, 2)
fps = cap.get(cv2.CAP_PROP_FPS)
print("fps:", fps)
# 设置缓冲区大小为2
# 定义视频编码器
fourcc = cv2.VideoWriter_fourcc(*'X264')
# 创建FFmpeg命令行参数
ffmpeg_cmd = ['ffmpeg',
'-y', # 覆盖已存在的文件
'-f', 'rawvideo',
'-pixel_format', 'bgr24',
'-video_size', '640x480',
'-i', '-', # 从标准输入读取数据
'-c:v', 'libx264', #使用x264编码器
'-preset', 'ultrafast',
'-tune', 'zerolatency',#零延迟
'-pix_fmt', 'yuv420p',
'-f', 'flv',
'rtmp://120.79.54.142:1935/live/cv_demo']
# 启动FFmpeg进程
ffmepg_process = subprocess.Popen(ffmpeg_cmd, stdin=subprocess.PIPE)
# 开始采集和推流
while True:
# 采集一帧图像
ret, frame = cap.read()
if ret:
# 通过FFmpeg编码和推流
ffmepg_process.stdin.write(frame.tobytes())
# 停止FFmpeg进程并释放资源
ffmepg_process.stdin.close()
ffmepg_process.wait()
cap.release()
2、使用ffmpeg-python包
chatgpt给出的示例,存在一些小问题,没有使用这种方式,暂未深究。
import cv2
import ffmpeg
# 打开本地摄像头
cap = cv2.VideoCapture(0)
# 设置编码参数
output_size = (640, 480)
fps = 30
codec = "libx264"
bitrate = "1000k"
# 设置输出流
rtmp_url = "rtmp://your-rtmp-server-url.com/live/stream_key"
out = ffmpeg.output(
ffmpeg.input('pipe:', format='rawvideo', pix_fmt='bgr24', s='{}x{}'.format(*output_size), r=fps),
ffmpeg.format('flv'),
rtmp_url,
vcodec=codec,
b=bitrate
)
# 打开输出流
process = out.run_async(pipe_stdin=True)
# 循环读取每一帧图像,并将其写入输出流
while True:
ret, frame = cap.read()
if not ret:
break
process.stdin.write(frame.tobytes())
# 关闭输入流和输出流
cap.release()
process.stdin.close()
process.wait()
拉流验证
推流成功后,可以使用VLC播放器拉流验证,使用网络串流功能,输入推流的url,播放即可。
视频采集+人脸检测+RTMP推流
将上面的两个例子组合一下,即可实现。
人生苦短,我用Python,直接上代码:
import cv2
import ffmpeg
import subprocess
# 人脸检测器
class FaceDetector:
def __init__(self, module_file):
self.module_file = module_file
self.face_cascade = cv2.CascadeClassifier(self.module_file)
def detectFace(self, gray_img):
face_rect = self.face_cascade.detectMultiScale(gray_img, 1.3, 5)
return face_rect
# 推流器
class StreamPusher:
def __init__(self, rtmp_url):
# 创建FFmpeg命令行参数
ffmpeg_cmd = ['ffmpeg',
'-y', # 覆盖已存在的文件
'-f', 'rawvideo',
'-pixel_format', 'bgr24',
'-video_size', '640x480',
'-i', '-', # 从标准输入读取数据
'-c:v', 'libx264',
'-preset', 'ultrafast',
'-tune', 'zerolatency',
'-pix_fmt', 'yuv420p',
'-f', 'flv',
rtmp_url]
print('ffmpeg_cmd:', ffmpeg_cmd)
# 启动 ffmpeg
self.ffmepg_process = subprocess.Popen(ffmpeg_cmd, stdin=subprocess.PIPE)
def streamPush(self, frame):
self.ffmepg_process.stdin.write(frame.tobytes())
# 人脸检测器模型文件路径
module_file = 'D:/opencv/opencv/sources/data/haarcascades/haarcascade_frontalface_default.xml'
rtmp_server = 'rtmp://120.79.54.142:1935/live/cv_demo'
# program entry
if __name__ == '__main__':
dectector = FaceDetector(module_file)
pusher = StreamPusher(rtmp_server)
# 打开摄像头
cap = cv2.VideoCapture(0)
while True:
# 读取一帧
ret, frame = cap.read()
# 将图片转换为灰度图像
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 检测人脸
faces = dectector.detectFace(gray)
# 在检测到的每张人脸周围画一个矩形
for (x,y,w,h) in faces:
cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2)
# 显示帧
cv2.imshow('frame',frame)
# 如果按下Esc键,退出循环
if cv2.waitKey(1) & 0xFF == 27:
print("Press Esc, to exit.")
break
pusher.streamPush(frame)
# 释放摄像头
cap.release()
# 关闭所有窗口
cv2.destroyAllWindows()
总结
使用python可以快速实现功能,但延迟还是有点高,画面也不太流畅,还有优化空间。有空还是用CPP实现一下吧。