计算机视觉小实例 No.3 基于Hough变化的答题卡识别

答题卡识别

答题卡自动阅卷系统通过获取答题卡图像作为系统输入,并通过计算机处理、自动识别填图标记,存入数据库完成阅卷。
但是答题卡在运输和使用过程中,容易受到设备、环境等因素的影响,使得图像质量在一定程度上有所下降,影响了自动阅卷的准确率,甚至导致无法正常阅卷,因此要对答题卡图像进行一系列的预处理,滤去干扰、噪声,做几何校正(有的答题卡可能是倒着的),彩色校正等,并进行二值化处理。

图像二值化

图像的二值化,就是将图像的像素点只有1和0的取值,用来表示黑(0)白(1)二种颜色。

 f = imread('答题卡.jpg');
 subplot(1, 2, 1), imshow(f), title('原图');
 subplot(1, 2, 2), imshow(im2bw(f, graythresh(f))), title('二值化');

这里写图片描述

可以看到,如果不预先对图像进行处理的话,就会出现这种情况,那么结合我们现在学习的知识,让我们试一试一些处理方法吧。

对比度增强处理
 f = imread('答题卡.jpg');
 f = rgb2gray(f);
 subplot(1, 3, 1), imshow(f), title('原图');
 f = adapthisteq(f);
 subplot(1, 3, 2), imshow(f), title('增强后');
 subplot(1, 3, 3), imshow(im2bw(f, graythresh(f))), title('二值化');

这里写图片描述

这下子清晰了很多,但是呢,我们是不是还录用了一些没有必要的干扰信息,我们现在就去除干扰信息。

对比度拉伸(归一化处理)

图像归一化是指对图像进行了一系列标准的处理变换,使之变换为一固定标准形式的过程,该标准图像称作归一化图像。

f = imread('答题卡.jpg');
f = rgb2gray(f);
subplot(1, 3, 1), imshow(f), title('原图');
f = adapthisteq(f); % 对比度增强
Low_High = stretchlim(f, [0.0 0.3]);
f= imadjust(f, [0.0 0.25], [ ]);
subplot(1, 3, 2), imshow(f), title('归一化处理');
subplot(1, 3, 3), imshow(im2bw(f, graythresh(f))), title('二值化');

这里写图片描述

是不是加强了很多,接着,我们继续用中值滤波进行平滑处理。

f = imread('答题卡.jpg');
f = rgb2gray(f);
subplot(1, 3, 1), imshow(f), title('原图');
f = adapthisteq(f); % 对比度增强
Low_High = stretchlim(f, [0.0 0.3]);
f= imadjust(f, Low_High, [ ]);
f = medfilt2(f, [7 5]);
subplot(1, 3, 2), imshow(f), title('平滑处理');
f = im2bw(f, graythresh(f));
subplot(1, 3, 3), imshow(f), title('二值化');

这里写图片描述

发现还是有一些小的点, 我们只需要进行开闭操作即可。

f = imread('答题卡.jpg');
f = rgb2gray(f);
subplot(1, 3, 1), imshow(f), title('原图');
f = adapthisteq(f); % 对比度增强
Low_High = stretchlim(f, [0.0 0.3]);
f= imadjust(f, Low_High, [ ]);
f = medfilt2(f, [7 5]);
f = im2bw(f, graythresh(f));
subplot(1, 3, 2), imshow(f), title('开闭前');
f = imopen(f, strel('square', 4));
f = imclose(f, strel('square', 4));
subplot(1, 3, 3), imshow(f), title('开闭后');

这里写图片描述

可以看到,一些小点点都消失了,接着,我们发现有的小方块太小了,那怎么办?只需要进行腐蚀操作即可。

f = imread('答题卡.jpg');
f = rgb2gray(f);
subplot(1, 3, 1), imshow(f), title('原图');
f = adapthisteq(f); % 对比度增强
Low_High = stretchlim(f, [0.0 0.3]);
f= imadjust(f, Low_High, [ ]);
f = medfilt2(f, [7 5]);
f = im2bw(f, graythresh(f));
f = imopen(f, strel('square', 4));
f = imclose(f, strel('square', 4));
subplot(1, 3, 2), imshow(imcomplement(f)), title('腐蚀前');
f = imerode(f, strel('square', 4));
subplot(1, 3, 3), imshow(imcomplement(f)), title('腐蚀后');

这里写图片描述

怎么样,是不是很霸气- -,其中imcomplement是获得图像的负片的函数,就是1变成0,0变成1。

现在图像预处理完成了,接下来要干什么呢?就是要进行判断,分析!

先将其变为线的形式

f = imread('答题卡.jpg');
f = rgb2gray(f);
f = adapthisteq(f); % 对比度增强
Low_High = stretchlim(f, [0.0 0.3]);
f= imadjust(f, Low_High, [ ]);
f = medfilt2(f, [7 5]);
f = im2bw(f, graythresh(f));
f = imopen(f, strel('square', 4));
f = imclose(f, strel('square', 4));
f = imerode(f, strel('square', 4));
subplot(1, 2, 1), imshow(imcomplement(f)), title('画线前');
f = edge(f, 'canny', [0.04, 0.10], 1.5 , 'vertical');
subplot(1, 2, 2), imshow(f), title('画线后');

这里写图片描述

再找到最长的基准线

f = imread('答题卡.jpg');
f = rgb2gray(f);
f = adapthisteq(f); % 对比度增强
Low_High = stretchlim(f, [0.0 0.3]);
f= imadjust(f, Low_High, [ ]);
f = medfilt2(f, [7 5]);
f = im2bw(f, graythresh(f));
f = imopen(f, strel('square', 4));
f = imclose(f, strel('square', 4));
f = imerode(f, strel('square', 4));
f = edge(f, 'canny', [0.04, 0.10], 1.5 , 'vertical');
[h t r] = hough(f);
peaks = houghpeaks(h, 5);
lines = houghlines(f, t, r, peaks, 'FillGap', 50, 'MinLength', 7);
imshow(f), hold on;
len = 0;
for i = 1 : length(lines)
    tLen = norm(lines(i).point1 - lines(i).point2);
    if len < tLen
        len = tLen
        xy = [lines(i).point1; lines(i).point2];
    end
end
plot(xy(:, 1), xy(:, 2), 'LineWidth', 4, 'Color', 'red');

这里写图片描述

不过可以看出来,这个有点歪,我们需要对他进行校正

f = imread('答题卡.jpg');
f = rgb2gray(f);
f = adapthisteq(f); % 对比度增强
Low_High = stretchlim(f, [0.0 0.3]);
f= imadjust(f, Low_High, [ ]);
f = medfilt2(f, [7 5]);
f = im2bw(f, graythresh(f));
f = imopen(f, strel('square', 4));
f = imclose(f, strel('square', 4));
f = imerode(f, strel('square', 4));
f = edge(f, 'canny', [0.04, 0.10], 1.5 , 'vertical');
flag = true;
while flag
    figure;
    [h t r] = hough(f);
    peaks = houghpeaks(h, 5);
    lines = houghlines(f, t, r, peaks, 'FillGap', 50, 'MinLength', 7);
    subplot(1, 2, 1), imshow(f), title('校正前');
    len = 0;
    for i = 1 : length(lines)
        tLen = norm(lines(i).point1 - lines(i).point2);
        if len < tLen
            len = tLen;
            xy = [lines(i).point1; lines(i).point2];
        end
    end
    x1 = xy(:, 1);
    y1 = xy(:, 2);
    K1 = (x1(2)-x1(1))/(y1(2)-y1(1));
    angle = atan(K1)*180/pi;
    s = size(f);
    if y1(1) <= s(2) / 2 && y1(2) <= s(2) / 2&& abs(angle) > 75
        f= imrotate(f, angle, 'bilinear');
        flag = true;
    else
        f= imrotate(f, -angle, 'bilinear');
        flag = false;
    end
    subplot(1, 2, 2), imshow(f), title('校正后');
end

第一次校正
这里写图片描述
第二次校正
这里写图片描述

现在,我们的答题卡已经校正完毕,接着,我们需要将答题卡分割为信息部分和答题部分

f = imread('答题卡.jpg');
f = rgb2gray(f);
f = adapthisteq(f); % 对比度增强
Low_High = stretchlim(f, [0.0 0.3]);
f= imadjust(f, Low_High, [ ]);
f = medfilt2(f, [7 5]);
f = im2bw(f, graythresh(f));
f = imopen(f, strel('square', 4));
f = imclose(f, strel('square', 4));
f = imerode(f, strel('square', 4));
f = edge(f, 'canny', [0.04, 0.10], 1.5 , 'vertical');
flag = true;
while flag
    [h t r] = hough(f);
    peaks = houghpeaks(h, 5);
    lines = houghlines(f, t, r, peaks, 'FillGap', 50, 'MinLength', 7);
    len = 0;
    for i = 1 : length(lines)
        tLen = norm(lines(i).point1 - lines(i).point2);
        if len < tLen
            len = tLen;
            xy = [lines(i).point1; lines(i).point2];
        end
    end
    x1 = xy(:, 1);
    y1 = xy(:, 2);
    K1 = (x1(2)-x1(1))/(y1(2)-y1(1));
    angle = atan(K1)*180/pi;
    s = size(f);
    if y1(1) <= s(2) / 2 && y1(2) <= s(2) / 2&& abs(angle) > 75
        f= imrotate(f, angle, 'bilinear');
        flag = true;
    else
        f= imrotate(f, -angle, 'bilinear');
        flag = false;
    end
end
% 再进行画线,画出三条基准线
[h t r] = hough(f);
peaks = houghpeaks(h, 5);
lines = houghlines(f, t, r, peaks, 'FillGap', 50, 'MinLength', 7);
figure, imshow(f), title('分割前'), hold on;
for i = 1 : length(lines)
    xy = [lines(i).point1; lines(i).point2];
    if abs(lines(i).point1(2) - lines(i).point2(2)) <= 10
        len(i) = norm(lines(i).point1 - lines(i).point2);
    else
        len(i) = -1;
    end
    plot(xy(:, 1), xy(:, 2), 'LineWidth', 4, 'Color', 'red');
    plot(lines(i).point1(1), lines(i).point1(2), 'bo');
    plot(lines(i).point2(1), lines(i).point2(2), 'yo');
    xyD{i} = xy;
end
[len, ind] = sort(len(:), 'descend');
for i = 1 : length(ind)
    xyN{i} = xyD{ind(i)};
end
up = f;
down = f;
u = xyN{1};
d = xyN{2};
if u(1, 2) > d(1, 2)
    t = u;
    u = d;
    d = t;
end
up(u(1, 2) : 1 : d(1, 2), :) = 0;
down(1 : u(1, 2), :) = 0;
figure, imshow(up), title('信息部分');
figure, imshow(down), title('答题部分');

这里写图片描述
这里写图片描述
这里写图片描述

分割完了以后,让我们来获取信息。

f = imread('答题卡.jpg');
rg = f;
f = rgb2gray(f);
f = adapthisteq(f); % 对比度增强
Low_High = stretchlim(f, [0.0 0.3]);
f= imadjust(f, Low_High , [ ]);
f = medfilt2(f, [7 5]);
f = im2bw(f, graythresh(f));
f = imopen(f, strel('square', 4));
f = imclose(f, strel('square', 4));
f = imerode(f, strel('square', 4));
f = edge(f, 'canny', [0.04, 0.10], 1.5 , 'vertical');
flag = true;
while flag
    [h t r] = hough(f);
    peaks = houghpeaks(h, 5);
    lines = houghlines(f, t, r, peaks, 'FillGap', 50, 'MinLength', 7);
    len = 0;
    for i = 1 : length(lines)
        tLen = norm(lines(i).point1 - lines(i).point2);
        if len < tLen
            len = tLen;
            xy = [lines(i).point1; lines(i).point2];
        end
    end
    x1 = xy(:, 1);
    y1 = xy(:, 2);
    K1 = (x1(2)-x1(1))/(y1(2)-y1(1));
    right = x1(2);
    angle = atan(K1)*180/pi;
    s = size(f);
    if y1(1) <= s(2) / 2 && y1(2) <= s(2) / 2&& abs(angle) > 75
        f = imrotate(f, angle, 'bilinear');
        rg = imrotate(rg, angle, 'bilinear');
        flag = true;
    else
        f= imrotate(f, -angle, 'bilinear');
        rg = imrotate(rg, -angle, 'bilinear');
        flag = false;
    end
end
down = f;
% 再进行画线,画出三条基准线
[h t r] = hough(f);
peaks = houghpeaks(h, 5);
lines = houghlines(f, t, r, peaks, 'FillGap', 50, 'MinLength', 7);
for i = 1 : length(lines)
    xy = [lines(i).point1; lines(i).point2];
    if abs(lines(i).point1(2) - lines(i).point2(2)) <= 10
        len(i) = norm(lines(i).point1 - lines(i).point2);
    else
        len(i) = -1;
    end
    xyD{i} = xy;
end
[len, ind] = sort(len(:), 'descend');
for i = 1 : length(ind)
    xyN{i} = xyD{ind(i)};
end
u = xyN{1};
d = xyN{2};
if u(1, 2) > d(1, 2)
    t = u;
    u = d;
    d = t;
end
% 下半部分处理

subplot(1, 2, 1), imshow(rg), title('原图'), hold on;

l = d(1, 2) - u(1, 2);
l = l / 31;
for i = 1 : 31
    plot([0 1000], [u(1, 2) + l * (i - 1), u(1, 2) + l * (i - 1)], 'LineWidth', 1, 'Color', 'red');
end

l = right - d(1, 1);
l = l / 26;
for i = 1 : 26
    plot([d(1, 1) + l * (i - 1), d(1, 1) + l * (i - 1)], [u(1, 2) d(1, 2)], 'LineWidth', 1, 'Color', 'red');
end

down(1 : u(1, 2), :) = 0;
down = imdilate(down, strel('square', 3));
down = imfill(down, 'holes');
subplot(1, 2, 2), imshow(down), title('处理图'), hold on;

l = d(1, 2) - u(1, 2);
l = l / 31;
for i = 1 : 31
    plot([0 1000], [u(1, 2) + l * (i - 1), u(1, 2) + l * (i - 1)], 'LineWidth', 1, 'Color', 'red');
end

l = right - d(1, 1);
l = l / 27;
for i = 1 : 27
    plot([d(1, 1) + l * (i - 1), d(1, 1) + l * (i - 1)], [u(1, 2) d(1, 2)], 'LineWidth', 1, 'Color', 'red');
end

这里写图片描述

到这里就差不多了,只需要判断对应方格内是否为1即可。

下面是另外一张答题卡的图像

这里写图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值