基础版本为i3-6100cpu耗时平均0.3秒
True:109
False:16
识别准确率为:0.872
高级版为i3-6100cpu耗时平均为0.45秒
True:230
False:10
识别准确率为:0.9583
经过大量测试(高级版):
True:715
False:33
准确率为:0.9558
其实可以再进行一些优化达到96%以上,不过由于价值不大,目前状态已经适用于商业,也适用于个人,所以不再改进优化。
以下是说明:
Canny边缘算子加上固定阈值只能85%左右
以下四种算子的效果不是太好,但是有推荐算法或者是神经网络的意识和想法,那么不同算子之间可以提供不同的信息,还有对应不同的匹配度有一定的信息可以被提取出来。
Roberts算子
Prewitt 算子
Sobel 算子
Laplacian 算子
不同算子的效果可以去除干扰,还能提取出轮廓,对提高识别率很有帮助。
通过使用不同阈值进行匹配,不同阈值能够提取的信息也是不同的,这种方法提供了强大的覆盖范围,能够提取大量信息,但是不适用于只有某一个小范围阈值才能精准对齐的。
五种算子匹配完后记录每一个距离的评分,评分提供了距离的依据,但是由于不同算子的效果,会出现一些距离偏差。
因为距离相差几像素是无所谓的,所以可以合并起来,评分相加。
这时候得出来的最大评分或者说最大权重对应的距离,就是滑块匹配的距离。
附上代码:
import cv2
import numpy as np
## 采用的是基础版本
def sliderSlip(address, width=50):
"""
基础版本为i3-6100cpu耗时平均0.3秒
True:109
False:16
识别准确率为:0.872
这里要说明一下,滑块可能与拼图的滑动长度不一致,要细微调整比例关系
address:图片地址和名字
width:从左边开始裁剪x轴宽度是多少
list_class:边缘识别阈值
list_d:边缘识别阈值下限
list_h:边缘识别阈值上限
list_val:保存每种阈值的最高匹配度
list_tl_x:保存每种阈值的滑动距离
return: X:移动X轴上的长度
"""
# 读取图片,通过灰度加载来更好完成识别任务
image = cv2.imread(address, cv2.IMREAD_GRAYSCALE)
# 指定裁剪区域的起始位置和宽度、高度
x, y = 0, 0
# 从左边开始裁剪x轴宽度是多少
width = width
# 获取图片的高
height = image.shape[0]
# 裁剪图像
tp_image = image[y:y + height, x:x + width] # 裁剪图片
bg_image = image[y:y + height, width:image.shape[1]] # 识别的背景图片
# 高斯模糊
tp_image = cv2.GaussianBlur(tp_image, (5, 5), sigmaX=0)
bg_image = cv2.GaussianBlur(bg_image, (5, 5), sigmaX=0)
# 灰度直方图均衡
tp_image = cv2.equalizeHist(tp_image)
bg_image = cv2.equalizeHist(bg_image)
# 识别图片边缘
# bg_edge = cv2.Canny(bg_image, 100, 200)
# tp_edge = cv2.Canny(tp_image, 100, 200)
# 匹配算法
# res = cv2.matchTemplate(bg_edge, tp_edge, cv2.TM_CCOEFF_NORMED) #寻图
# min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res) # 寻找最优匹配
# tl = max_loc # 左上角点的坐标
# X = tl[0]+width #x轴长度
list_class = ["较高", "正常", "调试", "中", "低"]
list_d = [150, 100, 10, 80, 10]
list_h = [250, 200, 150, 150, 80]
list_val = []
list_tl_x = []
# 所有分辨匹配机制
for i in range(len(list_class)):
bg_edge = cv2.Canny(bg_image, list_d[i], list_h[i])
tp_edge = cv2.Canny(tp_image, list_d[i], list_h[i])
res = cv2.matchTemplate(bg_edge, tp_edge, cv2.TM_CCOEFF_NORMED) # 寻图
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res) # 寻找最优匹配
list_val.append(max_val)
tl = max_loc # 左上角点的坐标
list_tl_x.append(tl[0])
# 去除匹配为1.0
n = 0
for i in list_val:
if i == 1.0:
list_val[n] = 0
n += 1
# 获取最大匹配度的索引
index = list_val.index(max(list_val))
X = list_tl_x[index] + width # x轴长度
if list_val[index] < 0.2:
# 最佳匹配度过低,采用众数匹配
list_l = np.arange(10, 500, 50)
list_u = np.arange(10, 500, 50)
v = []
x = []
for i in list_l:
for k in list_u:
bg_edge = cv2.Canny(bg_image, i, k)
tp_edge = cv2.Canny(tp_image, i, k)
res = cv2.matchTemplate(bg_edge, tp_edge, cv2.TM_CCOEFF_NORMED) # 寻图
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res) # 寻找最优匹配
v.append(max_val)
x.append(max_loc[0])
n = 0
for i in v:
if i == 1.0:
v[n] = 0
n += 1
word_dict = {}
n = 0
for word in x:
# 去除不匹配
if word > 10:
if word not in word_dict:
word_dict[word] = v[n]
else:
word_dict[word] += v[n]
n += 1
# 修改滑动距离
X = max(word_dict, key=word_dict.get) + width
# 返回长度
return X
## 采用的是高级版本
def sliderSlipHigh(address, width=50):
"""
高级版为i3-6100cpu耗时平均为0.45秒
True:230
False:10
识别准确率为:0.9583
这里要说明一下,滑块可能与拼图的滑动长度不一致,要细微调整比例关系
address:图片地址和名字
width:从左边开始裁剪x轴宽度是多少
list_class:边缘识别阈值
list_d:边缘识别阈值下限
list_h:边缘识别阈值上限
list_val:保存每种阈值的最高匹配度
list_tl_x:保存每种阈值的滑动距离
return: X:移动X轴上的长度
"""
# Roberts算子
def Roberts(image):
kernelx = np.array([[-1, 0], [0, 1]], dtype=int)
kernely = np.array([[0, -1], [1, 0]], dtype=int)
x = cv2.filter2D(image, cv2.CV_16S, kernelx)
y = cv2.filter2D(image, cv2.CV_16S, kernely)
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
image = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
return image
# Prewitt 算子
def Prewitt(image):
kernelx = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]], dtype=int)
kernely = np.array([[-1, -1, -1], [0, 0, 0], [1, 1, 1]], dtype=int)
x = cv2.filter2D(image, cv2.CV_16S, kernelx)
y = cv2.filter2D(image, cv2.CV_16S, kernely)
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
image = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
return image
# Sobel 算子
def Sobel(image):
x = cv2.Sobel(image, cv2.CV_16S, 1, 0)
y = cv2.Sobel(image, cv2.CV_16S, 0, 1)
absX = cv2.convertScaleAbs(x)
absY = cv2.convertScaleAbs(y)
image = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
return image
# 拉普拉斯算法
def Laplacian(image):
dst = cv2.Laplacian(image, cv2.CV_16S, ksize=3)
image = cv2.convertScaleAbs(dst)
return image
# Roberts算子
# kernelx = np.array([[-1, 0], [0, 1]], dtype=int)
# kernely = np.array([[0, -1], [1, 0]], dtype=int)
# Prewitt 算子
# kernelx = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]], dtype=int)
# kernely = np.array([[-1, -1, -1], [0, 0, 0], [1, 1, 1]], dtype=int)
# x = cv2.filter2D(image, cv2.CV_16S, kernelx)
# y = cv2.filter2D(image, cv2.CV_16S, kernely)
# # Sobel 算子
# x = cv2.Sobel(image, cv2.CV_16S, 1, 0)
# y = cv2.Sobel(image, cv2.CV_16S, 0, 1)
#
#
# 转uint8
# absX = cv2.convertScaleAbs(x)
# absY = cv2.convertScaleAbs(y)
# image = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
# # 拉普拉斯算法
# dst = cv2.Laplacian(image, cv2.CV_16S, ksize=3)
# image = cv2.convertScaleAbs(dst)
# 图片预处理
def imageDeal(image,width):
# 指定裁剪区域的起始位置和宽度、高度
x = 0
y = 0
width = width # 从左边开始裁剪x轴宽度是多少
height = image.shape[0]
# 裁剪图像
tp_image = image[y:y + height, x:x + width]
bg_image = image[y:y + height, width:image.shape[1]]
# 高斯模糊
tp_image = cv2.GaussianBlur(tp_image, (5, 5), sigmaX=0)
bg_image = cv2.GaussianBlur(bg_image, (5, 5), sigmaX=0)
# 灰度直方图均衡
tp_image = cv2.equalizeHist(tp_image)
bg_image = cv2.equalizeHist(bg_image)
return tp_image, bg_image
# 识别图片边缘
# bg_edge = cv2.Canny(bg_image, 100, 200)
# tp_edge = cv2.Canny(c_image, 100, 200)
# 匹配算法
# res = cv2.matchTemplate(bg_edge, tp_edge, cv2.TM_CCOEFF_NORMED)
# min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res) # 寻找最优匹配
# tl = max_loc # 左上角点的坐标
# print("匹配率:",max_val)
# X= tl[0]+width
# print(X)
list_val = [] # 匹配值
list_tl_x = [] # 匹配坐标
# 循环处理将图片进行匹配处理
def matchTemplate(bg_image, tp_image):
list_l = np.arange(10, 400, 50)
#list_l = np.arange(10, 200, 20) #更高精确度,但是时间更多一些
list_u = np.arange(10, 400, 50)
list_repeat = []
for i in list_l:
list_repeat.append(i)
for k in list_u:
if k not in list_repeat :
bg_edge = cv2.Canny(bg_image, i, k)
tp_edge = cv2.Canny(tp_image, i, k)
res = cv2.matchTemplate(bg_edge, tp_edge, cv2.TM_CCOEFF_NORMED) # 寻图
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res) # 寻找最优匹配
list_val.append(max_val)
tl = max_loc # 左上角点的坐标
list_tl_x.append(tl[0])
# 通过不同算子的处理
image = cv2.imread(address, cv2.IMREAD_GRAYSCALE)
b, t = imageDeal(Roberts(image),width)
matchTemplate(b, t)
b, t = imageDeal(Prewitt(image),width)
matchTemplate(b, t)
b, t = imageDeal(Sobel(image),width)
matchTemplate(b, t)
b, t = imageDeal(Laplacian(image),width)
matchTemplate(b, t)
# 普通边缘识别
b, t = imageDeal(image,width)
matchTemplate(b, t)
# 显示裁剪后的图像
n = 0
for i in list_val:
if i == 1.0:
list_val[n] = 0
n += 1
word_dict = {}
n = 0
# 去除坐标异常的数据
for word in list_tl_x:
if word > 10 and word < image.shape[1] - width* 2 - 10:
if word not in word_dict:
word_dict[word] = list_val[n]
else:
word_dict[word] += list_val[n]
n += 1
# 排序
list_d = sorted(word_dict.items(), key=lambda d: d[1], reverse=True)
list_keys = []
for i in list_d:
list_keys.append(i[0])
# 使用集合来跟踪已考虑的元素
seen = set()
val_dict = {}
n = 3 # 聚类距离
# 寻找相似距离
def seen_find(number):
for i in seen:
if i - n <= number <= i + n:
return i
# 聚类合并
for i in list_keys:
change = 0
# 检查当前元素是否在允许的范围内
if not any(abs(i - k) <= n and k != i for k in seen):
change = 1
seen.add(i)
if i not in val_dict:
val_dict[i] = word_dict.pop(i)
if change == 0: # 当没有改变值的时候说明没有通过判断语句,所以是合并的值,将值放入
# 通过找到相似key来传递value
val_dict[seen_find(i)] += word_dict.pop(i)
# 拿取最大权重对应的距离
X = max(val_dict, key=val_dict.get) + width
# 返回长度
return X
问题1:
如果发现准确率不高,而且容易滑到中间,这时候就要注意是不是width没有包裹住滑块即:从左开始到右边的width距离,没有很好截取到滑块,所以识别出了问题。
问题2:
如果发现报错为空的情况,请查看图片是否为中文,因为opencv不支持中文,改成非中文就行了。
问题3:
如果在处理时候发现速度太慢了,可以通过修改图片大小再处理缩放问题解决、或者是修改对应参数list_l和list_u里面的值,但是如果对精度有要求,那么可以删除其中1个算子,影响性能在3%以内,但是时间会减少20%左右。
问题4:
如果要做自动调试参数,现在是没有自动调试参数的。因为现在的准确率较高,所以在调试的时候要的图片数据要比较多,先处理一次标记数据,手动修改部分标记数据。修改一部分参数作为动态参数,以x张图片为一轮,计算对应准确率,其中正确权重外的不同权重的值占比作为loss损失,为了防止过拟合,要考虑一部分数据作为测试集。
问题5:
如果想要更好的性能,升级算法的方法(猜测):
- Eliciting Thinking Hierarchy without a Prior——在没有先验的情况下引出思维层次:
论文摘要如下:
当我们运用群体的智慧时,我们通常会根据答案的受欢迎程度对答案进行排名,尤其是在我们无法验证答案的时候。然而,当大多数人犯系统性错误时,这可能是非常危险的。一个基本的问题出现了:我们能否在没有任何先验的情况下,在答案之间建立一个层次结构,让那些可能不被大多数人支持的、排名更高的答案来自更老练的人?为了解决这个问题,我们提出一个新的模型来描述人们的思维层次;两种无先验学习思维层次的算法;基于上述理论框架的开放响应众包方法。除了理论论证外,我们还进行了四项实证众包研究,结果表明:我们的方法获得的排名答案的准确性远远高于多数投票(在一个问题中,多数答案得到74名受访者的支持,而正确答案只有3名受访者的支持)。我们的方法在没有任何先验的情况下将正确答案排在最高;我们的模型具有很高的拟合优度,特别是对于我们排名靠前的答案是正确的问题。据我们所知,我们是第一个在一般问题解决方案中提出具有经验验证的思维层次模型的人;并且第一个提出了一个实用的基于开放响应的众包方法,它击败了没有任何事先决定的多数投票。
算法可以这样:
推导多个数值,为了防止维度灾难,这里只取两个,其中最大匹配的标记为正确,其余随机挑选1个为标记错误。
从这里形成一个二维矩阵。
其中,知道正确答案的高认知水平的,也必然知道低认知水平的答案是错误的。
论文细节如下: