检测测试盒opencv上的线条 [英] Detecting lines on test cassettes opencv
问题描述
(C 处有一条实线,T 处有一条暗线)
(There is a solid line at C and a faint line at T)
我想检测 T 处的线.目前我正在使用 opencv 定位二维码并旋转图像,直到二维码直立.然后我通过使用二维码的坐标计算出C和T标记的大概位置.然后我的代码将沿 y 轴向下扫描并检测绿色和蓝色值是否存在差异.
I want to detect the line at T. Currently I am using opencv to locate the qr code and rotate the image until the qr code is upright. Then I calculate the approximate location of the C and T mark by using the coordinates of the qr code. Then my code will scan along the y axis down and detect there are difference in the Green and Blue values.
我的问题是,即使T线像图中那样微弱,也应该被认为是阳性的.我怎样才能进行更好的检测?
My problem is, even if the T line is as faint as shown, it should be regarded as positive. How could I make a better detection?
推荐答案
我只裁剪了白色条带,因为我假设您已经找到了它的方法.由于我们正在寻找红色,因此我更改为 LAB 色彩空间并查看a"颜色.频道.
I cropped out just the white strip since I assume you have a way of finding it already. Since we're looking for red, I changed to the LAB colorspace and looked on the "a" channel.
注意:为方便查看,条带的所有图像都已转置(np.transpose),代码中不是这样的.
Note: all images of the strip have been transposed (np.transpose) for viewing convenience, it's not that way in the code.
A 频道
我做了一个线性重构来提高对比度
I did a linear reframe to improve the contrast
图像非常嘈杂.同样,我不确定这是来自相机还是 jpg 压缩.我平均每一行以消除一些废话.
The image is super noisy. Again, I'm not sure if this is from the camera or the jpg compression. I averaged each row to smooth out some of the nonsense.
我绘制了强度(x-vals 是行索引)
I graphed the intensities (x-vals were the row index)
使用均值滤波器平滑图形
Use a mean filter to smooth out the graph
我运行了一个登山者算法来寻找山峰和山谷
I ran a mountain climber algorithm to look for peaks and valleys
然后我过滤了爬升大于 10 的山峰(第二高的山峰有 25.5 的爬升,第三高是 4.4).
And then I filtered for peaks with a climb greater than 10 (the second highest peak has a climb of 25.5, the third highest is 4.4).
使用这些峰值,我们可以确定有两条线,它们(大约)在这里:
Using these peaks we can determine that there are two lines and they are (about) here:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# returns direction of gradient
# 1 if positive, -1 if negative, 0 if flat
def getDirection(one, two):
dx = two - one;
if dx == 0:
return 0;
if dx > 0:
return 1;
return -1;
# detects and returns peaks and valleys
def mountainClimber(vals, minClimb):
# init trackers
last_valley = vals[0];
last_peak = vals[0];
last_val = vals[0];
last_dir = getDirection(vals[0], vals[1]);
# get climbing
peak_valley = []; # index, height, climb (positive for peaks, negative for valleys)
for a in range(1, len(vals)):
# get current direction
sign = getDirection(last_val, vals[a]);
last_val = vals[a];
# if not equal, check gradient
if sign != 0:
if sign != last_dir:
# change in gradient, record peak or valley
# peak
if last_dir > 0:
last_peak = vals[a];
climb = last_peak - last_valley;
climb = round(climb, 2);
peak_valley.append([a, vals[a], climb]);
else:
# valley
last_valley = vals[a];
climb = last_valley - last_peak;
climb = round(climb, 2);
peak_valley.append([a, vals[a], climb]);
# change direction
last_dir = sign;
# filter out very small climbs
filtered_pv = [];
for dot in peak_valley:
if abs(dot[2]) > minClimb:
filtered_pv.append(dot);
return filtered_pv;
# run an mean filter over the graph values
def meanFilter(vals, size):
fil = [];
filtered_vals = [];
for val in vals:
fil.append(val);
# check if full
if len(fil) >= size:
# pop front
fil = fil[1:];
filtered_vals.append(sum(fil) / size);
return filtered_vals;
# averages each row (also gets graph values while we're here)
def smushRows(img):
vals = [];
h,w = img.shape[:2];
for y in range(h):
ave = np.average(img[y, :]);
img[y, :] = ave;
vals.append(ave);
return vals;
# linear reframe [min1, max1] -> [min2, max2]
def reframe(img, min1, max1, min2, max2):
copy = img.astype(np.float32);
copy -= min1;
copy /= (max1 - min1);
copy *= (max2 - min2);
copy += min2;
return copy.astype(np.uint8);
# load image
img = cv2.imread("strip.png");
# resize
scale = 2;
h,w = img.shape[:2];
h = int(h*scale);
w = int(w*scale);
img = cv2.resize(img, (w,h));
# lab colorspace
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB);
l,a,b = cv2.split(lab);
# stretch contrast
low = np.min(a);
high = np.max(a);
a = reframe(a, low, high, 0, 255);
# smush and get graph values
vals = smushRows(a);
# filter and round values
mean_filter_size = 20;
filtered_vals = meanFilter(vals, mean_filter_size);
for ind in range(len(filtered_vals)):
filtered_vals[ind] = round(filtered_vals[ind], 2);
# get peaks and valleys
pv = mountainClimber(filtered_vals, 1);
# pull x and y values
pv_x = [ind[0] for ind in pv];
pv_y = [ind[1] for ind in pv];
# find big peaks
big_peaks = [];
for dot in pv:
if dot[2] > 10: # climb filter size
big_peaks.append(dot);
print(big_peaks);
# make plot points for the two best
tops_x = [dot[0] for dot in big_peaks];
tops_y = [dot[1] for dot in big_peaks];
# plot
x = [index for index in range(len(filtered_vals))];
fig, ax = plt.subplots()
ax.plot(x, filtered_vals);
ax.plot(pv_x, pv_y, 'og');
ax.plot(tops_x, tops_y, 'vr');
plt.show();
# draw on original image
h,w = img.shape[:2];
for dot in big_peaks:
y = int(dot[0] + mean_filter_size / 2.0); # adjust for mean filter cutting
cv2.line(img, (0, y), (w,y), (100,200,0), 2);
# show
cv2.imshow("a", a);
cv2.imshow("strip", img);
cv2.waitKey(0);
我想知道为什么这些线条看起来如此偏离,然后我意识到我忘记考虑 meanFilter 减小列表大小(它从正面和背面切割)这一事实.我已经更新以考虑到这一点.
I was wondering why the lines seemed so off, then I realized that I forgot to account for the fact that the meanFilter reduces the size of the list (it cuts from the front and back). I've updated to take that into account.
这篇关于检测测试盒opencv上的线条的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!