Opencv-python圆盘指针仪表检测

问题和思路说明

目前所处理的图片全都是单指针圆盘图像,一个图里可能出现1-2个表,但是项目任务谈的是每个摄像头大概要拍摄3-4个表(但是到时候摄像头位置会很好,直接切开每个图就行)

从以下图片可以看出,首先,光线和角度变化不是非常大。

  • Pass每个表上面都有1-2个 红色/绿色的小指针(这个可以作为定位圆盘时一个很好的参照物,但是甲方说 这个指针是指示读数正常范围的,是后面安装的,不见得每个都有)
  • 虽然都是单指针圆盘表,但是每个表的仪表盘刻度读数不一样(需要预先设置几个模板,然后进行选择吧)
  • 可能需要进行圆盘的矫正,为了让读数更准确
  • 为了简化处理思路,base版本将图片进行切割,保证每个子图只含有一个表盘,一定程度保留background(也不能太简单,还是要有一定的泛化性和鲁棒性)
  • 主要参考 github-Pointer-meter-identification-and-reading这个的代码,前期没有写成类,为了便于测试,先写函数,最后再整理代码为类。
  • 关于滤波:可以看到图中是有许多明显的白色光点/斑点,不用在意滤波器会对表盘数字造成的影响,表盘都是固定的数值单位,只要能扣除表盘和指针,表盘刻度就是模板。
    • 除了使用单个滤波,还可以串行使用多个滤波器,有空再尝试

Opencv-python圆盘指针仪表检测_第1张图片
Opencv-python圆盘指针仪表检测_第2张图片
Opencv-python圆盘指针仪表检测_第3张图片
Opencv-python圆盘指针仪表检测_第4张图片

注意点

Opencv读取图片矩阵 rows cols以及height width的关系

这里要记住一点:
opencv中图像的x,y 坐标以及 height, width,rows,cols 他们的关系经常混淆。
rows 其实就是行,一行一行也就是y 啦。height高度也就是y啦。
cols 也就是列,一列一列也就是x啦。width宽度也就是x啦。

配合以下代码,以下图为例(这是一张640*604的图片):
Opencv-python圆盘指针仪表检测_第5张图片
cols和rows这个概念是 C++ 版本的opencv中有mat这种数据类型时涉及的,例如

Mat logo = imread("smile.png")
logo.cols, logo.rows

opencv和numpy数组长宽对应的问题

在计算机中,图像是以矩阵的形式保存的,先行后列。所以,一张 宽×高×颜色通道=480×256×3 的图片会保存在一个 256×480×3 的三维张量中

numpy的一般就是

  • 行数=y=高度
  • 列数=x=宽度
  • img.shape(y,x,chanels) shape的组成是这样的: (高,宽,通道数)
  • 即使用numpy创建一幅图像时,shape就是 (高度,宽度), 如果还有通道的话,就是 (高度,宽度,通道数).
>>> a = np.zeros((3,5))
>>> a
array([[0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.]])
//可以看到这是35列 即 3是高度 5是宽度。即 
//numpy的shape[0:2]和opencv中通过imread读取的图像的shape[0:2]是一样的的,即 (高度,宽度). 
// 单凡涉及到size(包括cv2.resize 其约定的顺序 都是 宽度 x *高度 y)

# 也可以考虑直接自己造一个图片
from PIL import Image
img=np.zeros(shape=(100,200,3))
cv2.imshow('small_pic',img)
img=np.zeros(shape=(100,200,3))
# show_img(img,'img')

imgPIL=Image.fromarray(img.astype('uint8')).convert('RGB')
# imgPIL.show()
print(imgPIL.size)

上述numpy创造的图像使用opencv显示,可以看出,

  • numpy里的长度 x=100 在opencv中显示为了 高度 y=100
    在这里插入图片描述
  • 如果使用PIL的Image来显示这个图像,可以看到,也是一样的
    img.size为(宽,高)
    arry_img.shape为(高,宽,通道数)
    arry_img.size为 高x宽x通道数
    Opencv-python圆盘指针仪表检测_第6张图片

结论

  • numpy的shape[0:2]和opencv中通过imread读取的图像的shape[0:2]是一样的的,即 (高度,宽度).
  • 但凡涉及到size(包括cv2.resize 其约定的顺序 都是 宽度*高度),比如PIL的Image的size属性,以及resize里的fx和fy的书写。

图片预处理

读取图片问题

由于某些原始的大图尺寸为3200*1800,已经超过我电脑的分辨率,所以最开始使用cv2.imshow()的时候显示的图像不完全,根据参考[1],进行以下修改:

cv2.namedWindow('image', cv2.WINDOW_NORMAL)
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

当遇到图像太大需要加入进度条或者需要手动调节窗口大小的时候,可以使用如下方式:
cv2支持先创建一个窗口在load图像,所以可以使用函数cv2.namedWindow()明确窗口是否尺寸可变,默认情况下,函数cv2.namedWindow()的flag值为cv2.WINDOW_AUTOSIZE,也可以修改为cv2.WINDOW_NORMAL,后者允许我们自己调节窗口大小。

图片缩放

这属于Opencv功能里的 » Image Processing in OpenCV » Geometric Transformations of Images

resize函数的原型:
cv::resize (InputArray src, OutputArray dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR)

img = cv2.imread('messi5.jpg')
res = cv2.resize(img,None,fx=2, fy=2, interpolation = cv2.INTER_CUBIC)
#OR
height, width = img.shape[:2]
# img.shape(height width channel(RGB 3通道 灰度图 单通道))
res = cv2.resize(img,(2*width, 2*height), interpolation = cv2.INTER_CUBIC)

# 我想要实现的是,固定高度,宽度按比例变化,则需要使用缩放因子 上述的fx(factor x 水平轴  dsize.width/src.cols   fx 图像长度缩放因子 fy 图像宽度缩放因子)
height=600
width=img.shape[0]/600*img.shape[1]

滤波器

如果是在jupyter notebook中的话,jupyter的ipywidget组件支持一些自定义滑块,可以帮助快速调节查看效果,后期考虑添加进去,快速进行调参(勉强算调参吧)
滤波器(图上确实存在一些 很零散的白色斑点/亮斑/噪声):Opencv官网说明

中值滤波

  • 中值滤波: 对于500*500的图,kernel size设置1 基本没用 设置3或者5 有些文字就糊了(kg/cm2 以及下面的psi基本就糊的看不见了),而且该在的区域白点还是在,不好使。
  • 函数原型:
void cv::medianBlur	(	InputArray 	src,
						OutputArray 	dst,
						int 	ksize 
					)		
Python:
dst	=	cv.medianBlur(	src, ksize[, dst]	)
  • 测试结果图解
    Opencv-python圆盘指针仪表检测_第7张图片
    Opencv-python圆盘指针仪表检测_第8张图片

双边滤波

  • 双边滤波去噪:双边滤波能在保持边界清晰的情况下有效的去除噪音。但是这种操作与其他滤波器相比会比较慢(相对于均值滤波就好很多,确实边界清晰了许多)
  • 函数原型:
void cv::bilateralFilter	(	InputArray 	src,
								OutputArray 	dst,
								int 	d,
								double 	sigmaColor,
								double 	sigmaSpace,
								int 	borderType = BORDER_DEFAULT 
								)		
Python:
dst	=	cv.bilateralFilter(	src, d, sigmaColor, sigmaSpace[, dst[, borderType]])
cv2.bilateralFilter(img, 9, 50, 50)
// 这里的 三个数字分别对应 9 50 50
三个参数的大意分别是:
d滤波期间使用的每个像素邻域的直径。 如果它不是正值,则从sigmaSpace计算得出。
sigmaColor在色彩空间中过滤sigma。 参数的较大值表示像素邻域内的其他颜色(请参见sigmaSpace)将混合在一起,从而导致较大区域的半均等颜色。
sigmaSpace在坐标空间中过滤sigma。 该参数的值越大,表示越远的像素就会相互影响,只要它们的颜色足够接近即可(请参见sigmaColor)。 当d> 0时,它指定邻域大小,而不考虑sigmaSpace。 否则,d与sigmaSpace成比例。
  • 测试结果图解:
    Opencv-python圆盘指针仪表检测_第9张图片
    Opencv-python圆盘指针仪表检测_第10张图片

高斯滤波

  • 函数原型
ksize 必须是整数且是奇数
sigmaX,sigmaY即x,y方向上的高斯分布的标准差
void cv::GaussianBlur	(	InputArray 	src,
							OutputArray 	dst,
							Size 	ksize,
							double 	sigmaX,
							double 	sigmaY = 0,
							int 	borderType = BORDER_DEFAULT 
							)		
Python:
dst	=	cv.GaussianBlur(	src, ksize, sigmaX[, dst[, sigmaY[, borderType]]]	)
  • 测试结果图解:
    Opencv-python圆盘指针仪表检测_第11张图片

结论

综上,

  • 直接输入原图进行滤波,则三个常见滤波,高斯和中值滤波都会把刻度边缘还有指针边缘弄得很糊,双边滤波应该比较好
  • 如果经过图片缩放和灰度图处理再进行滤波,则效果如下:
    Opencv-python圆盘指针仪表检测_第12张图片
    Opencv-python圆盘指针仪表检测_第13张图片
  • 从这四张图可以看到,如果去扣取指针和表盘(圆) 肯定也是双边滤波效果最好
  • 综上,确定使用双边滤波。
  • 此外, 除了使用单个滤波,还可以串行使用多个滤波器,有空再尝试

二值化

根据opencv二值化的cv2.threshold函数可知,常用于二值化的函数有两个,如下:

  1. 简单阈值函数:cv2.threshold (src, thresh, maxval, type),选取一个全局阈值,然后就把整幅图像分成了非黑即白的二值图像了,其四个参数的含义为:第一个原图像,第二个进行分类的阈值,第三个是高于(低于)阈值时赋予的新值,第四个是一个方法选择参数
    • 函数原型及参数说明:参考opencv->cv:threshold

    • 函数返回的第一个值就是输入的thresh值,第二个就是处理后的图像(参考OpenCV 中cv2.threshold详解,(大白话版))

    • 函数使用实例,参考opencv: 阈值处理(cv2.threshold) 探究(图示+源码),这个博客中的 阈值类型是使用数字表示的,表格有些歧义,改成下面比较好:(参考opencv官方ThresholdTypes:其中标明了threshold是enum cv::ThresholdTypes即Enumerator类型 枚举类型 所以可以使用数字序号去按顺序访问)
      Opencv-python圆盘指针仪表检测_第14张图片

    • 简单测试图示:
      Opencv-python圆盘指针仪表检测_第15张图片

  2. 自适应阈值函数

结论

一个窗口显示多图

由于Opencv显示多图目前比较常见方法就是把图都拼接在一起,然后放到一个窗口进行显示,所以简单起见,还是使用matplotlib通过subplot显示多图

参考链接

Opencv官方文档:

  1. opencv-python-tutroals
  2. Opencv4.3官方英语文档

相关指针仪表读数代码及博客:

  • github-Pointer-meter-identification-and-reading
  • github-OpenCV-Point-Meter-Detection
  • OpenCV 指针仪表盘参数读取(四) 指针定位
  • OpenCV 表盘指针自动读数的示例代码
  • python+opencv水表识别

Opencv-python检测圆Hough算法:

  • Python+OpenCV图像处理之圆检测
  • 系列专栏-【从零学习OpenCV 4】圆形检测
  • Detecting Circles in Images using OpenCV and Hough Circles

Opencv和numpy长宽参考:

  • numpy中的shape和opencv中的shape的区别
  • 老卫带你学—opencv中shape与resize的区别
  • OpenCV图像坐标系与行列宽高的关系

滤波器相关:

  • opencv图像处理之常见滤波器

二值化相关:

  • opencv: 阈值处理(cv2.threshold) 探究(图示+源码)
  • OpenCV-Python Tutorials-Image Thresholding
  • Opencv-官方Image Thresholding

你可能感兴趣的:(项目实战,opencv)