您现在的位置是:网站首页> PY&Rust

Python OpencV

  • PY&Rust
  • 2023-05-22
  • 825人已阅读
摘要

Python OpenCV

基础学习资料

python opencv将图片合成视频,并插入音频

Python-混音、叠加音频、拼接音频及批处理

Python 视频添加音频(附代码) | Python工具

Python快速实现视频播放器

python播放音频的方法

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()


运行

1.png

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()


1.png

图像加权和: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()


1.png

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)


运行:

1.png

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)

1.png


# 翻转

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()


运行

2.png


# 平移

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.]]

1.png


# 旋转

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]]

1.png


# 仿射变换

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()


运行:

1.png


# 透视变换:将矩形映射为任意平行四边形

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()


运行:

1.png

先写到这里,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


运行:

1.png

低阈值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()


运行:

1.png

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()


运行:

1.png

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()


运行:

1.png

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,所以图像偏亮。

1.png

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()


运行:

1.png

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()


1.png

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()


运行:

1.png

今天先写到这里,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实现一下吧。





Top