首先

  首先我想进行介绍的是关于显著性检测的有关内容。
  关于显著性检测,其本身是一种视觉注意机制:当我们观察某一个场景的时候,时常会选择性地忽略不感兴趣的区域、聚焦于感兴趣的区域。对于这种感兴趣的区域的提取算法,我们将其称为视觉显著性检测(Visual Saliency Detection,VSD)如下图所示:在人眼的观察过程中,会对天上的白云以及草地上的牛群最先产生反应,而自动忽略了背景区域,则白云以及牛群所在区域就是我们常说的显著性区域。

  我们可以使用一些显著性检测算法,来得到下图的结果。

  其中,像素值接近0的黑色区域为不显著区域,像素值接近255的白色区域为显著区域。
  显著性检测算法在计算机视觉中有着很多的应用。
  此处,我们需要使用到OpenCV-Contrib库中的saliency模块,OpenCV-Contrib在Python下的安装非常容易:

pip install opencv-contrib-python

  使用上述命令进行安装即可。

使用说明

  在saliency模块下,其主要提供了4种显著性检测算法。

(1).StaticSaliencySpectralResidual

  原理:其本质是从统计学的角度进行的处理:模拟眼球的视觉捕捉过程,人们的视觉对变化部分更加敏感,视觉系统的一个基本原则就是抑制对频繁出现的特征的响应,同时对非常规的特征保持敏感。对图像的log频谱进行求均值后可以发现:大量图像的log频谱的平均值与频率成正比关系,如果再对log频谱进行一次log对数变换,其曲线会趋于直线,其中不能直线化的那一部分就是一幅图像的显著性区域,也就是普残差SR(Spectral Residual)。然后将谱残差进行空间变换,得到显著性图,该显著性图显示了感兴趣目标的位置。
  提出时间:2007。

(2).StaticSaliencyFineGrained

  原理:该方法基于空间尺度差异center-surround differences计算显著性。利用积分图像integral images来实时生成高分辨率显著性图。
  提出时间:2010。

(3).ObjectnessBING

  原理:基于二值化梯度特征(BING features)进行物体检测。
  提出时间:2014。

(4).BinWangApr2014

  原理:使用运动背景减除法实现显著性区域的检测。
  提出时间:2014。

代码实现

  接下来,我需要使用到vtest.avi作为我们的实验数据,需要进行下载的,可以点击此处进行下载,提取码为:1234。
  整体代码部分如下所示。

import cv2
import random


def main():
    # 显著性检测算法
    # 可选:SPECTRAL_RESIDUAL,FINE_GRAINED,BING,BinWangApr2014
    saliency_algorithm = "FINE_GRAINED"
    # 检测视频或者图像
    # video_name = "vtest.avi"
    video_name = "1.jpeg"
    # 起始帧
    start_frame = 0
    # 模型路径
    training_path = "ObjectnessTrainedModel"

    # 如果算法名和视频名为空,停止检测
    if saliency_algorithm is None or video_name is None:
        print("Please set saliency_algorithm and video_name")
        return

    # 打开对应的画面
    cap = cv2.VideoCapture(video_name)

    # 设置视频起始帧
    cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame)

    # 读图
    _, frame = cap.read()
    if frame is None:
        print("Please set saliency_algorithm and video_name")
        return

    # 拷贝当前帧的一个副本
    image = frame.copy()

    # 根据输入的方法确定检测类型
    if saliency_algorithm.find("SPECTRAL_RESIDUAL") == 0:

        # 检测结果,白色区域表示显著区域
        saliencyAlgorithm = cv2.saliency.StaticSaliencySpectralResidual_create()

        # 计算显著性
        start = cv2.getTickCount()
        success, saliencyMap = saliencyAlgorithm.computeSaliency(image)
        duration = (cv2.getTickCount() - start) / cv2.getTickFrequency()
        print("computeBinaryMap cost time is: {} ms".format(duration * 1000))

        if success:
            # 二值化图像
            start = cv2.getTickCount()
            _, binaryMap = saliencyAlgorithm.computeBinaryMap(saliencyMap)
            duration = (cv2.getTickCount() - start) / cv2.getTickFrequency()
            print("computeBinaryMap cost time is: {} ms".format(duration * 1000))

            cv2.imshow("Saliency Map", saliencyMap)
            cv2.imshow("Original Image", image)
            cv2.imshow("Binary Map", binaryMap)

            # 转换格式并保存图片
            saliencyMap = (saliencyMap * 255)
            cv2.imwrite("Results/FINE_GRAINED_saliencyMap.jpg", saliencyMap)
            cv2.imwrite("Results/FINE_GRAINED_binaryMap.jpg", binaryMap)
            cv2.waitKey(0)

    # FINE_GRAINED
    elif saliency_algorithm.find("FINE_GRAINED") == 0:
        saliencyAlgorithm = cv2.saliency.StaticSaliencyFineGrained_create()

        # 计算显著性
        start = cv2.getTickCount()
        success, saliencyMap = saliencyAlgorithm.computeSaliency(image)
        duration = (cv2.getTickCount() - start) / cv2.getTickFrequency()
        print("computeBinaryMap cost time is: {} ms".format(duration * 1000))
        if success:
            # 二值化图像
            start = cv2.getTickCount()
            _, binaryMap = saliencyAlgorithm.computeBinaryMap(saliencyMap)
            duration = (cv2.getTickCount() - start) / cv2.getTickFrequency()
            print("computeBinaryMap cost time is: {} ms".format(duration * 1000))

            cv2.imshow("Saliency Map", saliencyMap)
            cv2.imshow("Original Image", image)
            cv2.imshow("Binary Map", binaryMap)

            # 转换格式后保存图片
            saliencyMap = (saliencyMap * 255)
            cv2.imwrite("Results/FINE_GRAINED_saliencyMap.jpg", saliencyMap)
            cv2.imwrite("Results/FINE_GRAINED_binaryMap.jpg", binaryMap)
            cv2.waitKey(0)

    elif saliency_algorithm.find("BING") == 0:
        # 判断模型是否存在
        if training_path is None:
            print("Path of trained files missing! ")
            return
        else:
            saliencyAlgorithm = cv2.saliency.ObjectnessBING_create()
            # 提取模型文件参数
            saliencyAlgorithm.setTrainingPath(training_path)
            # 将算法检测结果保存在Results文件夹内
            saliencyAlgorithm.setBBResDir("Results")

            # 计算显著性
            start = cv2.getTickCount()
            success, saliencyMap = saliencyAlgorithm.computeSaliency(image)
            duration = (cv2.getTickCount() - start) / cv2.getTickFrequency()
            print("computeBinaryMap cost time is: {} ms".format(duration * 1000))
            if success:
                # saliencyMap获取检测到的目标个数
                ndet = saliencyMap.shape[0]
                print("Objectness done ", ndet)
                # 结果根据objectness进行排序,且只需要最大的那一个即可
                # 目标按可能性从大到小排列,maxd为显示前5个目标,step设置颜色,jitter设置矩形框微调
                maxd = 5
                step = 255 / maxd
                jitter = 9
                draw = image.copy()

                for i in range(0, min(maxd, ndet)):
                    # 获得矩形框坐标点
                    bb = saliencyMap[i][0]
                    # 设定颜色
                    col = ((i * step) % 255), 50, 255 - ((i * step) % 255)
                    # 矩形框微调
                    off = random.randint(-jitter,
                                         jitter), random.randint(-jitter, jitter)
                    # 画矩形
                    cv2.rectangle(draw, (bb[0] + off[0], bb[1] + off[1]),
                                  (bb[2] + off[0], bb[3] + off[1]), col, 2)
                    # mini temperature scale
                    # 颜色标注
                    cv2.rectangle(draw, (20, 20 + i * 10, 10, 10), col, -1)

                # 保存图片
                cv2.imwrite("Results/BING_draw.jpg", draw)
                cv2.imshow("BING", draw)
                cv2.waitKey(0)

    # 需要传入图像建模
    elif saliency_algorithm.find("BinWangApr2014") == 0:
        saliencyAlgorithm = cv2.saliency.MotionSaliencyBinWangApr2014_create()
        # 设置数据结构大小
        saliencyAlgorithm.setImagesize(image.shape[1], image.shape[0])
        # 初始化
        saliencyAlgorithm.init()
        paused = False

        while True:
            if not paused:
                _, frame = cap.read()
                if frame is None:
                    break

                frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                # 计算显著性
                start = cv2.getTickCount()
                success, saliencyMap = saliencyAlgorithm.computeSaliency(frame)
                duration = (cv2.getTickCount() - start) / \
                    cv2.getTickFrequency()
                print("computeBinaryMap cost time is: {} ms".format(duration * 1000))
                cv2.imshow("image", frame)
                # 显示
                cv2.imshow("saliencyMap", saliencyMap * 255)

            if cv2.waitKey(2)==ord('q'):
                break
            elif cv2.waitKey(2)==ord('p'):
                paused = not paused

    cv2.destroyAllWindows()
    return


if __name__ == '__main__':
    main()

  这里以FINE_GRAINED来进行展示,其原图(视频第一帧)如下所示。

  之后对其运行代码后,可以得到基于StaticSaliencyFineGrained算法的运行结果,如下所示。

  除此以外,其提供了检测图片的二值化,使用的是computeBinaryMap函数来进行的实现,即先聚类然后阈值分割,其结果如下所示。

  若要进行算法方法的修改,只需要修改上述代码中的第八行代码即可。例如,我把第八行的代码修改为:

saliency_algorithm = "BinWangApr2014"

  其就能够不再仅仅是对第一帧进行处理,而是对整个视频进行处理了,其运行结果如下所示。

总结

  就速度而言的话StaticSaliencySpectralResidual效果是这集中里面最快的,但与此同时,其效果很差。
  StaticSaliencyFineGrained效果和速度都还可以,但是StaticSaliencyFineGrained获得的结果是一个高维图像,为此需要我们进行二值转换。模块自带的computeBinaryMap函数进行二值化效果很不错,就是比较耗时。
  otionSaliencyBinWangApr2014中加入了运动检测,效果和速度都很好,但是建模时间比较长(因为他需要一些先验数据,所以运行的时候的前几秒钟会是白屏),这种当运动检测算法较好。
  如果是较为复杂的场景,最好不要使用显著性检测算法,效果不佳。