python 对视频帧提取图片,并去除变化不大的帧,保留清晰的帧。

见代码,改进了对于大视频文件会溢出的问题

处理的视频文件下载:https://download.csdn.net/download/babytiger/12732306

# 视频输入目录
input_dir=r'E:\video\xueshan\1'
# 图像输出目录
output_dir=r'e:/image/'

# smoothing window size
len_window = int(25) 
len_window 如果改大到100,会使局部运行感知不明显,图中的松鼠比较小,就不会被感知到,程序会认为整体没啥变化就不会生成关键帧

 


上面的要自行设定,实测一个小视频900多帧,下面程序提取出23多个有效帧

# -*- coding: utf-8 -*-

import cv2
import operator
import numpy as np
import matplotlib.pyplot as plt
import os
from scipy.signal import argrelextrema

# Setting fixed threshold criteria
USE_THRESH = False
# fixed threshold value
THRESH = 0.6
# Setting fixed threshold criteria
USE_TOP_ORDER = False
# Setting local maxima criteria
USE_LOCAL_MAXIMA = True
# Number of top sorted frames
NUM_TOP_FRAMES = 20
# 视频输入目录
input_dir=r'E:\video\xueshan\1'
# 图像输出目录
output_dir=r'e:/image/'
# smoothing window size
len_window = int(25)


def smooth(x, window_len=13, window='hanning'):
    """smooth the data using a window with requested size.

    This method is based on the convolution of a scaled window with the signal.
    The signal is prepared by introducing reflected copies of the signal
    (with the window size) in both ends so that transient parts are minimized
    in the begining and end part of the output signal.

    input:
        x: the input signal
        window_len: the dimension of the smoothing window
        window: the type of window from 'flat', 'hanning', 'hamming', 'bartlett', 'blackman'
            flat window will produce a moving average smoothing.
    output:
        the smoothed signal

    example:
    import numpy as np
    t = np.linspace(-2,2,0.1)
    x = np.sin(t)+np.random.randn(len(t))*0.1
    y = smooth(x)

    see also:

    numpy.hanning, numpy.hamming, numpy.bartlett, numpy.blackman, numpy.convolve
    scipy.signal.lfilter

    TODO: the window parameter could be the window itself if an array instead of a string
    """
    print(len(x), window_len)
    # if x.ndim != 1:
    #     raise ValueError, "smooth only accepts 1 dimension arrays."
    #
    # if x.size < window_len:
    #     raise ValueError, "Input vector needs to be bigger than window size."
    #
    # if window_len < 3:
    #     return x
    #
    # if not window in ['flat', 'hanning', 'hamming', 'bartlett', 'blackman']:
    #     raise ValueError, "Window is on of 'flat', 'hanning', 'hamming', 'bartlett', 'blackman'"

    s = np.r_[2 * x[0] - x[window_len:1:-1], x, 2 * x[-1] - x[-1:-window_len:-1]]
    # print(len(s))

    if window == 'flat':  # moving average
        w = np.ones(window_len, 'd')
    else:
        w = getattr(np, window)(window_len)
    y = np.convolve(w / w.sum(), s, mode='same')
    return y[window_len - 1:-window_len + 1]


# Class to hold information about each frame


class Frame:
    def __init__(self, id, frame, value):
        self.id = id
        self.frame = frame
        self.value = value

    def __lt__(self, other):
        if self.id == other.id:
            return self.id < other.id
        return self.id < other.id

    def __gt__(self, other):
        return other.__lt__(self)

    def __eq__(self, other):
        return self.id == other.id and self.id == other.id

    def __ne__(self, other):
        return not self.__eq__(other)


def rel_change(a, b):
    if(max(a,b)!=0):
        x = (b - a) / max(a, b)
        print(x)
    else:
        return 0
    return x

def write_frames(dir,filename):
    if USE_TOP_ORDER:
        # sort the list in descending order
        frames.sort(key=operator.attrgetter("value"), reverse=True)
        for keyframe in frames[:NUM_TOP_FRAMES]:
            name = "frame_" + str(keyframe.id) + ".jpg"
            cv2.imwrite(dir + "/" + filename+'_'+name, keyframe.frame)

    if USE_THRESH:
        print("Using Threshold")
        for i in range(1, len(frames)):
            if (rel_change(np.float(frames[i - 1].value), np.float(frames[i].value)) >= THRESH):
                # print("prev_frame:"+str(frames[i-1].value)+"  curr_frame:"+str(frames[i].value))
                name = "frame_" + str(frames[i].id) + ".jpg"
                cv2.imwrite(dir + "/" + filename+'_'+name, frames[i].frame)

    if USE_LOCAL_MAXIMA:
        print("Using Local Maxima")
        diff_array = np.array(frame_diffs)
        sm_diff_array = smooth(diff_array, len_window)
        frame_indexes = np.asarray(argrelextrema(sm_diff_array, np.greater))[0]
        for i in frame_indexes:
            name = "frame_" + str(frames[i - 1].id) + ".jpg"
            print(dir+name)
            cv2.imwrite(dir + "/" + filename+'_'+ name, frames[i - 1].frame)

frame_diffs = []
frames = []

def all_path(dirname):
    for maindir, subdir, file_name_list in os.walk(dirname):
        for fn in file_name_list:
            file_path = os.path.join(maindir, fn)  # 合并成一个完整路径

            (filepath,tempfilename) = os.path.split(file_path)
            (filename,extension) = os.path.splitext(tempfilename)
            if not os.path.exists(output_dir+filename+'/'):
                os.mkdir(output_dir+filename+'/')
            videopath =file_path
            # Directory to store the processed frames
            dir = output_dir+filename
            print("Video :" + videopath)
            print("Frame Directory: " + dir)

            cap = cv2.VideoCapture(str(videopath))

            curr_frame = None
            prev_frame = None


            ret, frame = cap.read()
            i = 1

            while (ret):
                luv = cv2.cvtColor(frame, cv2.COLOR_BGR2LUV)
                curr_frame = luv
                if curr_frame is not None and prev_frame is not None:
                    # logic here
                    diff = cv2.absdiff(curr_frame, prev_frame)
                    count = np.sum(diff)
                    frame_diffs.append(count)
                    frame = Frame(i, frame, count)
                    frames.append(frame)

                    print(filename,i )
                    #防止载入大视频内存溢出,每1000帧清空一次
                    if(i%1000==0):
                        write_frames(dir,filename)
                        frame_diffs.clear()
                        frames.clear()
                prev_frame = curr_frame
                i = i + 1
                ret, frame = cap.read()

                cv2.imshow('frame',luv)
                if cv2.waitKey(1) & 0xFF == ord('q'):
                    break
            write_frames(dir,filename)
            cap.release()

all_path(input_dir)
cv2.destroyAllWindows()


但是这23帧中看到还有不少很相似的,于是用下面代码再次去重

代码中的,用户可以根据自己情况调整,大概原理是把图像缩小到46*46,计算相似哈希

#相似度,如果大于max_ssim就进行删除
max_ssim=0.6

又去重了7张,效果如下

# -*- coding: utf-8 -*-
import os
import cv2
from skimage.measure import compare_ssim
import shutil
def yidong(filename1,filename2):
    shutil.move(filename1,filename2)
def delete(filename1):
    os.remove(filename1)
#相似度,如果大于max_ssim就进行删除
max_ssim=0.5
#利用拉普拉斯算子计算图片的二阶导数,反映图片的边缘信息,同样事物的图片,
# 清晰度高的,相对应的经过拉普拉斯算子滤波后的图片的方差也就越大。
def getImageVar(image):
    img2gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    imageVar = cv2.Laplacian(img2gray, cv2.CV_64F).var()
    return imageVar

if __name__ == '__main__':
    img_path = r'E:\image\xuedi'
    del_list = []
    img_files = [os.path.join(rootdir, file) for rootdir, _, files in os.walk(img_path) for file in files if
                 (file.endswith('.jpg'))]
    for currIndex, filename in enumerate(img_files):
        if not os.path.exists(img_files[currIndex]):
            print('not exist', img_files[currIndex])
            break
        new_cur = 0
        for i in range(10000000):
            currIndex1 =new_cur
            if currIndex1 >= len(img_files) - currIndex - 1:
                break
            else:
                size = os.path.getsize(img_files[currIndex1 + currIndex + 1])
                if size < 64:
                    # delete(img_files[currIndex + 1])
                    del_list.append(img_files.pop(currIndex1 + currIndex + 1))
                else:
                    img = cv2.imread(img_files[currIndex])
                    #计算图像清晰度
                    var_img=getImageVar(img)
                    img = cv2.resize(img, (46, 46), interpolation=cv2.INTER_CUBIC)
                    img1 = cv2.imread(img_files[currIndex1 + currIndex + 1])
                    # 计算图像清晰度
                    var_img1=getImageVar(img1)
                    img1 = cv2.resize(img1, (46, 46), interpolation=cv2.INTER_CUBIC)
                    ssim = compare_ssim(img, img1, multichannel=True)

                    if ssim > max_ssim:
                        #当达到了门限,比较两个图像的清晰度,把不清晰的放入删除列表中
                        print(var_img,var_img1,img_files[currIndex], img_files[currIndex1 + currIndex + 1], ssim)
                        if(var_img>var_img1):
                            del_list.append(img_files.pop(currIndex1 + currIndex + 1))
                        else:
                            del_list.append(img_files.pop(currIndex))
                        new_cur = currIndex1
                    else:
                        new_cur = currIndex1 + 1
                        if ssim > 0.4:
                            print('small_ssim',img_files[currIndex], img_files[currIndex1 + currIndex + 1], ssim)
    for image in del_list:
        #delete(image)
        print('delete',image)

去重过程中会保留更清晰的帧,比较两个图像的清晰度,把不清晰的放入删除列表中

#利用拉普拉斯算子计算图片的二阶导数,反映图片的边缘信息,同样事物的图片,
# 清晰度高的,相对应的经过拉普拉斯算子滤波后的图片的方差也就越大。
def getImageVar(image):
    img2gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    imageVar = cv2.Laplacian(img2gray, cv2.CV_64F).var()
    return imageVar

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值