国庆前在思考视频号发表相关的优化点时,需要想办法计算出”图片色彩度指标”,以及”视频镜头检测算法”,趁着国庆假期看了一些相关论文,比较了几个算法后筛选出了最适合我进行应用的落地方案,并在文章最后将论文中的算法用代码实现,并对参数进行调整优化后取得了比较好的效果,分享于此交流学习。

一、图片色彩度指标

(一)什么是图片色彩度

“色彩度”业内没有统一明确的定义,它可以用”图片艳丽程度”进行描述,比如下面这组图片中:
你觉得哪张图色彩最艳丽呢?
3个人可能会给出3个不同的答案,比如可能优先被选择的是图4、图9和图11。
这时我们可以发现一个问题:”色彩丰富度是一个因人而异的指标“,所以它并不能像色彩直方图、RGB频率分布图那样给出一个标准的公式,它的答案和心理学密切相关。

(二)如何量化色彩度?

既然”色彩丰富度”的答案和”人”相关,那我们是不是可以找N个人一起来评估同一份图片,最后根据大家的选择得出一份最接近大众的”色彩认知方案”呢?
答案是可以的,Hasler and Süsstrunk (论文链接在文末)做了这个实验,他们将颜色丰富度划分为7级,接着找了20个人对84副图片按照1-7分进行打分。最后对这份调查数据进行分析,发现图片颜色丰富度有如下计算公式。
通过计算我们可以获得颜色丰富度参数C,使用python实现论文中的算法逻辑如下:

import cv2
import numpy as np

def image_colorfulness(image):
    #将图片分为B,G,R三部分(注意,这里得到的R、G、B为向量而不是标量)
    (B, G, R) = cv2.split(image.astype("float"))
 
    #rg = R - G
    rg = np.absolute(R - G)
 
    #yb = 0.5 * (R + G) - B
    yb = np.absolute(0.5 * (R + G) - B)
 
    #计算rg和yb的平均值和标准差
    (rbMean, rbStd) = (np.mean(rg), np.std(rg))
    (ybMean, ybStd) = (np.mean(yb), np.std(yb))
 
    #计算rgyb的标准差和平均值
    stdRoot = np.sqrt((rbStd ** 2) + (ybStd ** 2))
    meanRoot = np.sqrt((rbMean ** 2) + (ybMean ** 2))
 
    # 返回颜色丰富度C
    return stdRoot + (0.3 * meanRoot)

image = cv2.imread('图片路径')  
print(image_colorfulness(image))    

(三)阶段小结

“图片色彩度”指标正如我们前面所说,它并不是一个有标准答案的题目,它的公式试图通过对人的实验总结出和人的色彩偏好相关的结果。就我所看的论文中,尽管有很多人试图从HSV、色彩直方图对图片色彩丰富度进行描述,但都没有给出一个切实可行的解决方案,Hasler and Süsstrunk 的解决方案算是截止到目前应用比较多的唯一的方案。

二、视频镜头检测算法

(一)视频镜头检测是什么?

和我们看电视一样,我们所看到的视频镜头都是在不断切换的,有时会从人切换到物,又有时会从物品切换到风景,这样每切换一次,我们就认为视频镜头数+1。

相比于码率、分辨率、帧率等指标,镜头数量是更深一步和视频内容起到关联作用的指标。

(二)镜头检测算法背景和选型

通过阅读论文可以发现,镜头检测算法是目前业内近20年一直在探究的话题,从颜色直方图、HSV、颜色加权到深度学习,镜头检测不仅向着数量准发展,而且还向着内容理解的方向进行发展(通过算法识别出每个镜头的内容,比如:人物阶段、景色阶段、动作阶段等)。
因为我们的目标是检测出有多少个镜头,而无需深入到每个镜头表达什么内容上,所以这里我针对基于颜色直方图变化率的视频镜头检测进行了优化,首先看一下实现的部分代码和效果。

NSUInteger coverNumber = 26;
CGFloat timeInterval = 25 / (coverNumber - 1);
Float64 currentTime = 0.0;
NSMutableArray<NSValue *> *times = [NSMutableArray array];
for (int i = 0; i < coverNumber; i++) {
    currentTime = timeInterval * i;
    CMTime time = CMTimeMakeWithSeconds(currentTime, 60);
    NSValue *timeValue = [NSValue valueWithCMTime:time];
    [times addObject:timeValue];
}

CGFloat frequencyThreshold = 0.3; // hcr变化阈值

__block NSUInteger lensCount = 1; // 一共有多少个镜头

__block NSDictionary<NSNumber *, NSNumber *> *serialFrequencyDic = nil; // 在这个镜头里的加权频率
__block NSUInteger curSerialCount = 0; // 在当前这个镜头里是第几帧

__block NSUInteger curImageCount = 0;
[self.coverGenerator generateCGImagesAsynchronouslyForTimes:times
                                          completionHandler:^(CMTime requestedTime,
                                                              CGImageRef  _Nullable image,
                                                              CMTime actualTime,
                                                              AVAssetImageGeneratorResult result,
                                                              NSError * _Nullable error) {
    curImageCount ++;
    UIImage *imageReal = [UIImage imageWithCGImage:image];
    
    NSDictionary<NSNumber *, NSNumber *> *curFrequencyDic = [self.class getImageHSVFrequencyDicByImage:imageReal];
    if (serialFrequencyDic == nil) {
        serialFrequencyDic = curFrequencyDic;
        curSerialCount = 1;
    }else{
        CGFloat hcrValue = [self.class getHCRBetweenFrequencyFrom:serialFrequencyDic frequencyDicJ:curFrequencyDic];
        if (hcrValue > frequencyThreshold) {
            // 发现新的镜头
            lensCount += 1;
            serialFrequencyDic = nil;
        }else{
            // 还是在同一个镜头里
            curSerialCount += 1;
            // 对同一个镜头里的直方图频率进行加权
            serialFrequencyDic = [self.class getWeightedByRaw:serialFrequencyDic curFrequencyDic:curFrequencyDic len:lensCount];
        }
        NSLog(@"filter hcrValue:%f curImageCount:%lu",hcrValue, (unsigned long)curImageCount);
    }
}];

运行代码跑出来的效果如下:

可以发现,查全率和查准率都到了90%的水平。

(三)镜头检测算法原理

1. 明确RGB和HSV的区别和相互转换

关于 RGB和HSV的关联 之前写了一篇文章:
将RGB转换成HSV之后,为了减少光照变化导致的疑似镜头变化的影响,我们可以抹去V分量,只关注H和S分量即可。

2. 对HS分量进行非均匀量化

量化处理时,令L = 4 * H + S,那么我们可以明确,L是在[0,43]区间的44维整数。

3. 统计直方图变化率

在计算完每一帧图片的L值后,我们可以统计直方图前后的变化率:

通过统计HCR,我们便可以在镜头发生切换时得到一个比较大的HCR值,将HCR进行图像化,会得到如下的插针图:

其中插针的地方则大概率是镜头切换的地方。

4. 解决平滑镜头切换的加权逻辑

很多情况下,镜头切换都不是突变的,比如可能会进行10秒的渐渐切换,这时如果单一计算第9秒和第10秒的变化率很难会发现镜头切换的情况。所以为了解决镜头渐变切换的情况,我们需要再加入一个因素:时间序列。
计算第10秒时,不是和第9秒的直方图进行比较,而是对前9秒里采集到的所有直方图加权求和进行比较,通过在公式中引入加权求和的概念,抹平了时间渐变带来的影响。

小结

本文我们研究了有关图片和视频两个比较深入的指标”图片色彩度”和”镜头检测算法”,前者难点在于不同人对于色彩有不同的定义,我们需要结合实际实验的情况得到一个最贴合大部分人审美的色彩复杂度;而后者检测算法虽然层出不穷,但实际操作过程中要细致考虑到镜头的突变和渐变,我们通过引入加权的概念让计算不再是点对点的比较,而将整个计算过程都串联起来,取得了比较好的效果。

参考文献:

  • 基于多特征的视频镜头检测方法
  • 基于颜色加权的新闻视频镜头检测方法
  • 基于颜色直方图变化率的视频镜头检测
  • A Study of Image Colourfulness
  • HaslerS-色彩丰富度公式