感知哈希算法对大量视频抽帧,并存放到同名文件夹中

目录

 基于相似性度量的抽帧调研报告 

相似性度量方法

 1.直方图计算图像相似度

2.哈希值计算图像相似度

(1)均值哈希算法

(2)差值哈希算法

(3)感知哈希算法

泛化性测试

实际应用

大量视频分批次处理

 补充问题


 基于相似性度量的抽帧调研报告 

背景:在对行车视频抽帧获取图像时,行车视频中存在的停车或者缓慢行驶的情况,这导致在对视频抽帧时获得大量相似图片。大量的高度相似的图片增加了图片标注的任务,采用直接抽帧又需要人为判断并且根据视频设置抽帧频率。
实现内容:在对视频抽帧的过程中,遍历视频每一帧,通过对比前后帧图片之间的相似性,保存差异较大的图片,舍去相似图片,从而达到自动删除冗余帧数,得到有效帧数。减轻后续标注工作,得到更多差异较大的场景。

目录

 基于相似性度量的抽帧调研报告 

相似性度量方法

 1.直方图计算图像相似度

2.哈希值计算图像相似度

(1)均值哈希算法

(2)差值哈希算法

(3)感知哈希算法

泛化性测试

实际应用


按照预设的频率进行抽帧代码如下:

def video_to_frames(video_path, outPutDirName):
    times = 0
    print('开始提取')
    # 提取视频的频率,每1帧提取一个
    frame_frequency = 10
    print('提取间隔:每隔',frame_frequency,'提取一张')
    # 如果文件目录不存在则创建目录
    if not os.path.exists(outPutDirName):
        os.makedirs(outPutDirName)

    # 读取视频帧
    camera = cv2.VideoCapture(video_path)
    # 帧率
    fps = int(round(camera.get(cv2.CAP_PROP_FPS)))
    # 总帧数
    frame_counter = int(camera.get(cv2.CAP_PROP_FRAME_COUNT))
    # 时长,单位s
    duration = frame_counter / fps
    print('总帧数:', frame_counter, '时长', duration,'秒')
    while True:
        times = times + 1
        res, image = camera.read()
        if not res:
            print('not res , not image')
            break
        # 按照设置间隔存储视频帧
        if times % frame_frequency == 0:
            cv2.imwrite(outPutDirName + '\\' + str(times) + '.jpg', image

    print('图片提取结束')
    # 释放摄像头设备
    camera.release()


这是最直接也是原始的抽帧方法,需要预设抽帧频率,根据不同的视频都要进行手动调整,才能得到重复较少的图片,那么如何识别相似图片并在抽取过程中将其滤除就要用到相似性度量的方法。

相似性度量方法

 1.直方图计算图像相似度

  • 直方图的横坐标代表各个像素点的灰度级,纵坐标代表该灰度级上像素点的数量。通过比较两图片的直方图,来判断两图片的相似性。
  • 所用函数:

hist = cv2.calcHist([img1], [0], None, [256], [0, 256])
#参数为:图像,通道数,未使用掩膜为none,被分成多少份,像素区间


例如,可以得到两个图像的直方图的对比

  • 这里用到的是单通道直方图对比,所以要先将输入的三通道图像转换为灰度图像再进行直方图计算

def histogram(img1,img2):
    color = ('b', 'g', 'r')
    for i, col in enumerate(color):
        hist1 = cv2.calcHist([img1], [i], None, [256], [0, 256])
        hist2 = cv2.calcHist([img2], [i], None, [256], [0, 256])
def calculate(image1, image2):
    # 灰度直方图算法
    # 计算单通道的直方图的相似值
    hist1 = cv2.calcHist([image1], [0], None, [256], [0.0, 255.0])
    hist2 = cv2.calcHist([image2], [0], None, [256], [0.0, 255.0])
    # 计算直方图的重合度
    degree = 0
    for i in range(len(hist1)):
        if hist1[i] != hist2[i]:
            degree = degree + \
                (1 - abs(hist1[i] - hist2[i]) / max(hist1[i], hist2[i]))
        else:
            degree = degree + 1
    degree = degree / len(hist1)
    return degree

if __name__ == '__main__':
    img1 = cv2.imread('D:/pythonproject/frequency/video/result/366.jpg')
    img2 = cv2.imread('D:/pythonproject/frequency/video/result/394.jpg')

    histogram(img1,img2)
    degree = calculate(img1, img2)
    print('两张图片的单通道直方图相似度为',degree*100,'%')


输出的结果为:
 

两张图片的单通道直方图相似度为 [86.21706] %

然后对视频进行处理

import cv2
import os
import numpy as np
import datetime
"""
使用方法:提取单通道灰度直方图>对比>得到两张图片相似度>比较每一帧与前一帧,舍去相似度高于0.75的
        修改图片路径与保存路径即可
提取效果:视频总帧数: 3823 时长 127.43333333333334 秒
        3823帧中提取了208帧,无重复图像
        抽帧率:5.44%  耗时:21.862912 Seconds
对比:直接间隔18帧抽一帧  耗时:14.832632 Seconds
总结:对于停车时处理较好,行车过程中效果与普通抽帧差别不大

修改图片路径与保存路径即可
"""
def calculate(image1, image2):
    # 灰度直方图算法
    # 计算单通道的直方图的相似值
    hist1 = cv2.calcHist([image1], [0], None, [256], [0.0, 255.0])
    hist2 = cv2.calcHist([image2], [0], None, [256], [0.0, 255.0])
    # 计算直方图的重合度
    degree = 0
    for i in range(len(hist1)):
        if hist1[i] != hist2[i]:
            degree = degree + \
                (1 - abs(hist1[i] - hist2[i]) / max(hist1[i], hist2[i]))
        else:
            degree = degree + 1
    degree = degree / len(hist1)
    return degree

"""
#该方法包含了其他程序使用CPU的时间
import datetime
start=datetime.datetime.now()
#中间写代码块
end=datetime.datetime.now()
print('Running time: %s Seconds'%(end-start))
"""
if __name__ == '__main__':

    start=datetime.datetime.now()
    # 中间写上代码块
    video_path = "video/shepidai_d_0.mp4"#视频路径
    outPutDirName = "video/result"#抽帧图片结果路径

    camera = cv2.VideoCapture(video_path)
    times = 0
    print('开始提取')
    # 提取视频的频率,每1帧提取一个
    frame_frequency = 1
    print('提取间隔:每隔', frame_frequency, '提取一张')
    # 如果文件目录不存在则创建目录
    if not os.path.exists(outPutDirName):
        os.makedirs(outPutDirName)
    # 初始化
    image0 = np.zeros((10, 10, 3), np.uint8)
    while True:
        times = times + 1
        res, image = camera.read()
        # 计算两图相似度
        degree = calculate(image0, image)
        if degree >= 0.75:
            image = image0
            # print('两图片相似,舍去后图')
        else:
            image0 = image
            # print('两图片不相似')
            if not res:
                print('not res , not image')
                break
            # 按照设置间隔存储视频帧
            if times % frame_frequency == 0:
                cv2.imwrite(outPutDirName + '\\' + str(times) + '.jpg', image)

    print('图片提取结束')
    # 释放摄像头设备
    camera.release()

    end = datetime.datetime.now()
    print('Running time: %s Seconds' % (end - start))


抽帧效果如下图

这一版的效果比较一般,虽然高于普通抽帧,但是对于参数的调整需要依靠人的感受,此处参数设置为相似度大于75%的时候判断两张图片相似,可以舍去。抽帧效果可以去除停车片段,也与自己设置的参数有关。其次就是运行时间,对于测试视频,大概0.1秒提取一张,有待提高,期望是在有限时间提取尽可能多的有效图片。

2.哈希值计算图像相似度

计算图片相似度的哈希算法有三种

  • 均值哈希算法
  • 差值哈希算法
  • 感知哈希算法

在计算之前需要了解两个概念

  • 图像指纹:简单来说就是将图像按照一定的哈希算法,经过运算后得到的一组二进制数字。
  • 汉明距离:假如一组二进制数据为101,另外一组为111,那么显然把第一组的第二位数据0改成1就可以变成第二组数据111,所以两组数据的汉明距离就为1。简单点说,汉明距离就是一组二进制数据变成另一组数据所需的步骤数,显然,这个数值可以衡量两张图片的差异,汉明距离越小,则代表相似度越高。汉明距离为0,即代表两张图片完全一样。

(1)均值哈希算法

计算步骤具体内容
缩放输入图片大小各异,为了统一图片输入,将图片缩放为8*8大小,一共得到64个像素点。
灰度化将图像转换为单通道灰度图像
求平均值计算灰度图片8*8矩阵里所有像素点的平均值
比较像素灰度值遍历灰度图片每一个像素,如果大于平均值记录为1,否则为0
生成Hash将上述步骤生成的 1 和 0 按顺序组合起来,既生成图片的指纹( hash)
对比指纹计算两幅图片的指纹,计算汉明距离(从一个指纹到另一个指纹需要变几次),汉明距离越大则说明图片越不一致,反之,汉明距离越小则说明图片越相似,当距离为0时,说明完全相同。(通常认为距离>10 就是两张完全不同的图片)

def aHash(img):
    # 均值哈希算法
    # 缩放为8*8
    img = cv2.resize(img,(8,8))
    # 转换为灰度图
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # s为像素和初值为0,hash_str为hash值初值为''
    s = 0
    hash_str = ''
    # 遍历累加求像素和
    for i in range(8):
        for j in range(8):
            s = s+gray[i, j]
    # 求平均灰度
    avg = s/64
    # 灰度大于平均值为1相反为0生成图片的hash值
    for i in range(8):
        for j in range(8):
            if gray[i, j] > avg:
                hash_str = hash_str+'1'
            else:
                hash_str = hash_str+'0'
    return hash_str


将其运用到视频抽帧当中,代码如下
 


import cv2
import os
import numpy as np
import datetime


"""
使用方法:均值哈希算法获取图像哈希值>Hash值对比>大于10即为不相似,0为完全一样
        修改图片路径与保存路径即可
        n1参数标准为10,若静止画面过多则调成20,抽帧率降为3%左右,耗时12秒
视频总帧数: 3823 时长 127.43333333333334 秒
          3823帧中提取了1038帧,无重复图像
         抽帧率:27.1%  耗时:25.850112 Seconds
提取效果:可检测颠簸,在颠簸时获取的图像相似度也不一样,会被采集
"""
def aHash(img):
    # 均值哈希算法
    # 缩放为8*8
    img = cv2.resize(img,(8,8))
    # 转换为灰度图
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # s为像素和初值为0,hash_str为hash值初值为''
    s = 0
    hash_str = ''
    # 遍历累加求像素和
    for i in range(8):
        for j in range(8):
            s = s+gray[i, j]
    # 求平均灰度
    avg = s/64
    # 灰度大于平均值为1相反为0生成图片的hash值
    for i in range(8):
        for j in range(8):
            if gray[i, j] > avg:
                hash_str = hash_str+'1'
            else:
                hash_str = hash_str+'0'
    return hash_str

def cmpHash(hash1, hash2):
    # Hash值对比
    # 算法中1和0顺序组合起来的即是图片的指纹hash。顺序不固定,但是比较的时候必须是相同的顺序。
    # 对比两幅图的指纹,计算汉明距离,即两个64位的hash值有多少是不一样的,不同的位数越小,图片越相似
    # 汉明距离:一组二进制数据变成另一组数据所需要的步骤,可以衡量两图的差异,汉明距离越小,则相似度越高。汉明距离为0,即两张图片完全一样
    n = 0
    # hash长度不同则返回-1代表传参出错
    if len(hash1) != len(hash2):
        return -1
    # 遍历判断
    for i in range(len(hash1)):
        # 不相等则n计数+1,n最终为相似度
        if hash1[i] != hash2[i]:
            n = n + 1
    return n

if __name__ == '__main__':
    start = datetime.datetime.now()
    video_path = "video/test01.mp4"  # 视频路径
    outPutDirName = "video/result"  # 抽帧图片结果路径
    camera = cv2.VideoCapture(video_path)
    times = 0
    print('开始提取')
    # 提取视频的频率,每1帧提取一个
    frame_frequency = 1
    print('提取间隔:每隔', frame_frequency, '提取一张')
    # 如果文件目录不存在则创建目录
    if not os.path.exists(outPutDirName):
        os.makedirs(outPutDirName)
    # 初始化
    image0 = np.zeros((10, 10, 3), np.uint8)
    image = np.zeros((10, 10, 3), np.uint8)
    while True:
        times = times + 1
        res, image = camera.read()
        if not res:
            print('not res , not image')
            break
        # 计算两图相似度
        hash1 = aHash(image0)
        hash2 = aHash(image)
        n1 = cmpHash(hash1, hash2)
        if n1 <= 10:
            image = image0
            #print('两图片相似,舍去后图')
        else:
            image0 = image
            # print('两图片不相似')
            # 按照设置间隔存储视频帧
            if times % frame_frequency == 0:
                cv2.imwrite(outPutDirName + '\\' + str(times) + '.jpg', image)

    end = datetime.datetime.now()
    print('Running time: %s Seconds' % (end - start))
    print('图片提取结束')
    camera.release()

注意:虽然从图像上看直方图抽取图片差异性更大,但要考虑到抽帧率与时间,同样的视频,直方图抽取了208张而均值哈希抽取了1038张,抽帧率分别为5.4%和27.1%,所用时间相差不大,而且使用普通抽帧算法按照5.4%的抽帧率也能得到很好的效果,而且时间更快,所以结果需要从各方面来评估。

  •  抽帧率比直方图算法明显提高,大概为27%,使用的测试视频为无停车行驶。之后又测算了对停车视频的提取,其中背景的植物被风吹动改变位置较大时也会被记录。在开阔路段的抽帧有类似视频倍速的效果,其中车辆的颠簸导致视野移位也会被认定两图像不相似,从而采集图像。
  •  优点是在针对停车与行车的判别上比普通抽帧处理的更好,在行车时景物变换,抽帧间隔可以精确到一帧,在停车时,抽帧间隔可以达到几百帧,根据视频停车时长不等。
  •  拓展:虽然说均值哈希算法当中,汉明距离>10为两张完全不一样的图片,但是行车过程是一个连续的过程,为了采集差异更大的图片可以适当调整参数。当参数调整为15时可以实现停车时刻只抽取一帧。

(2)差值哈希算法

计算步骤具体内容
缩放将图片缩放为8*9大小,一共得到72个像素点,保留结构,去除细节。
灰度化将图像转换为灰度图像
求平均值计算灰度图所有像素的平均值。 这步没有,只是为了与均值哈希做对比
比较像素灰度值像素值大于后一个像素值记作 1 ,相反记作 0 。本行不与下一行对比,每行 9 个像素,八个差值,有 8 行,总共 64 位
生成Hash将上述步骤生成的 1 和 0 按顺序组合起来既是图片的指纹( hash )。 
对比指纹将两幅图的指纹对比,计算汉明距离,即两个 64 位的 hash 值有多少位是不一样的,不相同位数越少,图片越相似。


def dHash(img):
    # 差值哈希算法
    # 缩放8*8
    img = cv2.resize(img, (9, 8))
    # 转换灰度图
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    hash_str = ''
    # 每行前一个像素大于后一个像素为1,相反为0,生成哈希
    for i in range(8):
        for j in range(8):
            if gray[i, j] > gray[i, j+1]:
                hash_str = hash_str+'1'
            else:
                hash_str = hash_str+'0'
    return hash_str


应用时直接把均值哈希改成差值哈希的函数即可。

因为差值哈希算法精度更高,太过灵敏,因此视频的抽帧率达到了67%,但这不是期望的结果。将其与普通抽帧结合后,也还是难以对停车时候的冗余图片剔除。


(3)感知哈希算法

计算步骤具体内容
缩小图片32 * 32是一个较好的大小,这样方便DCT计算
转化为灰度图把缩放后的图片转化为256阶的灰度图。(具体算法见平均哈希算法步骤)
计算DCTDCT把图片分离成分率的集合
缩小DCTDCT是32*32,保留左上角的8*8,这些代表的图片的最低频率
求平均值计算缩小DCT后的所有像素点的平均值。
进一步减小DCT大于平均值记录为1,反之记录为0
生成Hash将上述步骤生成的 1 和 0 按顺序组合起来,组合64个信息位,顺序随意保持一致性即可。
对比指纹计算两幅图片的指纹,计算汉明距离(从一个指纹到另一个指纹需要变几次),汉明距离越大则说明图片越不一致,反之,汉明距离越小则说明图片越相似,当距离为0时,说明完全相同。(通常认为距离>10 就是两张完全不同的图片)


def pHash(img):
    # 感知哈希算法
    # 缩放32*32
    img = cv2.resize(img, (32, 32))   # , interpolation=cv2.INTER_CUBIC

    # 转换为灰度图
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 将灰度图转为浮点型,再进行dct变换
    dct = cv2.dct(np.float32(gray))
    # opencv实现的掩码操作
    dct_roi = dct[0:8, 0:8]

    hash = []
    avreage = np.mean(dct_roi)
    for i in range(dct_roi.shape[0]):
        for j in range(dct_roi.shape[1]):
            if dct_roi[i, j] > avreage:
                hash.append(1)
            else:
                hash.append(0)
    return hash


得到的效果是比较好的,在停车时基本上能实现只抽取一帧,在缓慢行驶时也可以做到快速抽帧。视野范围内有目标进入例如行人或者车辆,会抽取这些关键帧,美中不足的是抽帧率大概9~10%

泛化性测试

在上述的几种方法中,效果比较出众的为均值哈希算法(aHash)和感知哈希算法(pHash),以下使用这两种算法处理多种行车视频,在处理效果,运行速度,抽帧率方面进行对比。因为均值哈希算法抽帧过多,故此处将汉明距离设置为15,便于在抽帧数量相差不大的情况下与感知哈希算法比较。

从结果图片可以看出,感知哈希算法更适合本次任务的要求,抽帧也更流畅。

实际应用

实际要求要从一个文件夹当中读取五万多个视频,对每个视频抽帧之后,创建一个视频相同名称的文件夹,存储该视频抽帧所得的图像。

先尝试如何创建同名文件夹


'''
批量提取视频的所有帧
'''
import os
import cv2
#视频地址
videos_src_path = 'video/'
#存放图片的地址
videos_save_path = 'video/result/'

#返回videos_src_path路径下包含的文件或文件夹名字的列表(所有视频的文件名),按字母顺序排序
videos = os.listdir(videos_src_path)
for each_video in videos:
    #获取每个视频的名称
    each_video_name = each_video.split('.')
    #创建目录保存抽出的帧
    os.mkdir(videos_save_path + each_video_name[0])
    #获取保存图片的完整路径,每个视频的图片帧存在以视频名为文件名的文件夹中
    each_video_save_full_path = os.path.join(videos_save_path, each_video_name[0]) + '/'
    #每个视频的完整路径
    each_video_full_path = os.path.join(videos_src_path, each_video)
    #读入视频
    cap = cv2.VideoCapture(each_video_full_path)
    print(each_video_full_path)

    """
    #这下面可以替换为自己的抽帧代码
    frame_count = 100
    success = True
    while (success):
        #提取视频帧,success为是否成功获取视频帧(true/false),第二个返回值为返回的视频帧
        success, frame = cap.read()
        # 如果想间隔比如25帧抽一张,可以在下方if里加条件:frame_count % 25 == 0
        if success == True:
            #存储视频帧,%04d则每张图片以4位数命名,比如0001.jpg
            cv2.imwrite(each_video_save_full_path + "%04d.jpg" % frame_count, frame)
        frame_count += 1
    """


如下如所示,这个文件夹中有八个视频还有一个result文件,将视频抽帧后的图片保存在result中创建的同名文件夹中

保存结果

可以看到,不同视频抽帧结果均保存在同名文件夹中了

使用代码如下


import cv2
import os
import datetime
import numpy as np
import matplotlib
matplotlib.use('TkAgg')

def pHash(img):
    # 感知哈希算法
    # 缩放32*32
    img = cv2.resize(img, (32, 32))   # , interpolation=cv2.INTER_CUBIC

    # 转换为灰度图
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 将灰度图转为浮点型,再进行dct变换
    dct = cv2.dct(np.float32(gray))
    # opencv实现的掩码操作
    dct_roi = dct[0:8, 0:8]

    hash = []
    avreage = np.mean(dct_roi)
    for i in range(dct_roi.shape[0]):
        for j in range(dct_roi.shape[1]):
            if dct_roi[i, j] > avreage:
                hash.append(1)
            else:
                hash.append(0)
    return hash

def cmpHash(hash1, hash2):
    # Hash值对比
    # 算法中1和0顺序组合起来的即是图片的指纹hash。顺序不固定,但是比较的时候必须是相同的顺序。
    # 对比两幅图的指纹,计算汉明距离,即两个64位的hash值有多少是不一样的,不同的位数越小,图片越相似
    # 汉明距离:一组二进制数据变成另一组数据所需要的步骤,可以衡量两图的差异,汉明距离越小,则相似度越高。汉明距离为0,即两张图片完全一样
    n = 0
    # hash长度不同则返回-1代表传参出错
    if len(hash1) != len(hash2):
        return -1
    # 遍历判断
    for i in range(len(hash1)):
        # 不相等则n计数+1,n最终为相似度
        if hash1[i] != hash2[i]:
            n = n + 1
    return n


#视频地址
videos_src_path = 'video/'
#存放图片的地址
videos_save_path = 'video/result/'

if __name__ == '__main__':
    start = datetime.datetime.now()
    # 返回videos_src_path路径下包含的文件或文件夹名字的列表(所有视频的文件名),按字母顺序排序
    videos = os.listdir(videos_src_path)
    for each_video in videos:
        # 获取每个视频的名称
        each_video_name = each_video.split('.')
        # 创建目录保存抽出的帧
        os.mkdir(videos_save_path + each_video_name[0])
        # 获取保存图片的完整路径,每个视频的图片帧存在以视频名为文件名的文件夹中
        each_video_save_full_path = os.path.join(videos_save_path, each_video_name[0]) + '/'
        # 每个视频的完整路径
        each_video_full_path = os.path.join(videos_src_path, each_video)
         ######################################################
        # 读入视频
        cap = cv2.VideoCapture(each_video_full_path)
        times = 0
        print('开始提取')
        # 提取视频的频率,每1帧提取一个
        frame_frequency = 1
        print('提取间隔:每隔', frame_frequency, '提取一张')
        # 如果文件目录不存在则创建目录
        if not os.path.exists(videos_save_path + each_video_name[0]):
            os.makedirs(videos_save_path + each_video_name[0])
        # 初始化
        image0 = np.zeros((10, 10, 3), np.uint8)
        image = np.zeros((10, 10, 3), np.uint8)
        while True:
            times = times + 1
            res, image = cap.read()
            if not res:
                print('not res , not image')
                break
            # 计算两图相似度
            hash1 = pHash(image0)
            hash2 = pHash(image)
            n1 = cmpHash(hash1, hash2)
            if n1 <= 10:
                image = image0
                # print('两图片相似,舍去后图')
            else:
                image0 = image
                # print('两图片不相似')
                # 按照设置间隔存储视频帧
                if times % frame_frequency == 0:
                    cv2.imwrite(videos_save_path + each_video_name[0] + '\\' + str(times) + '.jpg', image)

        end = datetime.datetime.now()
        print('Running time: %s Seconds' % (end - start))
        print('图片提取结束')
        ####################################################
        cap.release()
        print(each_video_full_path)


这样视频抽帧的任务就完成了,后续还可以进行其他优化,比如
1. 文件夹路径不需要手动输入,可以浏览输入
2.  实时显示视频处理进度
3.  ....
可以根据自己的需要来做一些调整。

大量视频分批次处理

存在大量视频需要进行抽帧处理,如下图所示:

由于电脑内存不足以一次性储存所有结果,因此想到将视频分批次处理。这里所用到的思路是:

  1. 先获取所有视频的名称,存储在txt文件内。
  2. 再根据自己的需要,将txt文件内的视频名分散到多个txt文件内备用。
  3. 抽帧时,从txt文件内读取视频名,与原视频文件夹路径拼接成为该视频的绝对路径
  4. 重复处理每个txt文件,得到多批次数据

先得到包含所有视频名的txt文件,代码如下

import os
import re


# 输入文件夹名称
def read_directory(directory_name):
    for filename in os.listdir(directory_name):
        print(filename)  # 仅仅是为了测试
        fw.write(filename + '\n')  # 打印成功信息


if __name__ == '__main__':
    videos_path = r'D:\pythonproject\frequency\video'#\\10.128.3.137\算法\原始视频\全地形\videos_20231108'D:\pythonproject\frequency\video'
    txt_path = r'D:\pythonproject\frequency\cs.txt'  # 生成的图片列表清单txt文件名
    fw = open(txt_path, "w")
    read_directory(videos_path)

获得了视频名的txt文件

根据自己的需求分割txt文件,这里将五千多个视频名分散到十个txt文件内,每个txt文件约有550个视频名

# 获取每个视频的名称,这里以空格划分
        each_video_name = each_video.split('\n')
#得到的结果为 each_video_name里面储存了['视频名','\n']
#例如['061_2M_20230319_124319_ningxia_yinchuan_5B_0_4.avi', '']

#之前使用的为:
# 获取每个视频的名称
        each_video_name = each_video.split('.')
#得到的结果为['视频名','后缀']

测试将原始视频路径变为与txt文件内容拼接所得路径,这里对之前的代码只有少量的修改,主要集中在对路径的处理上。运行中遇到了以下错误:

输出空文件夹,没有进行抽帧处理。解决方法:txt文件每行都相当于有空格,也就是"\n",因此,对视频名的分割可以改变思路。

总体代码为

import cv2
import os
import datetime
import numpy as np
import matplotlib
matplotlib.use('TkAgg')

"""
实现以感知哈希算法对视频抽帧并将内容存放到同名文件夹
分批次处理,从文件夹中读取视频名称
"""
def pHash(img):
    # 感知哈希算法
    # 缩放32*32
    img = cv2.resize(img, (32, 32))   # , interpolation=cv2.INTER_CUBIC

    # 转换为灰度图
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 将灰度图转为浮点型,再进行dct变换
    dct = cv2.dct(np.float32(gray))
    # opencv实现的掩码操作
    dct_roi = dct[0:8, 0:8]

    hash = []
    avreage = np.mean(dct_roi)
    for i in range(dct_roi.shape[0]):
        for j in range(dct_roi.shape[1]):
            if dct_roi[i, j] > avreage:
                hash.append(1)
            else:
                hash.append(0)
    return hash

def cmpHash(hash1, hash2):
    # Hash值对比
    # 算法中1和0顺序组合起来的即是图片的指纹hash。顺序不固定,但是比较的时候必须是相同的顺序。
    # 对比两幅图的指纹,计算汉明距离,即两个64位的hash值有多少是不一样的,不同的位数越小,图片越相似
    # 汉明距离:一组二进制数据变成另一组数据所需要的步骤,可以衡量两图的差异,汉明距离越小,则相似度越高。汉明距离为0,即两张图片完全一样
    n = 0
    # hash长度不同则返回-1代表传参出错
    if len(hash1) != len(hash2):
        return -1
    # 遍历判断
    for i in range(len(hash1)):
        # 不相等则n计数+1,n最终为相似度
        if hash1[i] != hash2[i]:
            n = n + 1
    return n


#视频地址
videos_src_path = r'\\10.128.3.137\算法\原始视频\全地形\videos_20231108'#r'D:/pythonproject/frequency/video'#r'\\10.128.3.137\算法\原始视频\全地形\videos_20231108'
#存放图片的地址
videos_save_path = r"F:/result/"


if __name__ == '__main__':
    start = datetime.datetime.now()
    # 返回videos_src_path路径下包含的文件或文件夹名字的列表(所有视频的文件名),按字母顺序排序
    #videos = os.listdir(videos_src_path)
    txt_path = 'name/name1.txt'
    file = open(txt_path, 'r')  # 打开文件
    file_data = file.readlines()  # 读取所有行
    for each_video in file_data:
        # 获取每个视频的名称
        each_video_name = each_video.split('\n')
        # 创建目录保存抽出的帧
        os.mkdir(videos_save_path + each_video_name[0])
        # 获取保存图片的完整路径,每个视频的图片帧存在以视频名为文件名的文件夹中
        each_video_save_full_path = os.path.join(videos_save_path, each_video_name[0]) + '/'
        # 每个视频的完整路径
        each_video_full_path = os.path.join(videos_src_path, each_video_name[0])
        #########################################################
        # 读入视频
        cap = cv2.VideoCapture(each_video_full_path)
        times = 0
        print('开始提取',each_video_name)
        # 提取视频的频率,每1帧提取一个
        frame_frequency = 1
        #print('提取间隔:每隔', frame_frequency, '提取一张')
        # 如果文件目录不存在则创建目录
        if not os.path.exists(videos_save_path + each_video_name[0]):
            os.makedirs(videos_save_path + each_video_name[0])
        # 初始化
        image0 = np.zeros((10, 10, 3), np.uint8)
        image = np.zeros((10, 10, 3), np.uint8)
        while True:
            times = times + 1
            res, image = cap.read()
            if not res:
                print('not res , not image')
                break
            # 计算两图相似度
            hash1 = pHash(image0)
            hash2 = pHash(image)
            n1 = cmpHash(hash1, hash2)
            if n1 <= 10:
                image = image0
                # print('两图片相似,舍去后图')
            else:
                image0 = image
                # print('两图片不相似')
                # 按照设置间隔存储视频帧
                if times % frame_frequency == 0:
                    cv2.imwrite(videos_save_path + each_video_name[0] + '\\' + str(times) + '.jpg', image)



        print('图片提取结束')
        ####################################################
        cap.release()
        #print(videos_save_path + each_video_name[0])

    end = datetime.datetime.now()
    print('Running time: %s Seconds' % (end - start))

使用时需要改变三个值,视频地址,存放地址,以及txt_path,这里处理的name1.txt是存放的第一批名称,处理后的结果可以在result文件夹中查看。

550个视频用时将近两小时处理完,获得的图片文件有32G左右

图片提取结束
Running time: 1:55:19.877035 Seconds

之后只需要更换不同的txt,读取不同批次的视频名,就能依次处理完了。

 补充问题

当读取的视频名为中文名时,意味着存放的文件夹也会是中文路径,出现了只创建文件夹,但不保存图片的问题。

解决方案:将代码中保存的这条代码修改即可

#原代码
cv2.imwrite(each_video_save_full_path + '\\' + str(times) + '.jpg', image)
#修改后
cv2.imencode('.jpg', image)[1].tofile(each_video_save_full_path + '\\' + str(times) + '.jpg')

大概在这个位置 

可以看到正常运行了

  • 27
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值