使用python检测裁剪面部的运动模糊,即opencv



我正在用haarcascade检测人脸,并使用OpenCV用网络摄像头跟踪它们。我需要保存被跟踪的每张脸。但问题是人们什么时候在移动。在这种情况下,脸部变得模糊。

我试图用 opencv 的 dnn 人脸检测器和 Laplacian 使用以下代码来缓解这个问题:

blob = cv2.dnn.blobFromImage(cropped_face, 1.0, (300, 300), (104.0, 177.0, 123.0))
net.setInput(blob)
detections = net.forward()
confidence = detections[0, 0, 0, 2]
blur = cv2.Laplacian(cropped_face, cv2.CV_64F).var()
if confidence >= confidence_threshold and blur >= blur_threshold:
cv2.imwrite('less_blurry_image', cropped_face)

在这里,我试图将保存一张脸(如果它不是由于运动而模糊)限制为setting blur_threshold到 500,confidence_threshold限制为 0.98(即 98%)。

但问题是,如果我更换相机,我必须再次手动更改阈值。在大多数情况下,设置阈值会省略大多数人脸。

另外,很难检测到,因为与模糊的面部相比,背景总是清晰的。

所以我的问题是我如何检测脸上的这种运动模糊。我知道我可以训练一个 ML 模型来检测面部的运动模糊。但这需要大量的处理资源来完成一项小任务。

此外,如果我走这条路,我将需要大量的带注释的数据进行训练。对于像我这样的学生来说,这并不容易。

因此,我正在尝试使用 OpenCV 来检测这一点,与使用 ML 模型进行检测相比,这将大大减少资源密集。

是否有任何资源密集度较低的解决方案?

您可能可以使用傅里叶变换(FFT)或离散余弦变换(DCT)来确定脸部的模糊程度。图像模糊导致高频消失,只剩下低频。

因此,您可以拍摄脸部的图像,将其归零到适用于FFT或DCT的尺寸,然后查看在更高频率下具有多少光谱功率。

您可能不需要FFT - DCT就足够了。DCT的优点是它产生实值结果(没有虚部)。在性能方面,FFT 和 DCT 对于 2 的幂尺寸以及只有因子 2、3 和 5 的尺寸非常快(尽管如果您也有 3 和 5,它会慢一点)。

正如@PlinyTheElder所提到的,DCT信息可以给你运动模糊。我正在下面附加存储库中的代码片段:

代码在C,我不确定是否有 python 绑定libjpeg.否则,您需要创建一个。

/* Fast blur detection using JPEG DCT coefficients
*
* Based on "Blur Determination in the Compressed Domain Using DCT
* Information" by Xavier Marichal, Wei-Ying Ma, and Hong-Jiang Zhang.
*
* Tweak MIN_DCT_VALUE and MAX_HISTOGRAM_VALUE to adjust
* effectiveness.  I reduced these values from those given in the
* paper because I find the original to be less effective on large
* JPEGs.
*
* Copyright 2010 Julian Squires <julian@cipht.net>
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <jpeglib.h>
static int min_dct_value = 1;   /* -d= */
static float max_histogram_value = 0.005; /* -h= */
static float weights[] = {  /* diagonal weighting */
8,7,6,5,4,3,2,1,
1,8,7,6,5,4,3,2,
2,1,8,7,6,5,4,3,
3,2,1,8,7,6,5,4,
4,3,2,1,8,7,6,5,
5,4,3,2,1,8,7,6,
6,5,4,3,2,1,8,7,
7,6,5,4,3,2,1,8
};
static float total_weight = 344;
static inline void update_histogram(JCOEF *block, int *histogram)
{
for(int k = 0; k < DCTSIZE2; k++, block++)
if(abs(*block) > min_dct_value) histogram[k]++;
}
static float compute_blur(int *histogram)
{
float blur = 0.0;
for(int k = 0; k < DCTSIZE2; k++)
if(histogram[k] < max_histogram_value*histogram[0])
blur += weights[k];
blur /= total_weight;
return blur;
}

static int operate_on_image(char *path)
{
struct jpeg_error_mgr jerr;
struct jpeg_decompress_struct cinfo;
jvirt_barray_ptr *coeffp;
JBLOCKARRAY cs;
FILE *in;
int histogram[DCTSIZE2] = {0};
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
if((in = fopen(path, "rb")) == NULL) {
fprintf(stderr, "%s: Couldn't open.n", path);
jpeg_destroy_decompress(&cinfo);
return 0;
}
jpeg_stdio_src(&cinfo, in);
jpeg_read_header(&cinfo, TRUE);
// XXX might be a little faster if we ask for grayscale
coeffp = jpeg_read_coefficients(&cinfo);
/* Note: only looking at the luma; assuming it's the first component. */
for(int i = 0; i < cinfo.comp_info[0].height_in_blocks; i++) {
cs = cinfo.mem->access_virt_barray((j_common_ptr)&cinfo, coeffp[0], i, 1, FALSE);
for(int j = 0; j < cinfo.comp_info[0].width_in_blocks; j++)
update_histogram(cs[0][j], histogram);
}
printf("%fn", compute_blur(histogram));
// output metadata XXX should be in IPTC etc
// XXX also need to destroy coeffp?
jpeg_destroy_decompress(&cinfo);
return 0;
}
int main(int argc, char **argv)
{
int status, i;
for(status = 0, i = 1; i < argc; i++) {
if(argv[i][0] == '-') {
if(argv[i][1] == 'd')
sscanf(argv[i], "-d=%d", &min_dct_value);
else if(argv[i][1] == 'h')
sscanf(argv[i], "-h=%f", &max_histogram_value);
continue;
}
status |= operate_on_image(argv[i]);
}
return status;
}

编译代码:

gcc -std=c99 blur_detection.c -l jpeg -o blur-detection

运行代码:

./blur-detection <image path>

最新更新