检测测试盒opencv上的线

Detecting lines on test cassettes opencv

(C处有实线,T处有淡线)

我想检测T处的线,目前正在用opencv定位二维码,旋转图片直到二维码竖直。然后我通过二维码的坐标计算出C和T标记的大概位置。然后我的代码将沿 y 轴向下扫描并检测绿色和蓝色值是否存在差异。

我的问题是,即使T线像图中那样微弱,也应该算是阳性。我怎样才能做出更好的检测?

我只裁剪了白色条带,因为我假设您已经有办法找到它。由于我们要寻找红色,因此我更改为 LAB 色彩空间并查看“a”通道。

注意:为了方便查看,所有的条带图像都被摆运行(np.transpose),代码中不是这样。

A频道

我做了一个线性重构来提高对比度

图像超级嘈杂。同样,我不确定这是来自相机还是 jpg 压缩。我对每一行进行平均以消除一些废话。

我绘制了强度图(x-vals 是行索引)

使用均值滤波器平滑图形

我运行一个寻找山峰和山谷的登山算法

然后我过滤了爬升大于 10 的山峰(第二高的山峰有 25.5 的爬升,第三高的是 4.4)。

使用这些峰值我们可以确定有两条线,它们(大约)在这里:

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 减少列表大小的事实(它从正面和背面切割)。我已经更新以考虑到这一点。