MATLAB——基於影像相減的紙牌識別系統

Jesy_Hsu發表於2021-01-05

一、設計要求

這是數字影像處理課程作業的其中一道題目,設計要求如下。
紙牌識別系統的設計要求
所用的資料集是我舍友在網上下載的,個人覺得很高清,也懶得去拍照,就直接白嫖了,自己做測試的話,可以自行取材,注意拍攝的大小合適、位置恰當、光照均勻;如果出現光照不均,識別結果可能會偏離更多,需要提前消除光照不均的影響。
在這裡插入圖片描述

二、程式設計思路

Created with Raphaël 2.2.0 源資料集 獲取模板 輸入測試影像 將模板分別於測試影像相減,取絕對值 確定與輸入影像差值最小的模板 輸出識別結果 識別完成

我的檔案管理如下:
在這裡插入圖片描述

1、獲取模板(含程式碼)

執行該程式之後,將獲得差不多大小(大小會有幾個畫素的差別)的模板,存放在同根目錄下的mask資料夾下,將所需要的數字和型別包含在待識別範圍內,減小識別量。

%-------------GetImagesMask.m------------------------
clear;clc;
%用於獲取模板
num = ['2' '3' '4' '5' '6' '7' '8' '9' '10'];
alpha = ['B' 'F' 'M' 'R'];
N = 1;
Al = 3;
for i=1:4
    path=strcat('../images/',alpha(i),'_',num(N),'.JPG');
    name = strcat(alpha(i),'_',num(N));
    A = imread(path);
    [x,y,z] = size(A);
    Shape_Left_Top = A(x/6 : 7*x/24, y/25 : y/7, 1 : z);
    % 影像二值化,反色
    Shape_Left_Top_BW = ~im2bw(rgb2gray(Shape_Left_Top),0.5);
    % 中值濾波去除椒鹽噪聲,[3,3]為視窗大小,不同噪聲用不同的濾波器
    mask_A = medfilt2(Shape_Left_Top_BW,[3,3]);
    restore_path_A = strcat('../mask/',alpha(i),'.JPG');
    imwrite(mask_A,restore_path_A); %將模板存放在mask資料夾中
end
for i = 1:9
    if i==9
        path=strcat('../images/',alpha(Al),'_',num(i),num(i+1),'.JPG');
        name = strcat(alpha(Al),'_',num(i),num(i+1));
    else
        path=strcat('../images/',alpha(Al),'_',num(i),'.JPG');
        name = strcat(alpha(Al),'_',num(i));
    end
    A = imread(path);
    [x,y,z] = size(A);
    Number_Left_Top = A(x/25 : x/6, y/30: y/7, 1 : z);  %自己確定的大小,可更改
     % 影像二值化,反色
    Number_Left_Top_BW = ~im2bw(rgb2gray(Number_Left_Top),0.5);
    % 中值濾波去除椒鹽噪聲,[3,3]為視窗大小
    mask_N = medfilt2(Number_Left_Top_BW,[3,3]);
    if i==9
        restore_path_N = strcat('../mask/',num(i),num(i+1),'.JPG');
    else
        restore_path_N = strcat('../mask/',num(i),'.JPG');
    end
    imwrite(mask_N,restore_path_N);
end

獲得的模板如下:
在這裡插入圖片描述

2、輸入影像測試(含程式碼)

需要注意的是,將輸入影像剪裁後的大小不一定與模板大小一致,所以要做預處理,選擇模板與擷取的輸入影像中較小的一方的大小,然後擷取另外一方為合適的大小。

%------------------PuKeRecog.m---------------------
clear;clc;
%輸入影像
A = imread('../images/M_6.JPG');
[x,y,z] = size(A);
% 擷取影像
Number_Left_Top = A(x/25 : x/6, y/30: y/7, 1 : z);
Shape_Left_Top =A(x/6 : 7*x/24, y/25 : y/7, 1 : z);
figure;
imshow(A)
% figure(1)
% imshow(Number_Left_Top)
% figure(2)
% imshow(Shape_Left_Top)
% 影像二值化,反色
Shape_Left_Top_BW = ~im2bw(rgb2gray(Shape_Left_Top),0.5);
Number_Left_Top_BW = ~im2bw(rgb2gray(Number_Left_Top),0.5);
% 中值濾波去除椒鹽噪聲,[3,3]為視窗大小
Shape_Left_Top_BW = medfilt2(Shape_Left_Top_BW,[3,3]);
Number_Left_Top_BW = medfilt2(Number_Left_Top_BW,[3,3]);
%模板引數
num = ['2' '3' '4' '5' '6' '7' '8' '9' '10'];
alpha = ['B' 'F' 'M' 'R'];
%分別與模板型別影像做減操作
diff_A = [0 0 0 0]; %用於儲存與四個型別模板相減的差值
for i=1:4
    path=strcat('../mask/',alpha(i),'.JPG');
    mask_A = imread(path);
    mask_A =~ mask_A;
    mask_A =~ mask_A;
    [h_m,w_m] = size(mask_A);
    [h_a,w_a] = size(Shape_Left_Top_BW);
    if h_m<h_a
        H=h_m;
    else
        H=h_a;
    end
    if w_m<w_a
        W=w_m;
    else
        W=w_a;
    end
    DD=zeros(H,W);
    mask_A=double(mask_A);
    for j=1:H
        for k=1:W
            DD(j,k) = Shape_Left_Top_BW(j,k) - mask_A(j,k);
        end
    end
    diff_A(i) = sum(sum(abs(DD)));
end
[minVal_a minInd_a] = min(diff_A);
switch minInd_a
    case 1
        name=strcat('the kind is:',alpha(1));
        disp(name)
    case 2
         name=strcat('the kind is:',alpha(2));
        disp(name)
    case 3
         name=strcat('the kind is:',alpha(3));
        disp(name)
    case 4
         name=strcat('the kind is:',alpha(4));
        disp(name)
    otherwise
        disp('No found!')
end

%與模板數字影像做減操作
diff_N = [0 0 0 0 0 0 0 0 0]; %用於儲存與九個數字模板相減的差值
for i=1:9
    if i==9
        path=strcat('../mask/',num(i),num(i+1),'.JPG');
    else
        path=strcat('../mask/',num(i),'.JPG');
    end
    mask_N = imread(path);
    mask_N =~ mask_N;
    mask_N =~ mask_N;
    [h_m,w_m] = size(mask_N);
    [h_a,w_a] = size(Number_Left_Top_BW);
    if h_m<h_a
        H=h_m;
    else
        H=h_a;
    end
    if w_m<w_a
        W=w_m;
    else
        W=w_a;
    end
    DD=zeros(H,W);
    mask_N=double(mask_N);
    for j=1:H
        for k=1:W
            DD(j,k) = Number_Left_Top_BW(j,k) - mask_N(j,k);
        end
    end
    diff_N(i) = sum(sum(abs(DD)));
end
[minVal_n minInd_n] = min(diff_N);
switch minInd_n
    case 1
        name=strcat('the number is:',num(1));
        disp(name)
    case 2
         name=strcat('the number is: ',num(2));
        disp(name)
    case 3
         name=strcat('the number is :',num(3));
        disp(name)
    case 4
         name=strcat('the number is :',num(4));
        disp(name)
    case 5
         name=strcat('the number is :',num(5));
        disp(name)
    case 6
         name=strcat('the number is :',num(6));
        disp(name)
    case 7
         name=strcat('the number is :',num(7));
        disp(name)
    case 8
         name=strcat('the number is :',num(8));
        disp(name)
    case 9
         name=strcat('the number is :',num(9),num(10));
        disp(name)
    otherwise
        disp('No found!')
end

三、測試結果

測試1
輸入為:
在這裡插入圖片描述
輸出為:
在這裡插入圖片描述
測試2
輸入為:
在這裡插入圖片描述
輸出為:
在這裡插入圖片描述
測試3
輸入為:
在這裡插入圖片描述
輸出為:
在這裡插入圖片描述
測試4
輸入為:
在這裡插入圖片描述
輸出為:
在這裡插入圖片描述

四、效能分析

這種相減作差的識別方法優點是模板的形狀一致,但對所拍攝圖片的位置有很嚴格的要求,若是模板選擇正確,拍攝紙牌的位置與模板一致,準確率是可以很高的,比手寫數字識別還要高很多(手寫數字的形狀也不唯一,因此準確率很低。)但如果位置一旦出現偏差,那麼識別錯誤的概率就會大大提高。
如下,型別識別錯誤,數字識別正確:
在這裡插入圖片描述
在這裡插入圖片描述

五、另外一道程式設計題(沒錯,就是手寫數字識別)

1、影像相減方法

利用同樣的影像相減方法,那識別正確率是相當的低呀!(雖然早就預料到了:),這次用的是Python,裡面有些程式碼是借鑑網上大佬的,我很想找出處標明,但我發現我找不到了,害。

Step1:採集手寫數字影像資料

在這裡插入圖片描述

Step2:執行MNIST_Pre.py程式對資料進行處理,變成二值化影像

在這裡插入圖片描述

Step3:執行MNIST_Pro.py程式,將輸入圖片分別與各個模板圖片進行減操作,計算剩餘有灰度值的畫素點數,選取最小值,進而對手寫數字進行識別,輸出結果

在這裡插入圖片描述

Step4:測試

在這裡插入圖片描述
在這裡插入圖片描述

Step5:效能分析

通過模板相減的辦法進行識別,對模板要求很高,模板的位置、形狀等不同,對同一圖片的識別效果也會不同。加上手寫數字的變化性很大,很難尋找到統一不變的特徵。該模型識別率很低。

程式程式碼

#-------------------------------------MNIST_Pre.py----------------------------------------------
import cv2
global img
global point1,point2
def on_mouse(event,x,y,flags,param):
    global img,point1,point2
    img2 = img.copy()
    if event == cv2.EVENT_LBUTTONDOWN:
        point1 = (x,y)
        cv2.circle(img2,point1,10,(0,255,0),5)
        cv2.imshow('image',img2)
    elif event == cv2.EVENT_MOUSEMOVE and (flags & cv2.EVENT_FLAG_LBUTTON): #按住左鍵拖拽
        cv2.rectangle(img2,point1,(x,y),(255,0,0),5) #影像,矩形頂點,相對頂點,顏色,粗細
        cv2.imshow('image',img2)
    elif event == cv2.EVENT_LBUTTONUP: #左鍵釋放
        point2 = (x,y)
        cv2.rectangle(img2,point1,point2,(0,0,255),5)
        cv2.imshow('image',img2)
        min_x = min(point1[0],point2[0])
        min_y = min(point1[1],point2[1])
        width = abs(point1[0]-point2[0])
        height = abs(point1[1]-point2[1])
        cut_img = img[min_y:min_y + height,min_x:min_x + width]
        resize_img = cv2.resize(cut_img,(28,28)) #調整影像尺寸為28*28
        ret,thresh_img = cv2.threshold(resize_img,127,255,cv2.THRESH_BINARY) #二值化
        cv2.imshow('result',thresh_img)
        cv2.imwrite('Images/05.png',thresh_img)  #存放位置

def main():
    global img
    img = cv2.imread(r'C:\\Users\\LENOVO\\Desktop\\test.jpg')
    img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    cv2.namedWindow('image')
    cv2.setMouseCallback('image',on_mouse)
    cv2.imshow('image',img)
    cv2.waitKey(0)

if __name__ == '__main__':
    main()

#----------------------------------------MNIST_Pro.py---------------------------------------
import cv2
import numpy as np
# # 畫素取反
# def get_img_reserve(img):
# #直接呼叫反選函式
#     dst = cv2.bitwise_not(img)
#     cv2.imshow("reserve",dst)
#     cv2.imshow('src',img)
#     return dst
#
# img = cv2.imread('Images/0.png')
# dst = get_img_reserve(img)
# kernel = np.ones((3,3),np.uint8)
# erosion = cv2.erode(dst,kernel,iterations = 1)
# cv2.imwrite('Images/pre_0.png',erosion)
# cv2.imshow('erode',erosion)
# cv2.waitKey()

def findSmallest(arr):
    smallest = arr[0]
    smallest_index = 0
    for i in range(1,len(arr)):
        if arr[i] < smallest:
            smallest = arr[i]
            smallest_index = i
    return smallest_index

counts = np.arange(10)
#print(counts)
for t in range(10):
    #print('-------------------{} processing-------------------'.format(t))  #模板圖片
    path = 'Images/1'+str(t)+'.png'
    mask = cv2.imread(path)
    img = cv2.imread('Images/6.png')  #輸入測試圖片
    differ = img - mask
    # print(differ)
    # (h,w,d)=differ.shape
    # for i in range(w):
    #     for j in range(h):
    #         for k in range(d):
    #             print(differ[i][j][k])
    differ = abs(differ)
    differ_gray = cv2.cvtColor(differ,cv2.COLOR_BGR2GRAY)
    # (h,w)=differ_gray.shape
    # for i in range(w):
    #     for j in range(h):
    #             print(differ[i][j])
    ret,thresh = cv2.threshold(differ_gray,0,255,cv2.THRESH_BINARY)
    cv2.imwrite('Images/differ'+str(t)+'.png',thresh)
    #cv2.imshow('differ',thresh)
    (h, w) = thresh.shape
    #print("width={}, height={}".format(w, h))
    count = 0
    for i in range(w):
        for j in range(h):
            if thresh[i][j] == 255:
                count = count + 1
    counts[t] = count
    #print('count'+str(t)+'= ',count)

min = findSmallest(counts)
print('the number is ',min)
cv2.imshow('Writing Number',img)
cv2.waitKey()

2、基於TensorFlow的手寫數字識別系統

之前學習TensorFlow的時候,跟著大佬打過的程式碼,還是深度學習香,準確率也高,po這裡,一起學習。

(1)步驟

Step1:載入TensorFlow裡的mnist資料;
Step2:訓練網路,利用梯度下降演算法訓練模型,利用AdamOptimizer優化器提高精確度;
Step3:載入自己的影像測試。

(2)測試

輸入影像:
在這裡插入圖片描述
處理之後的影像:
在這裡插入圖片描述
測試結果:
在這裡插入圖片描述

(3)模型效能

在這裡插入圖片描述

(4)程式程式碼

#我的TensorFlow版本是2,為了使用某些功能,將其表現為版本1,用的編輯器是jupyter
import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()
from tensorflow.examples.tutorials.mnist import input_data
#載入資料集
mnist = input_data.read_data_sets('MNIST_data',one_hot=True)

#每個資料集大小
batch_size = 30
#計算一共有多少個資料集
n_batch = mnist.train.num_examples // batch_size

#定義兩個placeholder
x = tf.placeholder(tf.float32,[None,784])
y = tf.placeholder(tf.float32,[None,10])

#定義神經網路中間層
Weights_L1 = tf.Variable(tf.zeros([784,30]))
biases_L1 = tf.Variable(tf.zeros([30]))
Wx_plus_b_L1 = tf.matmul(x,Weights_L1) + biases_L1
L1 = tf.nn.tanh(Wx_plus_b_L1)

#建立一個簡單的神經網路
W = tf.Variable(tf.random_normal([30,10]))
b = tf.Variable(tf.zeros([10]))
prediction = tf.nn.softmax(tf.matmul(L1,W)+b)

#二次代價函式
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y,logits=prediction))
#使用梯度下降法
#train_step = tf.train.GradientDescentOptimizer(0.3).minimize(loss)
train_step = tf.train.AdamOptimizer(1e-3).minimize(loss)

#初始化變數
init = tf.global_variables_initializer()

#結果存放在一個布林型列表中
correct_prediction = tf.equal(tf.argmax(y,1),tf.argmax(prediction,1)) #argmax返回一維張量中最大的值所在的位置
#求準確率
accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))

with tf.Session() as sess:
    sess.run(init)
    for epoch in range(21):
        for batch in range(n_batch):
            batch_xs,batch_ys = mnist.train.next_batch(batch_size)
            sess.run(train_step,feed_dict={x:batch_xs,y:batch_ys})
            
        acc = sess.run(accuracy,feed_dict={x:mnist.test.images,y:mnist.test.labels})
        print('Iter' + str(epoch) + ",Testing Accuracy" + str(acc))

#影像預處理
import cv2
global img
global point1,point2
def on_mouse(event,x,y,flags,param):
    global img,point1,point2
    img2 = img.copy()
    if event == cv2.EVENT_LBUTTONDOWN:
        point1 = (x,y)
        cv2.circle(img2,point1,10,(0,255,0),5)
        cv2.imshow('image',img2)
    elif event == cv2.EVENT_MOUSEMOVE and (flags & cv2.EVENT_FLAG_LBUTTON): #按住左鍵拖拽
        cv2.rectangle(img2,point1,(x,y),(255,0,0),5) #影像,矩形頂點,相對頂點,顏色,粗細
        cv2.imshow('image',img2)
    elif event == cv2.EVENT_LBUTTONUP: #左鍵釋放
        point2 = (x,y)
        cv2.rectangle(img2,point1,point2,(0,0,255),5)
        cv2.imshow('image',img2)
        min_x = min(point1[0],point2[0])
        min_y = min(point1[1],point2[1])
        width = abs(point1[0]-point2[0])
        height = abs(point1[1]-point2[1])
        cut_img = img[min_y:min_y + height,min_x:min_x + width]
        resize_img = cv2.resize(cut_img,(28,28)) #調整影像尺寸為28*28
        ret,thresh_img = cv2.threshold(resize_img,127,255,cv2.THRESH_BINARY) #二值化
        cv2.imshow('result',thresh_img)
        cv2.imwrite('images/test.png',thresh_img)  #存放位置

def main():
    global img
    img = cv2.imread(r'C:\Users\LENOVO\Desktop\test.jpg')
    img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    cv2.namedWindow('image')
    cv2.setMouseCallback('image',on_mouse)
    cv2.imshow('image',img)
    cv2.waitKey(0)

if __name__ == '__main__':
main()

#利用訓練好的模型進行測試
from PIL import Image
import tensorflow as tf
import numpy as np

im = Image.open('images/test.png')
data = list(im.getdata())
result = [(255-x)*1.0/255.0 for x in data] 
print(result)

# 為輸入影像和目標輸出類別建立節點
x = tf.placeholder("float", shape=[None, 784]) # 訓練所需資料  佔位符

# *************** 構建多層卷積網路 *************** #
def weight_variable(shape):
  initial = tf.truncated_normal(shape, stddev=0.1) # 取隨機值,符合均值為0,標準差stddev為0.1
  return tf.Variable(initial)

def bias_variable(shape):
  initial = tf.constant(0.1, shape=shape)
  return tf.Variable(initial)

def conv2d(x, W):
  return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

def max_pool_2x2(x):
  return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')

x_image = tf.reshape(x, [-1,28,28,1]) # -1表示任意數量的樣本數,大小為28x28,深度為1的張量

W_conv1 = weight_variable([5, 5, 1, 32]) # 卷積在每個5x5的patch中算出32個特徵。
b_conv1 = bias_variable([32])
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) 
h_pool1 = max_pool_2x2(h_conv1)

W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)

W_fc1 = weight_variable([7 * 7 * 64, 1024])
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)

# 在輸出層之前加入dropout以減少過擬合
keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

# 全連線層
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])

# 輸出層
# tf.nn.softmax()將神經網路的輸層變成一個概率分佈
y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

saver = tf.train.Saver() # 定義saver

# *************** 開始識別 *************** #
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    saver.restore(sess, "save/model.ckpt")#這裡使用了之前儲存的模型引數

    prediction = tf.argmax(y_conv,1)
    predint = prediction.eval(feed_dict={x: [result],keep_prob: 1.0}, session=sess)

    print("recognize result: %d" %predint[0])

555數圖作業讓我沒有頭髮
如需轉載,請標明出處,xixixi。

相關文章