项目场景:
我们项目中使用的摄像头,在部署之后,有可能会遇到被人为遮挡、破坏,或者自然遮挡,再或者使用时间太长,摄像头已经有些损坏老化,拍摄的效果不好,导致我们的算法检测不到,需要检测的物品。人力排查又很麻烦,这里就写几个容易实现,检测效果还不错的小方法。一、色偏:
1.检测色偏,将RGB图像转变到CIE Lab空间,其中L表示图像亮度,a表示图像红/绿分量,b表示图像黄/蓝分量。 通常存在色偏的图像,在a和b分量上的均值会偏离原点很远,方差也会偏小; 通过计算图像在a和b分量上的均值和方差,就可评估图像是否存在色偏。
2.当我们的摄像头被树叶或者其他的物品遮挡的时候,画面就只会出现一种颜色或者说是一种颜色在画面中占得比例很大,所以我们可以通过检测画面的色偏程度来实现判断摄像头有没有被遮挡。
def colour(self,img):
img = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
l_channel, a_channel, b_channel = cv2.split(img)
h, w, _ = img.shape
da = a_channel.sum() / (h * w) - 128
db = b_channel.sum() / (h * w) - 128
histA = [0] * 256
histB = [0] * 256
for i in range(h):
for j in range(w):
ta = a_channel[i][j]
tb = b_channel[i][j]
histA[ta] += 1
histB[tb] += 1
msqA = 0
msqB = 0
for y in range(256):
msqA += float(abs(y - 128 - da)) * histA[y] / (w * h)
msqB += float(abs(y - 128 - db)) * histB[y] / (w * h)
import math
result = math.sqrt(da * da + db * db) / math.sqrt(msqA * msqA + msqB * msqB)
if result > 1:
print("有异常,可能被遮挡,d/m是:", result)
else:
print("正常,d/m是:", result)
二、清晰度:
1.Laplace 清晰度检测利用拉普拉斯算子计算图片的二阶导数,反映图片的边缘信息,同样事物的图片,清晰度高的,相对应的经过拉普拉斯算子滤波后的图片的方差也就越大。
2.摄像头老化,损坏,被遮挡,清晰度都会变低。
def definition(self,img,definitionnum=1000):
imgPath = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
imageVar = cv2.Laplacian(imgPath, cv2.CV_64F).var()
if imageVar < definitionnum:
print("有异常,清晰度的得分是:", imageVar)
else:
print("正常,清晰度的得分是:", imageVar)
三、两张图片的特征点对比:
1、提取关键点:关键点是一些十分突出的不会因光照、尺度、旋转等因素而消失的点,比如角点、边缘点、暗区域的亮点以及亮区域的暗点。此步骤是搜索所有尺度空间上的图像位置。通过高斯微分函数来识别潜在的具有尺度和旋转不变的兴趣点。
2、定位关键点并确定特征方向:在每个候选的位置上,通过一个拟合精细的模型来确定位置和尺度。关键点的选择依据于它们的稳定程度。然后基于图像局部的梯度方向,分配给每个关键点位置一个或多个方向。 所有后面的对图像数据的操作都相对于关键点的方向、尺度和位置进行变换,从而提供对于这些变换的不变性。
3. 通过各关键点的特征向量,进行两两比较找出相互匹配的若干对特征点,建立景物间的对应关系。
4.这个方法需要保存一张,摄像头状态好的时候的一张图片,可以防止摄像头移位等。
def detectAndDescribe(self,image):
# 将彩色图片转换成灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 建立SIFT生成器
descriptor = cv2.xfeatures2d.SIFT_create()
# 检测SIFT特征点,并计算描述子
(kps, features) = descriptor.detectAndCompute(image, None)
# 将结果转换成NumPy数组
kps = numpy.float32([kp.pt for kp in kps])
# 返回特征点集,及对应的描述特征
return (kps, features)
def matchKeypoints(self,kpsA, kpsB, featuresA, featuresB, ratio=0.75, reprojThresh=4.0,featurenum=400):
# 建立暴力匹配器
matcher = cv2.BFMatcher()
# 使用KNN检测来自A、B图的SIFT特征匹配对,K=2
rawMatches = matcher.knnMatch(featuresA, featuresB, 2)
matches = []
for m in rawMatches:
# 当最近距离跟次近距离的比值小于ratio值时,保留此匹配对
if len(m) == 2 and m[0].distance < m[1].distance * ratio:
# 存储两个点在featuresA, featuresB中的索引值
matches.append((m[0].trainIdx, m[0].queryIdx))
ptsA = numpy.float32([kpsA[i] for (_, i) in matches])
ptsB = numpy.float32([kpsB[i] for (i, _) in matches])
# 计算视角变换矩阵
(H, status) = cv2.findHomography(ptsA, ptsB, cv2.RANSAC, reprojThresh)
# 返回结果
point = 0
for ((trainIdx, queryIdx), s) in zip(matches, status):
# 当点对匹配成功时,画到可视化图上
if s == 1:
# 画出匹配对
point = point + 1
if point < featurenum:
print("有异常,清晰度的得分是:", point)
else:
print("正常,清晰度的得分是:", point)
def feature(self,img, path, filename,featurenumf=400):
try:
way = path + "/" + filename
imageB = cv2.imread(way)
imageA = cv2.resize(img, dsize=(600, 500))
imageB = cv2.resize(imageB, dsize=(600, 500))
(kpsA, featuresA) = self.detectAndDescribe(imageA)
(kpsB, featuresB) = self.detectAndDescribe(imageB)
self.matchKeypoints(kpsA, kpsB, featuresA, featuresB,featurenum=featurenumf)
print("\n")
except:
print("没有读取到图片")
print("\n")
四、亮度:
1.亮度检测,计算图片在灰度图上的均值和方差,当存在亮度异常时,均值会偏离均值点(可以假设为128),方差也会偏小;通过计算灰度图的均值和方差,就可评估图像是否存在过曝光或曝光不足。
2.摄像头画面被遮挡亮度不会很高,摄像头损坏的话,画面也会出现大面积黑,而且上述的方法在夜晚效果都不好,我们可以考虑只在白天检测。
def intensity(self,img):
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_shape = gray_img.shape
height, width = img_shape[0], img_shape[1]
size = gray_img.size
# 灰度图的直方图
hist = cv2.calcHist([gray_img], [0], None, [256], [0, 256])
# 计算灰度图像素点偏离均值(128)程序
a = 0
ma = 0
# numpy.full 构造一个数组,用指定值填充其元素
reduce_matrix = numpy.full((height, width), 128)
shift_value = gray_img - reduce_matrix
shift_sum = numpy.sum(shift_value)
da = shift_sum / size
# 计算偏离128的平均偏差
for i in range(256):
ma += (abs(i - 128 - da) * hist[i])
m = abs(ma / size)
# 亮度系数
k = abs(da) / m
if k[0] > 1:
# 过亮
if da > 0:
print("过亮")
else:
print("过暗")
else:
print("亮度正常:", k)
return 0
总结:
这些方法都是传统的方法,可以使用,而且简单方便,但是会有很多的限制,会出现各种误判。希望看到这篇文章的大佬和朋友们,如果你们有更好的方法,欢迎评论区交流!!!
我现在使用分类的算法做的,勉强可以用。