1. 程式人生 > >計算機視覺小例項 No.3 基於Hough變化的答題卡識別

計算機視覺小例項 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即可。

下面是另外一張答題卡的影象

這裡寫圖片描述