本程序主要利用影像处理,以及影像色彩统计并加以分析的方式,对台币进行分类标记。
使用函数HoughCircles 来侦测圆边,其中大多数数值都需要手动调整来提高标记的正确率,进行消除噪声的前处理可以减少误判的情况。
关于函数使用可参考以下官方网址:
https://docs.opencv.org/master/da/d53/tutorial_py_houghcircles.html
cv2.HoughCircles(image,method,dp,minDist[, circles[,param1, param2[,minRadius[,maxRadius]]]])
image:输入矩阵
method:cv2.HOUGH_GRADIENT 霍夫圆检测,梯度法
dp:计数器的分辨率图像像素分辨率与参数空间分辨率的比值
minDist:圆心之间最小距离,如果距离太小,会产生很多相交的圆,如果距离太大,则会漏掉正确的圆
param1:canny检测的双阈值中的高阈值,低阈值是它的一半
param2:最小投票数(基于圆心的投票数)
minRadius:需要检测圆的最小半径
maxRadius:需要检测圆的最大半径
import numpy as np #處理list跟數值的常用模組
import cv2 as cv
from google.colab.patches import cv2_imshow #colab所使用的imshow
import matplotlib.pyplot as plt #畫圖用
from PIL import Image
img = cv.imread('coins.jpg')
imgGlay = cv.imread('coins.jpg',0) #載入灰階圖片
imgGlay = cv.medianBlur(imgGlay,5) #消除噪訊
cimg = cv.cvtColor(imgGlay,cv.COLOR_GRAY2BGR)
circles = cv.HoughCircles(imgGlay,cv.HOUGH_GRADIENT,1,25,param1=30,param2=40,minRadius=30,maxRadius=70) #偵測圓圈
circles = np.uint16(np.around(circles))
利用硬币半径先将硬币大致进行分类,本次测试只有1 5 10 元,因此只从图表中间进行切割,当然如果有50元的话也可以利用类似比例切割的方法,因为在固定比例的情况下相机的误差有限。
其中输出的数值xy 为图片上坐标,r 为半径单位像素。
array = []
for i in circles[0,:]:
array.append(i[2])
print('x = {:>4d} y = {:>4d} r = {:>3d}'.format(i[0],i[1],i[2])) #數值整形
mid = (max(array)+min(array))/2
print("mid = {:.2f}".format(mid))
import matplotlib.pyplot as plt #直方圖繪製
plt.hist(array,10) #只有10條的模糊直方圖
plt.xlabel('coins size (pt)') #標籤
plt.ylabel('amount')
plt.title('Coins size and amount')
plt.show()
x = 94 y = 410 r = 58
x = 212 y = 552 r = 57
x = 260 y = 96 r = 53 x =
244 y = 324 r = 56
x = 242 y = 198 r = 44 x
= 224 y = 428 r = 44
x = 408 y = 236 r = 46
x = 334 y = 470 r = 44
x = 116 y = 280 r = 43
x = 376 y = 136 r = 42
x = 352 y = 318 r = 41
x = 108 y = 110 r = 44
mid = 49.50
把输出的数字利用for的方式绘制在图片上,为了方便人工侦错把list序号也标记上去。
imgd = cv.imread('coins.jpg')
flag = 0
for i in circles[0,:]: #直接迭代資料
text = str(flag) #產生文字編號
#繪制文字
cv.putText(imgd, text, (i[0]-5, i[1]+5), cv.FONT_HERSHEY_SIMPLEX, .7, (0, 255, 255), 1, cv.LINE_AA)
#繪制圓圈
if i[2] > mid:
cv.circle(imgd,(i[0],i[1]),i[2],(0,255,0),2)
else :
cv.circle(imgd,(i[0],i[1]),i[2],(255,0,0),2)
flag += 1
cv2_imshow(imgd)
先截取硬币的部分面积进行分析,产生同时分别有RGB统计的直方图(这跟Photoshop上的直方图功能是同一个原理)。
可以发现蓝色绿色的总量,在1元铜色的情况下比例会比5元或是10元银色的比例低,所以可以利用这项特性来分类两种颜色的硬币。
这边是用中位数找比值,可以修改flag数值观察。
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
src= Image.open('coins.jpg')
flag = 8
cropPict = src.crop((circles[0,flag][0]-30, circles[0,flag][1]-30, circles[0,flag][0]+30, circles[0,flag][1]+30)) #截取
r,g,b=cropPict.split()
# 統計RGB總量直方圖
ar=np.array(r).flatten()
plt.hist(ar, bins=256, density=True,facecolor='r',edgecolor='r')
ag=np.array(g).flatten()
plt.hist(ag, bins=256, density=True, facecolor='g',edgecolor='g')
ab=np.array(b).flatten()
plt.hist(ab, bins=256, density=True, facecolor='b',edgecolor='b')
plt.show()
print(np.median(np.array(r)))
print(np.median(np.array(g)))
print(np.median(np.array(b)))
143.0
116.0
101.0
利用前述之方式进行分类,公式只是很简单的除比例。
这边只使用了手工的方式输入分类比例为1.3,并产生一个手动计算的list。
coinCategory = []
for i in circles[0,:]:
cropPict = src.crop((i[0]-30, i[1]-30, i[0]+30, i[1]+30)) #截取
r,g,b=cropPict.split()
#統計中位數
medR = np.median(np.array(r))
medG = np.median(np.array(g))
medB = np.median(np.array(b))
#print(medR, medG, medB)
rate = medR /((medG + medB) / 2) #比例
if rate < 1.3 :
coinCategory.append(1) #白銀色就 1
print('{:.2f} , it is white'.format(rate))
else :
coinCategory.append(0) #銅色就 0
print('{:.2f}'.format(rate))
print('coinCategory {}'.format(coinCategory))
1.09 , it is white
1.25 , it is white
1.08 , it is white
1.19 , it is white
1.34
1.47
1.08 , it is white
1.15 , it is white
1.32
1.33
1.49
1.31
coinCategory [1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0]
利用同样的绘图方式,把分类好的数字画上去就完成分类了!
imgd = cv.imread('coins.jpg')
flag = 0
for i in circles[0,:]:
if i[2] > mid: #判斷半徑中值
cv.putText(imgd, '10', (i[0]-5, i[1]+5), cv.FONT_HERSHEY_SIMPLEX, .7, (0,255,0), 1, cv.LINE_AA) #繪制文字
cv.circle(imgd,(i[0],i[1]),i[2],(0,255,0),2) #繪制圓圈
else :
text = str(1 + coinCategory[flag] * 4) #判斷顏色
cv.putText(imgd, text, (i[0]-5, i[1]+5), cv.FONT_HERSHEY_SIMPLEX, .7, (255,coinCategory[flag]*255,0), 1, cv.LINE_AA) #繪制文字
cv.circle(imgd,(i[0],i[1]),i[2],(255,coinCategory[flag]*255,0),2) #繪制圓圈
flag += 1
cv2_imshow(imgd)
HoughCircles并不是一个「聪明的」函数,它只能应付一些比较干净没有太多杂物的图片。如果图片受到光线影响可能又会严重影响精度,所以目前本程序的应用能力有限。
颜色怎么判断也是值得再深入研究的部分,比如利用寻找峰值函数peak去处理,甚至寻找特征来提高程序的鲁棒性,这样,程序就可能可以不再限于某一两种货币而是一个通用的工具了。