精华内容
参与话题
问答
  • python实现手势识别

    万次阅读 多人点赞 2020-04-14 20:39:59
    python实现手势识别(入门) 使用open-cv实现简单的手势识别 1.获取视频(摄像头) 2.肤色检测 3.轮廓提取 最终实现结果

    python实现手势识别(入门)

    使用open-cv实现简单的手势识别。刚刚接触python不久,看到了很多有意思的项目,尤其时关于计算机视觉的。网上搜到了一些关于手势处理的实验,我在这儿简单的实现一下(PS:和那些大佬比起来真的是差远了,毕竟刚接触不久),主要运用的知识就是opencv,python基本语法,图像处理基础知识。

    最终实现结果:
    在这里插入图片描述

    获取视频(摄像头)

    这部分没啥说的,就是获取摄像头。
    `

    cap = cv2.VideoCapture("C:/Users/lenovo/Videos/1.mp4")#读取文件
    #cap = cv2.VideoCapture(0)#读取摄像头
    while(True):
        ret, frame = cap.read()    key = cv2.waitKey(50) & 0xFF
        if key == ord('q'):
        	break
    cap.release()
    cv2.destroyAllWindows()
    
    

    肤色检测

    这里使用的是椭圆肤色检测模型
    在RGB空间里人脸的肤色受亮度影响相当大,所以肤色点很难从非肤色点中分离出来,也就是说在此空间经过处理后,肤色点是离散的点,中间嵌有很多非肤色,这为肤色区域标定(人脸标定、眼睛等)带来了难题。如果把RGB转为YCrCb空间的话,可以忽略Y(亮度)的影响,因为该空间受亮度影响很小,肤色会产生很好的类聚。这样就把三维的空间将为二维的CrCb,肤色点会形成一定得形状,如:人脸的话会看到一个人脸的区域,手臂的话会看到一条手臂的形态。

    def A(img):
    
        YCrCb = cv2.cvtColor(img, cv2.COLOR_BGR2YCR_CB) #转换至YCrCb空间
        (y,cr,cb) = cv2.split(YCrCb) #拆分出Y,Cr,Cb值
        cr1 = cv2.GaussianBlur(cr, (5,5), 0)
        _, skin = cv2.threshold(cr1, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) #Ostu处理
        res = cv2.bitwise_and(img,img, mask = skin)
        return res
    

    轮廓处理

    轮廓处理的话主要用到两个函数,cv2.findContours和cv2.drawContours,这两个函数的使用使用方法很容易搜到就不说了,这部分主要的问题是提取到的轮廓有很多个,但是我们只需要手的轮廓,所以我们要用sorted函数找到最大的轮廓。

    def B(img):
    
        #binaryimg = cv2.Canny(Laplacian, 50, 200) #二值化,canny检测
        h = cv2.findContours(img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE) #寻找轮廓
        contour = h[0]
        contour = sorted(contour, key = cv2.contourArea, reverse=True)#已轮廓区域面积进行排序
        #contourmax = contour[0][:, 0, :]#保留区域面积最大的轮廓点坐标
        bg = np.ones(dst.shape, np.uint8) *255#创建白色幕布
        ret = cv2.drawContours(bg,contour[0],-1,(0,0,0),3) #绘制黑色轮廓
        return ret
    

    全部代码

    """ 从视频读取帧保存为图片"""
    import cv2
    import numpy as np
    cap = cv2.VideoCapture("C:/Users/lenovo/Videos/1.mp4")#读取文件
    #cap = cv2.VideoCapture(0)#读取摄像头
    
    #皮肤检测
    def A(img):
    
        YCrCb = cv2.cvtColor(img, cv2.COLOR_BGR2YCR_CB) #转换至YCrCb空间
        (y,cr,cb) = cv2.split(YCrCb) #拆分出Y,Cr,Cb值
        cr1 = cv2.GaussianBlur(cr, (5,5), 0)
        _, skin = cv2.threshold(cr1, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) #Ostu处理
        res = cv2.bitwise_and(img,img, mask = skin)
        return res
    
    def B(img):
    
        #binaryimg = cv2.Canny(Laplacian, 50, 200) #二值化,canny检测
        h = cv2.findContours(img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE) #寻找轮廓
        contour = h[0]
        contour = sorted(contour, key = cv2.contourArea, reverse=True)#已轮廓区域面积进行排序
        #contourmax = contour[0][:, 0, :]#保留区域面积最大的轮廓点坐标
        bg = np.ones(dst.shape, np.uint8) *255#创建白色幕布
        ret = cv2.drawContours(bg,contour[0],-1,(0,0,0),3) #绘制黑色轮廓
        return ret
    
    
    while(True):
    
        ret, frame = cap.read()
        #下面三行可以根据自己的电脑进行调节
        src = cv2.resize(frame,(400,350), interpolation=cv2.INTER_CUBIC)#窗口大小
        cv2.rectangle(src, (90, 60), (300, 300 ), (0, 255, 0))#框出截取位置
        roi = src[60:300 , 90:300]  # 获取手势框图
    
        res = A(roi)  # 进行肤色检测
        cv2.imshow("0",roi)
    
        gray = cv2.cvtColor(res, cv2.COLOR_BGR2GRAY)
        dst = cv2.Laplacian(gray, cv2.CV_16S, ksize = 3)
        Laplacian = cv2.convertScaleAbs(dst)
    
        contour = B(Laplacian)#轮廓处理
        cv2.imshow("2",contour)
    
        key = cv2.waitKey(50) & 0xFF
        if key == ord('q'):
                break
    cap.release()
    cv2.destroyAllWindows()

    注意:最后送大家一套2020最新企业Pyhon项目实战视频教程,点击此处 进来获取 跟着练习下,希望大家一起进步哦!

    毕竟刚学不久,做出这个东西自己已经很满足了,当然和那些大佬也是没法比的,没有什么东西是速成的,只能一步一个脚印的走。

    展开全文
  • python实现随机森林

    万次阅读 多人点赞 2018-08-26 18:10:59
    定义: 随机森林指的是利用多棵决策树对样本进行训练并预测的一种分类器。可回归可分类。 所以随机森林是基于多颗决策树的一种集成学习算法,常见的决策树算法主要有以下几种: 1. ID3:使用信息增益g(D,A)进行...

    定义:

    随机森林指的是利用多棵决策树对样本进行训练并预测的一种分类器。可回归可分类。
    所以随机森林是基于多颗决策树的一种集成学习算法,常见的决策树算法主要有以下几种:
    1. ID3:使用信息增益g(D,A)进行特征选择
    2. C4.5:信息增益率 =g(D,A)/H(A)
    3. CART:基尼系数
    一个特征的信息增益(或信息增益率,或基尼系数)越大,表明特征对样本的熵的减少能力更强,这个特征使得数据由不确定性到确定性的能力越强。


    Bagging和Boosting的概念与区别

    随机森林属于集成学习(Ensemble Learning)中的bagging算法。在集成学习中,主要分为bagging算法和boosting算法。我们先看看这两种方法的特点和区别。

    Bagging(套袋法)

    bagging的算法过程如下:

    • 从原始样本集中使用Bootstraping方法(自助法,是一种有放回的抽样方法)随机抽取n个训练样本,共进行k轮抽取,得到k个训练集。(k个训练集之间相互独立,元素可以有重复)

    • 对于k个训练集,我们训练k个模型(这k个模型可以根据具体问题而定,比如决策树,knn等)

    • 对于分类问题:由投票表决产生分类结果;对于回归问题:由k个模型预测结果的均值作为最后预测结果。(所有模型的重要性相同)

    Boosting(提升法)

    boosting的算法过程如下:

    • 对于训练集中的每个样本建立权值wi,表示对每个样本的关注度。当某个样本被误分类的概率很高时,需要加大对该样本的权值。
    • 进行迭代的过程中,每一步迭代都是一个弱分类器。我们需要用某种策略将其组合,作为最终模型。(例如AdaBoost给每个弱分类器一个权值,将其线性组合最为最终分类器。误差越小的弱分类器,权值越大)
    Bagging,Boosting的主要区别
    • 样本选择上:Bagging采用的是Bootstrap随机有放回抽样;而Boosting每一轮的训练集是不变的,改变的只是每一个样本的权重。
    • 样本权重:Bagging使用的是均匀取样,每个样本权重相等;Boosting根据错误率调整样本权重,错误率越大的样本权重越大。
    • 预测函数:Bagging所有的预测函数的权重相等;Boosting中误差越小的预测函数其权重越大。
    • 并行计算:Bagging各个预测函数可以并行生成;Boosting各个预测函数必须按顺序迭代生成。

    下面是将决策树与这些算法框架进行结合所得到的新的算法:

    1)Bagging + 决策树 = 随机森林

    2)AdaBoost + 决策树 = 提升树

    3)Gradient Boosting + 决策树 = GBDT


    创建流程及举例

    举例:
    这里写图片描述
    考虑一个简单例子:在二分类任务中,假定三个分类器在三个测试样本上的表现如下图,其中√表示分类正确,×表示分类错误,集成学习的结果通过投票法产生,即“少数服从多数”。如上图,在(a)中,每个分类器都只有66.6%的精度,但集成学习却达到了100%;在(b)中,三个分类器没有差别,集成之后性能没有提高;在(c)中,每个分类器的精度都只有33.3%,集成学习的结果变得更糟。这个简单地例子显示出:要获得好的集成,个体学习器应“好而不同”,即个体学习器要有一定的“准确性”,即学习器不能太差,并且要有“多样性”,即学习器间具有差异。

    构建:
    所以综上:
    随机森林用于分类时,即采用n个决策树分类,将分类结果用简单投票法得到最终分类,提高分类准确率。

    简单来说,随机森林就是对决策树的集成,但有两点不同:

    (1)采样的差异性:从含m个样本的数据集中有放回的采样,得到含m个样本的采样集,用于训练。这样能保证每个决策树的训练样本不完全一样。

    (2)特征选取的差异性:每个决策树的n个分类特征是在所有特征中随机选择的(n是一个需要我们自己调整的参数)

    决策树相当于一个大师,通过自己在数据集中学到的知识对于新的数据进行分类。但是俗话说得好,一个诸葛亮,玩不过三个臭皮匠。随机森林就是希望构建多个臭皮匠,希望最终的分类效果能够超过单个大师的一种算法。
    那随机森林具体如何构建呢?有两个方面:数据的随机性选取,以及待选特征的随机选取。

    1.数据的随机选取:

    首先,从原始的数据集中采取有放回的抽样,构造子数据集,子数据集的数据量是和原始数据集相同的。不同子数据集的元素可以重复,同一个子数据集中的元素也可以重复。第二,利用子数据集来构建子决策树,将这个数据放到每个子决策树中,每个子决策树输出一个结果。最后,如果有了新的数据需要通过随机森林得到分类结果,就可以通过对子决策树的判断结果的投票,得到随机森林的输出结果了。如下图,假设随机森林中有3棵子决策树,2棵子树的分类结果是A类,1棵子树的分类结果是B类,那么随机森林的分类结果就是A类。

    这里写图片描述

    2.待选特征的随机选取

    与数据集的随机选取类似,随机森林中的子树的每一个分裂过程并未用到所有的待选特征,而是从所有的待选特征中随机选取一定的特征,之后再在随机选取的特征中选取最优的特征。这样能够使得随机森林中的决策树都能够彼此不同,提升系统的多样性,从而提升分类性能。

    下图中,蓝色的方块代表所有可以被选择的特征,也就是目前的待选特征。黄色的方块是分裂特征。左边是一棵决策树的特征选取过程,通过在待选特征中选取最优的分裂特征(ID3算法,C4.5算法,CART算法等等),完成分裂。右边是一个随机森林中的子树的特征选取过程。

    这里写图片描述

    所以随机森林需要调整的参数有:

    (1) 决策树的个数

    (2) 特征属性的个数

    (3) 递归次数(即决策树的深度)


    python代码实现

    基于scikit-learn第三方机器学习库的实现:

    # -*- coding: utf-8 -*-
    """
    Created on Thu Jul 26 16:38:18 2018
    
    @author: aoanng
    """
    from sklearn.model_selection import cross_val_score
    from sklearn.datasets import make_blobs
    from sklearn.ensemble import RandomForestClassifier
    from sklearn.ensemble import ExtraTreesClassifier
    from sklearn.tree import DecisionTreeClassifier
    
    ##创建100个类共10000个样本,每个样本10个特征
    X, y = make_blobs(n_samples=10000, n_features=10, centers=100,random_state=0)
    
    ## 决策树
    clf1 = DecisionTreeClassifier(max_depth=None, min_samples_split=2,random_state=0)
    scores1 = cross_val_score(clf1, X, y)
    print(scores1.mean())
    
    ## 随机森林
    clf2 = RandomForestClassifier(n_estimators=10, max_depth=None,min_samples_split=2, random_state=0)
    scores2 = cross_val_score(clf2, X, y)
    print(scores2.mean())
    
    ## ExtraTree分类器集合
    clf3 = ExtraTreesClassifier(n_estimators=10, max_depth=None,min_samples_split=2, random_state=0)
    scores3 = cross_val_score(clf3, X, y)
    print(scores3.mean())

    输出结果:

    0.979408793821
    0.999607843137
    0.999898989899

    性能对比:ExtraTree分类器集合 > 随机森林 > 决策树

    代码实现流程:

    (1) 导入文件并将所有特征转换为float形式

    (2) 将数据集分成n份,方便交叉验证

    (3) 构造数据子集(随机采样),并在指定特征个数(假设m个,手动调参)下选取最优特征

    (4) 构造决策树

    (5) 创建随机森林(多个决策树的结合)

    (6) 输入测试集并进行测试,输出预测结果

    网上别人代码:

    # -*- coding: utf-8 -*-
    """
    Created on Thu Jul 26 16:38:18 2018
    
    @author: aoanng
    """
    
    import csv
    from random import seed
    from random import randrange
    from math import sqrt
    
    
    def loadCSV(filename):#加载数据,一行行的存入列表
        dataSet = []
        with open(filename, 'r') as file:
            csvReader = csv.reader(file)
            for line in csvReader:
                dataSet.append(line)
        return dataSet
    
    # 除了标签列,其他列都转换为float类型
    def column_to_float(dataSet):
        featLen = len(dataSet[0]) - 1
        for data in dataSet:
            for column in range(featLen):
                data[column] = float(data[column].strip())
    
    # 将数据集随机分成N块,方便交叉验证,其中一块是测试集,其他四块是训练集
    def spiltDataSet(dataSet, n_folds):
        fold_size = int(len(dataSet) / n_folds)
        dataSet_copy = list(dataSet)
        dataSet_spilt = []
        for i in range(n_folds):
            fold = []
            while len(fold) < fold_size:  # 这里不能用if,if只是在第一次判断时起作用,while执行循环,直到条件不成立
                index = randrange(len(dataSet_copy))
                fold.append(dataSet_copy.pop(index))  # pop() 函数用于移除列表中的一个元素(默认最后一个元素),并且返回该元素的值。
            dataSet_spilt.append(fold)
        return dataSet_spilt
    
    # 构造数据子集
    def get_subsample(dataSet, ratio):
        subdataSet = []
        lenSubdata = round(len(dataSet) * ratio)#返回浮点数
        while len(subdataSet) < lenSubdata:
            index = randrange(len(dataSet) - 1)
            subdataSet.append(dataSet[index])
        # print len(subdataSet)
        return subdataSet
    
    # 分割数据集
    def data_spilt(dataSet, index, value):
        left = []
        right = []
        for row in dataSet:
            if row[index] < value:
                left.append(row)
            else:
                right.append(row)
        return left, right
    
    # 计算分割代价
    def spilt_loss(left, right, class_values):
        loss = 0.0
        for class_value in class_values:
            left_size = len(left)
            if left_size != 0:  # 防止除数为零
                prop = [row[-1] for row in left].count(class_value) / float(left_size)
                loss += (prop * (1.0 - prop))
            right_size = len(right)
            if right_size != 0:
                prop = [row[-1] for row in right].count(class_value) / float(right_size)
                loss += (prop * (1.0 - prop))
        return loss
    
    # 选取任意的n个特征,在这n个特征中,选取分割时的最优特征
    def get_best_spilt(dataSet, n_features):
        features = []
        class_values = list(set(row[-1] for row in dataSet))
        b_index, b_value, b_loss, b_left, b_right = 999, 999, 999, None, None
        while len(features) < n_features:
            index = randrange(len(dataSet[0]) - 1)
            if index not in features:
                features.append(index)
        # print 'features:',features
        for index in features:#找到列的最适合做节点的索引,(损失最小)
            for row in dataSet:
                left, right = data_spilt(dataSet, index, row[index])#以它为节点的,左右分支
                loss = spilt_loss(left, right, class_values)
                if loss < b_loss:#寻找最小分割代价
                    b_index, b_value, b_loss, b_left, b_right = index, row[index], loss, left, right
        # print b_loss
        # print type(b_index)
        return {'index': b_index, 'value': b_value, 'left': b_left, 'right': b_right}
    
    # 决定输出标签
    def decide_label(data):
        output = [row[-1] for row in data]
        return max(set(output), key=output.count)
    
    
    # 子分割,不断地构建叶节点的过程对对对
    def sub_spilt(root, n_features, max_depth, min_size, depth):
        left = root['left']
        # print left
        right = root['right']
        del (root['left'])
        del (root['right'])
        # print depth
        if not left or not right:
            root['left'] = root['right'] = decide_label(left + right)
            # print 'testing'
            return
        if depth > max_depth:
            root['left'] = decide_label(left)
            root['right'] = decide_label(right)
            return
        if len(left) < min_size:
            root['left'] = decide_label(left)
        else:
            root['left'] = get_best_spilt(left, n_features)
            # print 'testing_left'
            sub_spilt(root['left'], n_features, max_depth, min_size, depth + 1)
        if len(right) < min_size:
            root['right'] = decide_label(right)
        else:
            root['right'] = get_best_spilt(right, n_features)
            # print 'testing_right'
            sub_spilt(root['right'], n_features, max_depth, min_size, depth + 1)
    
            # 构造决策树
    def build_tree(dataSet, n_features, max_depth, min_size):
        root = get_best_spilt(dataSet, n_features)
        sub_spilt(root, n_features, max_depth, min_size, 1)
        return root
    # 预测测试集结果
    def predict(tree, row):
        predictions = []
        if row[tree['index']] < tree['value']:
            if isinstance(tree['left'], dict):
                return predict(tree['left'], row)
            else:
                return tree['left']
        else:
            if isinstance(tree['right'], dict):
                return predict(tree['right'], row)
            else:
                return tree['right']
                # predictions=set(predictions)
    def bagging_predict(trees, row):
        predictions = [predict(tree, row) for tree in trees]
        return max(set(predictions), key=predictions.count)
    # 创建随机森林
    def random_forest(train, test, ratio, n_feature, max_depth, min_size, n_trees):
        trees = []
        for i in range(n_trees):
            train = get_subsample(train, ratio)#从切割的数据集中选取子集
            tree = build_tree(train, n_features, max_depth, min_size)
            # print 'tree %d: '%i,tree
            trees.append(tree)
        # predict_values = [predict(trees,row) for row in test]
        predict_values = [bagging_predict(trees, row) for row in test]
        return predict_values
    # 计算准确率
    def accuracy(predict_values, actual):
        correct = 0
        for i in range(len(actual)):
            if actual[i] == predict_values[i]:
                correct += 1
        return correct / float(len(actual))
    
    
    if __name__ == '__main__':
        seed(1) 
        dataSet = loadCSV('sonar-all-data.csv')
        column_to_float(dataSet)#dataSet
        n_folds = 5
        max_depth = 15
        min_size = 1
        ratio = 1.0
        # n_features=sqrt(len(dataSet)-1)
        n_features = 15
        n_trees = 10
        folds = spiltDataSet(dataSet, n_folds)#先是切割数据集
        scores = []
        for fold in folds:
            train_set = folds[
                        :]  # 此处不能简单地用train_set=folds,这样用属于引用,那么当train_set的值改变的时候,folds的值也会改变,所以要用复制的形式。(L[:])能够复制序列,D.copy() 能够复制字典,list能够生成拷贝 list(L)
            train_set.remove(fold)#选好训练集
            # print len(folds)
            train_set = sum(train_set, [])  # 将多个fold列表组合成一个train_set列表
            # print len(train_set)
            test_set = []
            for row in fold:
                row_copy = list(row)
                row_copy[-1] = None
                test_set.append(row_copy)
                # for row in test_set:
                # print row[-1]
            actual = [row[-1] for row in fold]
            predict_values = random_forest(train_set, test_set, ratio, n_features, max_depth, min_size, n_trees)
            accur = accuracy(predict_values, actual)
            scores.append(accur)
        print ('Trees is %d' % n_trees)
        print ('scores:%s' % scores)
        print ('mean score:%s' % (sum(scores) / float(len(scores))))

    输出结果准确率大致在0.64左右。
    代码中数据集下载地址:https://archive.ics.uci.edu/ml/machine-learning-databases/undocumented/connectionist-bench/sonar/


    性能及优缺点

    优点:

        1.很多的数据集上表现良好;
    
        2.能处理高维度数据,并且不用做特征选择;
    
        3.训练完后,能够给出那些feature比较重要;
    
        4.训练速度快,容易并行化计算。
    

    缺点:

        1.在噪音较大的分类或回归问题上会出现过拟合现象;
    
        2.对于不同级别属性的数据,级别划分较多的属性会对随机森林有较大影响,则RF在这种数据上产出的数值是不可信的。
    



    参考:
    1. 决策树算法及python实现
    2. 随机森林算法学习(RandomForest)
    3. 随机森林的原理分析及Python代码实现
    4. 随机森林_百度百科

    展开全文
  • Python实现5毛钱特效

    万次阅读 多人点赞 2020-04-16 17:19:43
    请务必看到最后。Python牛已经不是一天两天的事了,但是我开始也没想到,Python能这么牛。

    一、前言

    请务必看到最后。Python牛已经不是一天两天的事了,但是我开始也没想到,Python能这么牛。前段时间接触了一个批量抠图的模型库,而后在一些视频中找到灵感,觉得应该可以通过抠图的方式,给视频换一个不同的场景,于是就有了今天的文章。

    我们先看看能实现什么效果,先来个正常版的,先看看原场景:在这里插入图片描述
    下面是我们切换场景后的样子:
    在这里插入图片描述
    看起来效果还是不错的,有了这个我们就可以随意切换场景,坟头蹦迪不是梦。另外,我们再来看看另外一种效果,相比之下要狂放许多:
    在这里插入图片描述
    光理论是不够的,在此送大家一套2020最新Python全栈项目视频教程,点击此处 进来获取 跟着练习下,希望大家一起进步哦!

    二、实现步骤

    我们都知道,视频是有一帧一帧的画面组成的,每一帧都是一张图片,我们要实现对视频的修改就需要对视屏中每一帧画面进行修改。所以在最开始,我们需要获取视频每一帧画面。

    在我们获取帧之后,需要抠取画面中的人物。

    抠取人物之后,就需要读取我们的场景图片了,在上面的例子中背景都是静态的,所以我们只需要读取一次场景。在读取场景之后我们切换每一帧画面的场景,并写入新的视频。

    这时候我们只是生成了一个视频,我们还需要添加音频。而音频就是我们的原视频中的音频,我们读取音频,并给新视频设置音频就好了。

    具体步骤如下:

    1. 读取视频,获取每一帧画面
    2. 批量抠图
    3. 读取场景图片
    4. 对每一帧画面进行场景切换
    5. 写入视频
    6. 读取原视频的音频
    7. 给新视频设置音频

    因为上面的步骤还是比较耗时的,所以我在视频完成后通过邮箱发送通知,告诉我视频制作完成。

    三、模块安装

    我们需要使用到的模块主要有如下几个:

    pillow
    opencv
    moviepy
    paddlehub
    

    我们都可以直接用pip安装:

    pip install pillow
    pip install opencv-python
    pip install moviepy
    

    其中OpenCV有一些适配问题,建议选取3.0以上版本。

    在我们使用paddlehub之前,我们需要安装paddlepaddle:具体安装步骤可以参见官网。用paddlehub抠图参考:别再自己抠图了,Python用5行代码实现批量抠图。我们这里直接用pip安装cpu版本的:

    # 安装paddlepaddle
    python -m pip install paddlepaddle -i https://mirror.baidu.com/pypi/simple
    # 安装paddlehub
    pip install -i https://mirror.baidu.com/pypi/simple paddlehub
    

    有了这些准备工作就可以开始我们功能的实现了。

    四、具体实现

    我们导入如下包:

    import cv2	# opencv
    import mail	# 自定义包,用于发邮件
    import math
    import numpy as np
    from PIL import Image	# pillow
    import paddlehub as hub
    from moviepy.editor import *
    

    其中Pillow和opencv导入的名称不太一样,还有就是我自定义的mail模块。另外我们还要先准备一些路径:

    # 当前项目根目录,系统自动获取当前目录
    BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "."))
    # 每一帧画面保存的地址
    frame_path = BASE_DIR + '\\frames\\'
    # 抠好的图片位置
    humanseg_path = BASE_DIR + '\\humanseg_output\\'
    # 最终视频的保存路径
    output_video = BASE_DIR + '\\result.mp4'
    

    接下来我们按照上面说的步骤一个一个实现。

    (1)读取视频,获取每一帧画面

    在OpenCV中提供了读取帧的函数,我们只需要使用VideoCapture类读取视频,然后调用read函数读取帧,read方法返回两个参数,ret为是否有下一帧,frame为当前帧的ndarray对象。完整代码如下:

    def getFrame(video_name, save_path):
        """
        读取视频将视频逐帧保存为图片,并返回视频的分辨率size和帧率fps
        :param video_name: 视频的名称
        :param save_path: 保存的路径
        :return: fps帧率,size分辨率
        """
        # 读取视频
        video = cv2.VideoCapture(video_name)
    
        # 获取视频帧率
        fps = video.get(cv2.CAP_PROP_FPS)
        # 获取画面大小
        width = int(video.get(cv2.CAP_PROP_FRAME_WIDTH))
        height = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))
        size = (width, height)
    
        # 🦍获取帧数,用于给图片命名
        frame_num = str(video.get(7))
        name = int(math.pow(10, len(frame_num)))
    	# 读取帧,ret为是否还有下一帧,frame为当前帧的ndarray对象
        ret, frame = video.read()
        while ret:
            cv2.imwrite(save_path + str(name) + '.jpg', frame)
            ret, frame = video.read()
            name += 1
        video.release()
        return fps, size
    

    在标🦍处,我获取了帧的总数,然后通过如下公式获取比帧数大的整十整百的数:

    frame_name = math.pow(10, len(frame_num))
    

    这样做是为了让画面逐帧排序,这样读取的时候就不会乱。另外我们获取了视频的帧率和分辨率,这两个参数在我们创建视频时需要用到。这里需要注意的是opencv3.0以下版本获取帧率和画面大小的写法有些许差别。

    (2)批量抠图

    批量抠图需要用到paddlehub中的模型库,代码很简单,这里就不多说了:

    def getHumanseg(frames):
        """
        对帧图片进行批量抠图
        :param frames: 帧的路径
        :return:
        """
        # 加载模型库
        humanseg = hub.Module(name='deeplabv3p_xception65_humanseg')
        # 准备文件列表
        files = [frames + i for i in os.listdir(frames)]
        # 抠图
        humanseg.segmentation(data={'image': files})
    

    我们执行上面函数后会在项目下生成一个humanseg_output目录,抠好的图片就在里面。

    (3)读取场景图片

    这也是简单的图片读取,我们使用pillow中的Image对象:

    def readBg(bgname, size):
        """
        读取背景图片,并修改尺寸
        :param bgname: 背景图片名称
        :param size: 视频分辨率
        :return: Image对象
        """
        im = Image.open(bgname)
        return im.resize(size)
    

    这里的返回的对象并非ndarray对象,而是Pillow中定义的类对象。

    (4)对每一帧画面进行场景切换

    简单来说就是将抠好的图片和背景图片合并,我们知道抠好的图片都在humanseg_output目录,这也就是为什么最开始要准备相应的变量存储该目录的原因:

    def setImageBg(humanseg, bg_im):
        """
        将抠好的图和背景图片合并
        :param humanseg: 抠好的图
        :param bg_im: 背景图片,这里和readBg()函数返回的类型一样
        :return: 合成图的ndarray对象
        """
        # 读取透明图片
        im = Image.open(humanseg)
        # 分离色道
        r, g, b, a = im.split()
        # 🦍复制背景,以免源背景被修改
        bg_im = bg_im.copy()
        # 合并图片
        bg_im.paste(im, (0, 0), mask=a)
        return np.array(bg_im.convert('RGB'))[:, :, ::-1]
    

    在标🦍处,我们复制了背景,如果少了这一步的话,生成的就是我们上面的“千手观音效果”了。

    其它步骤都很好理解,只有返回值比较长,我们来详细看一下:

    # 将合成图转换成RGB,这样A通道就没了
    bg_im = bg_im.convert('RGB')
    # 将Image对象转换成ndarray对象,方便opencv读取
    im_array = np.array(bg_im)
    # 此时im_array为rgb模式,而OpenCV为bgr模式,我们通过下面语句将rgb转换成bgr
    bgr_im_array = im_array[:, :, ::-1]
    

    最后bgr_im_array就是我们最终的返回结果。

    (5)写入视频

    为了节约空间,我并非等将写入图片放在合并场景后面,而是边合并场景边写入视频:

    def writeVideo(humanseg, bg_im, fps, size):
        """
        :param humanseg: png图片的路径
        :param bgname: 背景图片
        :param fps: 帧率
        :param size: 分辨率
        :return:
        """
        # 写入视频
        fourcc = cv2.VideoWriter_fourcc(*'mp4v')
        out = cv2.VideoWriter('green.mp4', fourcc, fps, size)
    
        # 将每一帧设置背景
        files = [humanseg + i for i in os.listdir(humanseg)]
        for file in files:
        	# 循环合并图片
            im_array = setImageBg(file, bg_im)
            # 逐帧写入视频
            out.write(im_array)
        out.release()
    

    上面的代码也非常简单,执行完成后项目下会生成一个green.mp4,这是一个没有音频的视频,后面就需要我们获取音频然后混流了。

    (6)读取原视频的音频

    因为在opencv中没找到音频相关的处理,所以选用moviepy,使用起来也非常方便:

    def getMusic(video_name):
        """
        获取指定视频的音频
        :param video_name: 视频名称
        :return: 音频对象
        """
        # 读取视频文件
        video = VideoFileClip(video_name)
        # 返回音频
        return video.audio
    

    然后就是混流了。

    (7)给新视频设置音频

    这里同样使用moviepy,传入视频名称和音频对象进行混流:

    def addMusic(video_name, audio):
        """实现混流,给video_name添加音频"""
        # 读取视频
        video = VideoFileClip(video_name)
        # 设置视频的音频
        video = video.set_audio(audio)
        # 保存新的视频文件
        video.write_videofile(output_video)
    

    其中output_video是我们在最开始定义的变量。

    (8)删除过渡文件

    在我们生产视频时,会产生许多过渡文件,在视频合成后我们将它们删除:

    def deleteTransitionalFiles():
        """删除过渡文件"""
        frames = [frame_path + i for i in os.listdir(frame_path)]
        humansegs = [humanseg_path + i for i in os.listdir(humanseg_path)]
        for frame in frames:
            os.remove(frame)
        for humanseg in humansegs:
            os.remove(humanseg)
    

    最后就是将整个流程整合一下。

    (8)整合

    我们将上面完整的流程合并成一个函数:

    def changeVideoScene(video_name, bgname):
        """
        :param video_name: 视频的文件
        :param bgname: 背景图片
        :return:
        """
        # 读取视频中每一帧画面
        fps, size = getFrame(video_name, frame_path)
    
        # 批量抠图
        getHumanseg(frame_path)
    
        # 读取背景图片
        bg_im = readBg(bgname, size)
    
        # 将画面一帧帧写入视频
        writeVideo(humanseg_path, bg_im, fps, size)
    
        # 混流
        addMusic('green.mp4', getMusic(video_name))
    
        # 删除过渡文件
        deleteTransitionalFiles()
    

    (9)在main中调用

    我们可以把前面定义的路径也放进了:

    if __name__ == '__main__':
    
        # 当前项目根目录
        BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "."))
        # 每一帧画面保存的地址
        frame_path = BASE_DIR + '\\frames\\'
        # 抠好的图片位置
        humanseg_path = BASE_DIR + '\\humanseg_output\\'
        # 最终视频的保存路径
        output_video = BASE_DIR + '\\result.mp4'
    
        if not os.path.exists(frame_path):
            os.makedirs(frame_path)
    
        try:
        	# 调用函数制作视频
            changeVideoScene('jljt.mp4', 'bg.jpg')
            # 当制作完成发送邮箱
            mail.sendMail('你的视频已经制作完成')
        except Exception as e:
        	# 当发生错误,发送错误信息
            mail.sendMail('在制作过程中遇到了问题' + e.__str__())
    

    这样我们就完成了完整的流程。

    五、发送邮件

    邮件的发送又是属于另外的内容了,我定义了一个mail.py文件,具体代码如下:

    import smtplib
    from email.mime.text import MIMEText
    from email.mime.multipart import MIMEMultipart      # 一封邮件
    
    
    def sendMail(msg):	
    	# 
        sender = '发件人'
        to_list = [
            '收件人'
        ]
        subject = '视频制作情况'
    
        # 创建邮箱
        em = MIMEMultipart()
        em['subject'] = subject
        em['From'] = sender
        em['To'] = ",".join(to_list)
    
        # 邮件的内容
        content = MIMEText(msg)
        em.attach(content)
    
        # 发送邮件
        # 1、连接服务器
        smtp = smtplib.SMTP()
        smtp.connect('smtp.163.com')
        # 2、登录
        smtp.login(sender, '你的密码或者授权码')
        # 3、发邮件
        smtp.send_message(em)
        # 4、关闭连接
        smtp.close()
    

    里面的邮箱我是直接写死了,大家可以自由发挥。为了方便,推荐发件人使用163邮箱,收件人使用QQ邮箱。另外在登录的时候直接使用密码比较方便,但是有安全隐患。
    最后送大家一套2020最新Pyhon项目实战视频教程,点击此处 进来获取 跟着练习下,希望大家一起进步哦!

    六、总结

    老实说上述程序的效率非常低,不仅占空间,而且耗时也比较长。在最开始我切换场景选择的是遍历图片每一个像素,而后找到了更加高效的方式取代了。但是帧画面的保存,和png图片的存储都很耗费空间。

    另外程序设计还是有许多不合理的地方,像是ndarray对象和Image的区分度不高,另外有些函数选择传入路径,而有些函数选择传入文件对象也很容易让人糊涂。最后说一下,我们我们用上面的方式不仅可以做静态的场景切换,还可以做动态的场景切换,这样我们就可以制作更加丰富的视频。当然,效率依旧是个问题。感兴趣的读者可以关注我的公众号:ZackSock。完整代码已提交GitHub:https://github.com/IronSpiderMan/VideoSpecialEffects

    最后,给大家带来一个彩蛋:
    在这里插入图片描述
    因为实在找不到素材,所以只能自己拍了一个,上面的实现原理大致就是本篇博客,因为光线的关系,效果还是差了一点。感兴趣的读者可以关注我,后续会写相关博客。

    展开全文
  • Python 实现【连连看】游戏

    千次阅读 热门讨论 2019-01-14 21:49:39
    自己蛋疼地设计了个连连看游戏,消除算法真的很蛋疼。...实现“相连”判断算法 消除图片元素并判断是否消除完毕 【连连看】的代码存放于 https://gitee.com/luhuadong/Python_Learning/tree/ma...

    自己蛋疼地设计了个连连看游戏,消除算法真的很蛋疼。。。

    完成设计之后,总结了该连连看游戏几个重点:

    • 生成成对的图片元素
    • 将图片元素打乱排布
    • 定义什么才算“相连”(两张图片的连线不多于3跟直线,或者说转角不超过2个)
    • 实现“相连”判断算法
    • 消除图片元素并判断是否消除完毕

    【连连看】的代码存放于 https://gitee.com/luhuadong/Python_Learning/tree/master/2nd_week/homework/link_up

    游戏开始:
    在这里插入图片描述

    点击图片,如果两张图片相同,并且能相连的就可以消除:
    在这里插入图片描述

    全部图片消除完毕就赢了:
    在这里插入图片描述

    展开全文
  • python 实现手机投屏

    万次阅读 2020-04-17 12:09:12
    https://github.com/Genymobile/scrcpy
  • Python实现简单的神经网络

    万次阅读 多人点赞 2019-03-27 23:18:30
    在说神经网络之前,我们讨论一下神经元(Neurons),它是神经网络的基本单元。神经元先获得输入,然后执行某些数学运算后,再产生一个输出。比如一个2输入神经元的例子: 在这个神经元里,输入总共经历了3步数学...
  • Python实现简单分类器

    千次阅读 多人点赞 2018-09-21 23:57:01
    文章目录@[toc]第一步,导入我们需要的python库第二步,获取训练数据并解析坐标第三步,随机化数据第四步、生成分界线斜率第五步、处理测试数据第六步、输出展示分类结果输入数据完整代码 今天重新开始学习机器学习...
  • 图结构的python实现

    千次阅读 2018-11-18 18:16:12
    本文主要介绍两种基于python的图结构的实现方法。 邻接矩阵实现 邻接矩阵是表示图中顶点间邻接关系的方阵。对于n个顶点的图G=(V,E),其邻接矩阵是一个的方阵,图中每个顶点(按顺序)对应于矩阵里的一行和一列,...
  • 线性回归是确定两种及两种以上变量的相互依赖关系。在数据分析中,线性回归是最简单且最有效的分析方法。举个简单的例子,某商品的利润在售价为2元、5元、10元时分别为4元、10元、20元,我们很容易得出商品的利润与...
  • 1. 算法介绍 Graph Cut(图形切割)应用于计算机视觉领域用来有效的解决各种低级计算机视觉问题,例如图像平滑(image smoothing)、立体应对问题(stereo correspondence problem)、图像分割(image segmentation...
  • Python实现简单的智能回答

    千次阅读 2018-06-19 15:14:49
    本人新书《玩转Python网络爬虫》,可在天猫、京东等商城搜索查阅,项目深入浅出,适合爬虫初学者或者是已经有一些网络爬虫编写经验,但希望更加全面、深入理解Python爬虫的开发人员。 ———-欢迎加入学习交流QQ...
  • 利用Python实现推流直播详解

    万次阅读 2020-03-15 10:11:53
    https://blog.csdn.net/Mind_programmonkey/article/details/102732555
  • python实现复制文件功能

    万次阅读 2018-08-27 22:21:59
    写程序,实现复制文件功能 要求: 1) 要考虑关闭文件问题 2) 要考虑超大文件复制问题 3) 要能复制二进制文件(如: / usr / bin / python3 等文件) def copy_file(): file=input(‘源文件’) file1=input(...
  • Python实现 灰色关联分析 与结果可视化

    万次阅读 多人点赞 2018-06-07 13:21:45
    之前在比赛的时候需要用Python实现灰色关联分析,从网上搜了下只有实现两个列之间的,于是我把它改写成了直接想Pandas中的计算工具直接计算person系数那样的形式,可以对整个矩阵进行运算,并给出了可视化效果,效果请见...
  • Python实现模拟按键和改键精灵

    万次阅读 2019-12-09 10:36:34
    Python实现模拟按键和改键精灵,用以控制拳皇97,亲测有效. VK_CODE:虚拟键码 参考: https://www.cnblogs.com/Evan-fanfan/p/11097519.html https://blog.csdn.net/wang8978/article/details/52900048 ...
  • Python实现发短信

    万次阅读 2020-01-11 23:05:20
    改写互亿无线官方发短信代码类实现 输出结果: <?xml version="1.0" encoding="utf-8"?> <SubmitResult xmlns="http://106.ihuyi.com/"> <code>2</code> <msg>提交成功</msg> &...
  • Python实现SVM使用案例

    万次阅读 多人点赞 2019-06-05 20:14:31
    这篇推文主要介绍Python实现SVM的案例,后期会更新加强版。 这里主要讲的是使用Python的Sklearn包实现SVM样本分类,而不包括SVM的理论推导,我在看SVM的理论的时候看了很多网上的博客,有很多都写的不错,这里...
  • python实现opencv学习一:安装、环境配置、工具

    万次阅读 多人点赞 2018-03-09 20:51:58
    一:python的下载、安装、配置 以python2.7.9为例 http://blog.csdn.net/FUCAIHE/article/details/45353283 若需其他最新版本请到官网下载:https://www.python.org/downloads/ 二:安装pip 进入Python安装...
  • Python实现softmax函数

    万次阅读 2019-03-03 18:15:49
    Python实现softmax函数: PS:为了避免求exp(x)出现溢出的情况,一般需要减去最大值。 # -*-coding: utf-8 -*- import tensorflow as tf import numpy as np def softmax(x, axis=1): # 计算每行的最大值 ...
  • python3_实现BP神经网络 + BP神经网络应用实例

    万次阅读 多人点赞 2018-07-29 22:10:28
    1.BP神经网络简介 BP神经网络是1986年由Rumelhart和McClelland为首的科学家提出的概念,是一种按照逆向传播算法训练的多层前馈神经网络,是目前应用最广泛的神经网络。 优点:具有任意复杂的模式分类能力和优良的...

空空如也

1 2 3 4 5 ... 20
收藏数 1,232,893
精华内容 493,157
关键字:

python实现

python 订阅