#環境
macOS Cataina
10.15.7
python 3.8.1
fortran 95
スキャナ
canon
ソフト
CaptureOnTouch
読み取り
グレースケール
200dpi
jpeg
3標準
#必要なライブラリ
cv2
$ pip install cv2
#必要なもの
・記入済みマークシート用紙を含むディレクトリ(student_sheet
)
・マーカー(maker.jpg
)←スキャナで読み取った画像から作成
・本プログラム(mark30.sh
)
・主に画像認証用(mark_f30.py
)
・正答率の計算用(rate_windows.f95
)
・空のディレクトリ(answersheet
)
・結果出力用のディレクトリ(answersheet_rate
)
これを同一ディレクトリに入れて実行
$ ./mark30.sh
すると,
結果出力用のディレクトリ(answersheet_rate
)に
答案用紙が作成されます.
#各プログラムソースコード
本プログラム
mark30.sh
#!/bin/sh
for number in {1..9}
do
echo "■■■■■■■■■■■■■■■■■■■■■■■■"
echo "student_"00${number}".jpg"
python mark_f30.py 00${number}
done
for number in {10..45}
do
echo "■■■■■■■■■■■■■■■■■■■■■■■■"
echo "student_"0${number}".jpg"
python mark_f30.py 0${number}
done
#for number in {100..120}
#do
# echo "■■■■■■■■■■■■■■■■■■■■■■■■"
# echo "student_"${number}".jpg"
# python mark_f30.py ${number}
#done
echo "■■■■■■■■■■■■■■■■■■■■■■■■"
echo "■■■■■■■■■■■■■■■■■■■■■■■■"
echo "■■■■■■■■■■■■■■■■■■■■■■■■"
echo "全てのマークシートの読取完了"
ls answersheet > list.txt
gfortran -o rate rate_windows.f95
./rate
画像認証用プログラム
mark_f30.py
import numpy as np
import cv2
#import pandas as pd
import sys
#print("##################")
#正解,配点記入欄#
number = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30] #番号
eanswer = [6, 9, 3, 2, 2, 1, 2, 3, 5, 4, 2, 6, 6, 6, 5, 3, 5, 4, 4, 6, 2, 3, 6, 2, 1, 4, 4, 9, 6, 8] #正解
allo = [2, 3, 3, 3, 2, 3, 3, 3, 4, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3] #配点
#score = 0
T = "1"
F = "0"
E = "20"#マークミスエラー用
####################################################################################
n_col = 11 # マークの列数
n_row = 35 # マークの行数
margin_top = 3 # 上余白行数
margin_bottom = 0 # 下余白行数
n_row_total = n_row + margin_top + margin_bottom # 行数 (マーク行 + 上余白 + 下余白 )
start = 5 #出席番号の分の列#百の位〜解答欄までの列の総計
#グレースケール (mode = 0)でファイルを読み込む
marker=cv2.imread('marker.jpg',0)
#コマンドラインから引数#########
#例 >python mark.py 001#####
args = sys.argv
n = str(args[1])
##########################################
#スキャン画像を読み込む
img = cv2.imread('student_sheet/student_%s.jpg' %n, 0)
#マーカー検出
res = cv2.matchTemplate(img, marker, cv2.TM_CCOEFF_NORMED)
threshold = 0.5#閾値
loc = np.where( res >= threshold)
#cv2.imwrite('resimg.jpg',img)
#マークシート部分の切り抜き
mark_area={}
mark_area['top_x']= min(loc[1])
mark_area['top_y']= min(loc[0])
mark_area['bottom_x']= max(loc[1])
mark_area['bottom_y']= max(loc[0])
img = img[mark_area['top_y']:mark_area['bottom_y'],mark_area['top_x']:mark_area['bottom_x']]
#cv2.imwrite('kiri.jpg',img)
#リサイズ
#Answer = cv2.imread('kiri.jpg', 0)
Answer_Resize = cv2.resize(img, (n_col*100, n_row_total*100))
#cv2.imwrite('kiri.jpg', Answer_Resize)
#ぼかし
Answer_Blur = cv2.GaussianBlur(Answer_Resize, (5,5), 0)
#cv2.imwrite('Answer_Blur.jpg',Answer_Blur)
#2値化
RetVal, Answer_Binarization = cv2.threshold(Answer_Blur, 50, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
#cv2.imwrite('Answer_Binarization.jpg',Answer_Binarization)
#白黒反転
Answer_Reverse = 255 - Answer_Binarization
#cv2.imwrite('Answer_Reverse.jpg',Answer_Reverse)
#行と列に分解して各場所の階調の合計値を計算
Result = []
for row in range(margin_top+1, margin_top+1+n_row):
tmp_Answer = Answer_Reverse [(row-1)*100:row*100,]#
cv2.imwrite("PNG/Answer_Tmp%d.png" % row, tmp_Answer)#
tmp_Answer = Answer_Reverse [(row-1)*100:row*100,]
Area_sum = []
for col in range(n_col):
Area_sum.append(np.sum(tmp_Answer[:,col*100:(col+1)*100]))
Result.append(Area_sum > np.median(255*300))#閾値
#ここから番号の読み取り#########
#組番号の読み取り
cl = np.where(Result[0]==True)[0]#+1
if len(cl)>1:
print("ダブルマーク")
print("読取失敗")
sys.exit( )
elif len(cl)==1:
cla = str(cl[0])
else:
print("ノーマーク")
print("読取失敗")
sys.exit( )
#cla = str(cl[0])
#出席番号の読み取り
num = []
for y in range(1,3):
re = np.where(Result[y]==True)[0]#+1
if len(re)>1:
print("ダブルマーク")
print("読取失敗")
sys.exit( )
elif len(re)==1:
num.append(int(re))
else:
print("ノーマーク")
print("読取失敗")
sys.exit( )
nu = str(num[0])+ str(num[1])
code = str(cla) + str(nu)
print("出席番号%s" % code)#ターミナルに表示
#出力ファイル作成#生徒に配るやつ##################################
with open('answersheet/%s.txt' % code, 'w') as f:#出力ファイル作成
#回答番号の読み取り
answer = []
for x in range(start,len(Result)):
res = np.where(Result[x]==True)[0]#+1
if len(res)>1:
res = 20
print("問%sダブルマーク" % (x-start+1))
answer.append(res)
elif len(res)==1:
answer.append(int(res[0]))#リスト化
else:
res = 20
print("問%sノーマーク" % (x-start+1))
answer.append(res)#リスト化
#ファイルに出力##
# print("\n出席番号%s" % code, file=f)
# print("\n", file=f)
# print("番号", "正解", "回答", "正誤", "配点", file=f)
for nu, an, ea, al in zip(number, answer, eanswer, allo):
if str(an) == str(20):
print(str(nu).rjust(3), str(ea).rjust(3), E.rjust(3), F.rjust(3), str(al).rjust(3), file=f)
elif str(an) == str(ea):
print(str(nu).rjust(3), str(ea).rjust(3), str(an).rjust(3), T.rjust(3), str(al).rjust(3), file=f)
# score = score + al
else:
print(str(nu).rjust(3), str(ea).rjust(3), str(an).rjust(3), F.rjust(3), str(al).rjust(3), file=f)
# print("\n合計 %d 点" % score, file=f)
f.close()
print("読取完了")
#おわり
正答率計算用
rate_windows.f95
program main
implicit none
integer, parameter :: n = 1000
integer i, j, nd, ndd, x(n,n), y(n,n), z(n,n), v(n,n), w(n,n), total(n), totals(n), k, totalm
integer f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10
character :: a(n)*7, b*12, bb*17, c(n)*19, cc(n)*24
character t*1, f*1, e*1
real r(n), av, s, ss(n), ssm
b = 'answersheet/'
bb = 'answersheet_rate/'
t = 'o'
f = 'x'
e = '?'
!リストからファイル読み込み
open(60,file='list.txt')
do i = 1, n
read(60,*,end=100) a(i)
c(i) = b//a(i)
cc(i)= bb//a(i)
end do
100 close(60)
nd = i - 1
!初期値リセット
total = 0
r = 0
av = 0
s = 0
do i = 1, nd !ファイル(40人学級なら40列)
open(20,file=c(i))
do j = 1, n !紙(問題数が20なら20列)
read(20,*,end=101) x(j,i), y(j,i), z(j,i), v(j,i), w(j,i)
end do
101 close(20)
end do
ndd = j - 1
!合計点の計算
do i = 1, nd
do j = 1, ndd
if (y(j,i) == z(j,i)) then
total(i) = total(i) + w(j,i)
else
endif
end do
end do
!平均点の計算
do i = 1, nd
av = av + total(i)
end do
av = av/nd
!標準偏差の計算
do i = 1, nd
s = s + (total(i) - av)**2
end do
s = s/nd
s = sqrt(s)
!偏差値の計算
do i = 1, nd
ss(i) = ((total(i) - av)/s)*10 + 50
end do
!正答率の計算
do j = 1, ndd
do i = 1, nd
r(j) = r(j) + v(j,i)
end do
r(j) = (r(j)/nd)*100 !正答率の計算
end do
!度数分布作成
f0 = 0
f1 = 0
f2 = 0
f3 = 0
f4 = 0
f5 = 0
f6 = 0
f7 = 0
f8 = 0
f9 = 0
f10 = 0
do i = 1, nd
if (total(i) >= 0 .and. total(i) < 10) then
f0 = f0 + 1
else if (total(i) >= 10 .and. total(i) < 20) then
f1 = f1 + 1
else if (total(i) >= 20 .and. total(i) < 30) then
f2 = f2 + 1
else if (total(i) >= 30 .and. total(i) < 40) then
f3 = f3 + 1
else if (total(i) >= 40 .and. total(i) < 50) then
f4 = f4 + 1
else if (total(i) >= 50 .and. total(i) < 60) then
f5 = f5 + 1
else if (total(i) >= 60 .and. total(i) < 70) then
f6 = f6 + 1
else if (total(i) >= 70 .and. total(i) < 80) then
f7 = f7 + 1
else if (total(i) >= 80 .and. total(i) < 90) then
f8 = f8 + 1
else if (total(i) >= 90 .and. total(i) < 100) then
f9 = f9 + 1
else if (total(i) == 100) then
f10 = f10 + 1
endif
end do
!結果出力
do i = 1, nd
open(30,file=cc(i))
write(30,*)
write(30,*) ' 出席番号 ', a(i)(1:3)
write(30,*)
write(30,*)
write(30,*) ' 番号', ' 正解', ' 回答', ' 正誤', ' 配点', ' 正答率'
do j = 1, ndd
if (z(j,i) == 20) then
write(30,'(2i5,2a5,i6,f8.1)') x(j,i), y(j,i), e, e, w(j,i), r(j)
else
if (v(j,i) == 1) then
write(30,'(3i5,a5,i6,f8.1)') x(j,i), y(j,i), z(j,i), t, w(j,i), r(j)
else if (v(j,i) == 0) then
write(30,'(3i5,a5,i6,f8.1)') x(j,i), y(j,i), z(j,i), f, w(j,i), r(j)
endif
endif
end do
write(30,*)
write(30,*)' 合計点', ' 偏差値'
write(30,'(i5,f8.1)') total(i), ss(i)
write(30,*)'----------------------------------'
write(30,*)' 平均点', ' 標準偏差'
write(30,'(f7.1,f6.1)') av, s
write(30,*)
write(30,*)' 度数分布'
write(30,*)' 100', ' ', f10
write(30,*)' 90~99', f9
write(30,*)' 80~89', f8
write(30,*)' 70~79', f7
write(30,*)' 60~69', f6
write(30,*)' 50~59', f5
write(30,*)' 40~49', f4
write(30,*)' 30~39', f3
write(30,*)' 20~29', f2
write(30,*)' 10~19', f1
write(30,*)' 0 ~ 9', f0
close(30)
end do
!模範解答
i = 1
open(40,file='answersheet_rate/model_answer.txt')
write(40,*)
write(40,*) ' 模範解答 '
write(40,*)
write(40,*)
write(40,*) ' 番号', ' 正解', ' 回答', ' 正誤', ' 配点', ' 正答率'
do j = 1, ndd
write(40,'(3i5,a5,i6,f8.1)') x(j,i), y(j,i), y(j,i), t, w(j,i), r(j)
end do
totalm = 0
do j = 1, ndd
totalm = totalm + w(j,i)
end do
!偏差値の計算
do i = 1, nd
ssm = ((totalm - av)/s)*10 + 50
end do
write(40,*)
write(40,*)' 合計点', ' 偏差値'
write(40,'(i5,f8.1)') totalm, ssm
write(40,*)'----------------------------------'
write(40,*)' 平均点', ' 標準偏差'
write(40,'(f7.1,f6.1)') av, s
write(40,*)
write(40,*)' 度数分布'
write(40,*)' 100', ' ', f10
write(40,*)' 90~99', f9
write(40,*)' 80~89', f8
write(40,*)' 70~79', f7
write(40,*)' 60~69', f6
write(40,*)' 50~59', f5
write(40,*)' 40~49', f4
write(40,*)' 30~39', f3
write(40,*)' 20~29', f2
write(40,*)' 10~19', f1
write(40,*)' 0 ~ 9', f0
close(40)
endprogram main