想讓自己輕松點就要讓計算機多為你做點!
前幾天一個朋友找到我讓我做一個網上閱卷系統,就是實現這么幾個功能:高速掃描儀掃描試卷后得到一張一張的圖片,軟件的功能就是處理圖片,計算成績。再詳細點就是自動識別考生塗的學號,自動識別考生的選擇題答案並記錄,后面的大題要分塊,把每一個題從試卷中分離出來,轉發給老師進行閱卷。最后就是實現成績的匯總與分析。
說起來功能也不算復雜,實現起來細節性的東西還是挺多的,比如每一科的試卷都不相同,要做一個通用型的軟件,適合各種類型的試卷,還有就是服務器的設置呀,學生信息的保密呀,各種錯誤的處理啊……反正挺麻煩的,都怪我知識不夠。
下面說說識別的方法:這實際上就是一個概率算法,只要是被塗了的方格就是黑色的,沒有塗的就只有紅色或淺色的數字還有外面的框,如下圖,這樣的話我們就能根據方格的顏色來判斷是不是被塗了。一個方格里面有很多點,我們通過取每個點的顏色,得到相應的RGB顏色分量,由於黑色是RGB(0,0,0),白色是RGB(255,255,255),其他的顏色就介於兩者之間了,我是用RGB三個分量直接求和來判斷這個點是不是被塗黑了,當然要有一個判斷標准,我的標准是三個數的和小於30就被認為是黑色。當然這個數字可以變,根據掃描出來的圖片質量來看了,如果質量不是很好,有點模糊的話,就應該比30大了。這樣的話我們只是得到一個點,要判斷整個方格的話就要把里面的所有點都判斷一次,只要兩個for循環就可以得到所有的點,然后我們要做好相應的統計,假設方格里面總共有total個點,黑色的點有count個,這樣我們count/total就會得到一個概率,我假設概率大於0.5也就是有一半的點是黑色的我認為這個點是被塗過了,當然這也是一個可變參數,根據實際情況來確定吧。既然這個點被塗過了,我們就能得到這個點的位置,然后就可以推斷出這個位置對應的數字或者選擇題的ABCD,把所有的數字組合起來就是學號了,把所有的ABCD組合起來就是答案了,然后我們就可以和標准答案進行比較,得出結果。
軟件運行截圖(可以正確識別學號):
程序源碼:
/*
功能:網上閱卷系統的識別
作者:ma6174
郵箱:ma6174@163.com
時間:2012年2月23日
其他:包含的頭文件<graphics.h>來則easyx的圖形庫
*/
#include<stdio.h>
#include<graphics.h>
#include<stdlib.h>
#include< string.h>
#include<conio.h>
#include<windows.h>
// 定義方格的大小和方格的間距,根據實際情況做相應的修改
#define BOX_X 42
#define BOX_Y 13
#define BLANK_X 18
#define BLANK_Y 25
// 顏色分量類
class rgb
{
public:
BYTE red;
BYTE green;
BYTE blue;
};
// 判斷一個點是不是黑色
bool judge_black(rgb color)
{
if(color.blue+color.green+color.red< 30) // 可能會有點誤差,根據實際情況調整
return true; // 黑色,有標記
return false;
}
// 判斷一個方格有沒有被填塗
bool judge_box( int x, int y,HDC hdc)
{
int i,j,total= 0,count= 0;
COLORREF color;
for(i=x+ 2;i<x+BOX_X- 2;i++) // 循環判斷所有點
{
for(j=y+ 1;j<y+BOX_Y- 1;j++)
{
total++;
color=GetPixel(hdc,i,j);
rgb temp;
temp.red=GetRValue(color);
temp.green=GetGValue(color);
temp.blue=GetBValue(color);
if(judge_black(temp)== true)
count++;
}
}
if( double(count)/total> 0.5) // 黑色的比例大於50%則認為該方格被填塗
return true;
return false;
}
// 判斷所有的,入口參數分別是起始橫坐標,起始縱坐標,行數,列數,窗口句柄
void judge_all( int start_x, int start_y, int hangshu, int lieshu,HDC hdc)
{
int i,j;
for(i= 0;i<hangshu;i++)
{
for(j= 0;j<lieshu;j++)
{
// Sleep(50);
// circle(start_x+i*(BOX_X+BLANK_X),start_y+j*(BOX_Y+BLANK_Y),10);
if(judge_box(start_x+i*(BOX_X+BLANK_X),start_y+j*(BOX_Y+BLANK_Y),hdc)== true)
{
printf( " %d ",j);
// outtextxy(start_x+i*(BOX_X+BLANK_X),start_y+j*(BOX_Y+BLANK_Y),"A");
}
}
// printf("\n");
}
}
int main()
{
int start_x,start_y,total= 0,count= 0;
initgraph( 1440, 900);
cleardevice();
IMAGE img;
loadimage(NULL, " d:\\1.jpg ");
HDC hdc = GetImageHDC();
MOUSEMSG m;
while( 1) // 通過鼠標點擊得到起始位置
{
m=GetMouseMsg();
if(m.uMsg==WM_LBUTTONDOWN)
{
start_x=m.x;
start_y=m.y;
break;
}
}
judge_all(start_x,start_y, 18, 10,hdc);
printf( " \n ");
getch();
}
作者:ma6174
郵箱:ma6174@163.com
時間:2012年2月23日
其他:包含的頭文件<graphics.h>來則easyx的圖形庫
*/
#include<stdio.h>
#include<graphics.h>
#include<stdlib.h>
#include< string.h>
#include<conio.h>
#include<windows.h>
// 定義方格的大小和方格的間距,根據實際情況做相應的修改
#define BOX_X 42
#define BOX_Y 13
#define BLANK_X 18
#define BLANK_Y 25
// 顏色分量類
class rgb
{
public:
BYTE red;
BYTE green;
BYTE blue;
};
// 判斷一個點是不是黑色
bool judge_black(rgb color)
{
if(color.blue+color.green+color.red< 30) // 可能會有點誤差,根據實際情況調整
return true; // 黑色,有標記
return false;
}
// 判斷一個方格有沒有被填塗
bool judge_box( int x, int y,HDC hdc)
{
int i,j,total= 0,count= 0;
COLORREF color;
for(i=x+ 2;i<x+BOX_X- 2;i++) // 循環判斷所有點
{
for(j=y+ 1;j<y+BOX_Y- 1;j++)
{
total++;
color=GetPixel(hdc,i,j);
rgb temp;
temp.red=GetRValue(color);
temp.green=GetGValue(color);
temp.blue=GetBValue(color);
if(judge_black(temp)== true)
count++;
}
}
if( double(count)/total> 0.5) // 黑色的比例大於50%則認為該方格被填塗
return true;
return false;
}
// 判斷所有的,入口參數分別是起始橫坐標,起始縱坐標,行數,列數,窗口句柄
void judge_all( int start_x, int start_y, int hangshu, int lieshu,HDC hdc)
{
int i,j;
for(i= 0;i<hangshu;i++)
{
for(j= 0;j<lieshu;j++)
{
// Sleep(50);
// circle(start_x+i*(BOX_X+BLANK_X),start_y+j*(BOX_Y+BLANK_Y),10);
if(judge_box(start_x+i*(BOX_X+BLANK_X),start_y+j*(BOX_Y+BLANK_Y),hdc)== true)
{
printf( " %d ",j);
// outtextxy(start_x+i*(BOX_X+BLANK_X),start_y+j*(BOX_Y+BLANK_Y),"A");
}
}
// printf("\n");
}
}
int main()
{
int start_x,start_y,total= 0,count= 0;
initgraph( 1440, 900);
cleardevice();
IMAGE img;
loadimage(NULL, " d:\\1.jpg ");
HDC hdc = GetImageHDC();
MOUSEMSG m;
while( 1) // 通過鼠標點擊得到起始位置
{
m=GetMouseMsg();
if(m.uMsg==WM_LBUTTONDOWN)
{
start_x=m.x;
start_y=m.y;
break;
}
}
judge_all(start_x,start_y, 18, 10,hdc);
printf( " \n ");
getch();
}
目前只是實現了基本的識別功能。當然如果僅僅是識別只有學號和選擇的答題卡這些功能就夠了。真正使用的話還要進行完善,比如增加選擇題的識別,控制功能等。當然軟件功能也可以進一步擴展,比如自動識別開始位置等。