MarkSheetReader」カテゴリーアーカイブ

マークシートを読み取るプログラムの解説

塗りつぶさないマークシート

「塗りつぶす」方式でなく、より簡易な「線を引く」方式でマークするシートの例。

複数マーク可能なマークシート(選択肢は0~99 に対応)を作成したら、マークする時間そのものを短縮する必要性を痛感。今回は「よりはやく」・「よりカンタン」にマーク可能なシート作りに挑戦。

【もくじ】

1.もっとはやくマークできないか?
2.凡例として線でマークして違和感のないカタチは?
3.「線」マークの読み取りテスト
4.まとめ
5.お願いとお断り

1.もっとはやくマークできないか?

設問数が100ある試験で、複数マークを可とした場合、受験者は制限時間内に最大で200近いマークを塗りつぶさなくてはならない。制限時間が50分の試験で、190個マークするとして、1個2秒でマークした場合、マークするのに必要な時間は380秒、すなわち6分20秒となる。1個3秒でマークした場合の必要時間は、なんと9分30秒。制限時間の1/5がマークするためだけに使われてしまう。

採点者がラクをするのに、無理やり付き合わされる受験者の不満が爆発するのが見えるような気がして、小心者の筆者は「マークするために必要な時間を短縮する」にはどうしたらいいか、必死で考えた。

これまでに使用してきたマークシートのマークは、すべて選択肢の番号を縦長だ円で囲んだもの。

これまでに使用してきたマークシート


実際には、テストしてみると、次のようにマークしても読み取りパラメータの設定次第で十分読み取り可能なのだが・・・

こんなマークでも、実際には読み取りが可能。


しかし、受験者の心理として、選択肢が縦長だ円で囲まれていれば、だ円内を塗りつぶしたくなるもの。塗りつぶさず、「線」でマークしてもらうには、「線」でマークしたくなる形状にマークシートを改良しなければならない。

最初に考えたのは、「選択肢の番号を縦長四角形で囲む」という方法。

たとえば、こんな感じ


ただ、これだと、凡例で次のようにマーク方法を示してあっても・・・

どちらかと言えば、これはむしろ「悪いマーク」の例


真面目な受験者であれば、あるほど・・・

枠内を塗りつぶさなくてはいけないと考えるのが自然。


かといって、次のようにすると、デザインとして美しくない気がするし、四角形の幅が狭まったたけで「塗りつぶし」たくなる気持ちは同じ。

塗りつぶし面積が減少しただけ?


そんな、こんな理由から縦長四角形で選択肢の番号を囲むというアイデアは見送ることに決定。

2.凡例として線でマークして違和感のないカタチは?

では、凡例として線でマークしてあっても受験者に違和感を与えないマークとは、どんなマークか?

思いついたのは「囲みのない」マーク。これなら・・・

凡例として「線」が使えるし、塗りつぶしたくても塗りつぶす枠がない!


ただ、これだとマークシートっぽくない気が・・・。

マークシートらしくするために一工夫した結果・・・

[ ] 記号で数字の上下を囲んでみた


これならマークの凡例として、「線」が使えるのではあるまいか?


で、出来上がったのが、このマークシート。

複数マーク可能な「線」でマークするシート(設問数は100設問まで)


このマークシートは、次のリンク先からダウンロードすることができます。

3.「線」マークの読み取りテスト

例え、「線」であっても、これまでの経験からまず間違いなくマークの読み取りには成功すると思ったが、「やっぱりできませんでした!」では使ってくださる方に申し訳がたたないので、念のためテストを実行。

読み取り結果を確認しやすいように、設問番号にマークした次のようなマークシートを作成。やっぱり「線」で引く方が、「塗りつぶし」より遥かにラクであることを実感。これなら1設問に対し2か所マークしても「塗りつぶす」より、全然はやい!!

筆記用具は、シャープペンシルを使用。
芯は、HBの0.5mm。
用紙はホームセンターなどで普通に販売されている白色度の高いコピー用紙を使用。
(普段マークシートの印刷に使用している再生紙より、白色度が高いもの)


MS_Reader.exe を起動し、パラメータ設定はデフォルトのまま、読み取りテストを実行。

スキャナーの解像度は200dpiに設定。
カラー画像としてスキャン。
テスト1回目で、全てのマークの読み取りに成功!


間違いなく読めると思ってはいたけれど、ちょっと(あれっ?)って思ったのは、デフォルトパラメータの設定のまま、テスト第1回目で、全てのマークを正しく読み取ることに成功したこと。

実は、今回、実験で使用した用紙とは異なる、白色度が70%程度の再生コピー用紙を用いて予備的に実験した際は、閾値のパラメータをデフォルト設定より1~3程度大きく設定しないと読み取れないマークがあったのだ。今回、実験したものと「線」そのものの濃さや太さが厳密には異なるから正確なことは言えないが、白色度が高い用紙の方が二値化の際に有利なのだろうか?

・・・ということで、今回の実験では何も問題が起きなかったので、その前に行った白色度が70%程度の再生コピー用紙を使って行った予備実験で発見できた問題と対応方法を紹介。

(これは当Blogの過去記事「100選択肢対応マークシートを使用した試験の実施方法」に書いたものの再録です)

【重要】二値化閾値の修正方法(20240707追加)

マークが「うすい」場合、これを正しく読み取ることができず、「空欄」と判定して「999」と表示される場合があります。同じ理由で、複数マークされた解答欄の「1の位」が読めなかった場合も、読み取り判定は「マークの状態に問題あり」となり、「999」と表示されます。

これらの場合は、この後、実行する「読み取り結果のチェック」時に、該当箇所の解答欄が赤枠で囲まれて表示されますので、マークの状態をヒトの眼で確認し、読み取り結果を修正できます。

最も困るのが、複数マークされた解答欄の「10の位」のマークは薄くて読めなかったが、「1の位」のマークの読み取りには成功している場合です。この場合は正しく読み取れた「1の位」のマークが読み取り結果として表示されてしまいます。大変申し訳ないのですが、MS_Reader.exe のチェックプログラムは、この誤読を見つけることができません!!(これは原理的な問題なので、チェックする方法がありません)

この事故を防ぐには、事前に読み取りテストを十分に行って、読み取りパラメータを調整するしか方法がありません。具体的には、最もマークの濃度が薄い受験者のマークシートが正しく読めるようになるまで、閾値の値を1ずつ大きくして読み取りテストを実行します。筆者の行ったテストでは、デフォルト設定の閾値(180)では正しく読めなかったマークも、閾値を大きくすれば読めるようになりました。

「塗りつぶす」方式でなく、より簡易な「線を引く」方式でマークするシートを用いて行った読み取りテストの例

「64」と読むべき箇所の「10の位」を「空欄」と判定、「1の位」は正しく読めた場合、プログラムは読み取り結果を「4」と表示してしまう。


閾値を「1」大きくすると・・・

正しく読めるようになりました。


ごく薄いマークも正しく読めるようになるまで、この操作をくり返します。筆者の行ったテストでは、閾値を1ずつ大きくすることで、指示通り普通の濃さ(マーク箇所の数字が読めなくなる濃さ)でマークされたシートであれば、最終的にすべてのマークを正しく読めるようになりました(上記のマークシートを使用して行った実験では、最終的にすべてのマークを正しく読めた段階で、二値化の閾値の値は「184」でした。なお、このマークシートは後日公開する予定です)。

ただ、あまりにもマークの濃さが薄い場合は「原理的に読めません」ので、「対応不可」としてヒトの眼で読むしかないと思います・・・。

そのような事態にならないよう、予め受験者に対し「薄いマークは読み取れない」旨の注意を徹底しておく必要があります。マークシートを用いた試験では、これが最も重要なことかもしれません。

4.まとめ

(1)複数マーク可能なマークシートを使う場合は、「線」でマークできるようにする。
(2)マークシートを印刷する用紙は、白色度の高いものがよい?カモ
(3)マークシートは「濃く」マークするよう、予め注意を徹底する。

追記_20240709

【注意】複数マーク可能なマークシートでは、音声読み上げ機能は正しく動作しません!

5.お願いとお断り

このサイトの内容を利用される場合は、自己責任でお願いします。記載した内容及びダウンロードしたプログラムを利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。

100選択肢対応マークシートを使用した試験の実施方法

自作マークシートリーダーのプログラムを書き替え、大語群(使用できる選択肢の番号は 0~99)の使用に対応したマークシートを Excel で作成した。

今回は、このマークシートを用いた試験を実施する方法です。

【もくじ】

1.事前の準備と受験者への注意事項
2.スキャナーでマークシートをスキャンしてJpeg画像に変換
3.指定のフォルダ内にフォルダを作成してスキャンした画像をコピー
4.採点専用画像に変換
5.テンプレートとして登録
6.マーク読み取りを実行
7.マーク読み取り結果のチェック
8.CSV形式で出力
9.採点結果通知シートの作成方法
10.お願いとお断り

100選択肢に対応したマークシートリーダーです。

1.事前の準備と受験者への注意事項

(1)マークシートの印刷

プログラムに添付した R25C04D19.xlsx をダブルクリックして開き、必要な枚数をインクジェットプリンタで印刷します。印刷設定はA4・横に設定済みです。用紙はホームセンター等で購入できるコピー用紙を使用してください(読み取りパラメータの設定とマークの読み取りテストは再生コピー用紙を使用して行っています)。マークや罫線枠が設定よりも濃く印刷される輪転機での印刷はお止めください。

100選択肢(0~99)に対応した、複数選択可能なマークシート 「R25C04D19.xlsx」


(2)受験者への注意事項

試験を実施する前にHB以上の濃さの鉛筆とプラスチック消しゴムを用意するよう、受験者全員に連絡してください。可能であれば、シャープペンシルの使用は禁止した方がよいと思います。

問題冊子の表紙には、次の注意事項を印刷してください。


上記注意事項のサンプルをダウンロードできます。

2.スキャナーでマークシートをスキャンしてJpeg画像に変換

(1)スキャンの準備

解答用紙(マークシート)をスキャナーでスキャンする前に、解答用紙が裏返しだったり、逆さまになっていないかどうか等の確認に加え、次のことを必ず実行してください。

・解答用紙に付着している消しゴムの屑をしっかり落とす。
・受験番号(出席番号)が昇順になるよう、解答用紙の並び順を2回以上確認する。
・欠席者がいる場合は、未使用の解答用紙を該当箇所に挿入する。

(2)スキャナーの設定

解答用紙(マークシート)をスキャンする際の読み取り解像度は 200dpi で十分です(解像度を大きく設定しても、MS_Reader.exe での利用に関する限り、メリットは何一つありません)。また、画質は「カラー」を指定し、出力先はPDFファイルではなく、Jpeg画像を指定してください。

3.指定のフォルダ内にフォルダを作成してスキャンした画像をコピー

スキャンしたJpeg画像は、MS_Reader.exeと同じ階層にあるScanDataフォルダ内に適切な名称のフォルダを作成し、そこに保存してください。

【利用できるフォルダ構造】

〇:ScanData¥1年A組

【利用できないフォルダ構造】

×:ScanData¥1年¥A組

ScanData フォルダ内に新規にフォルダを作成し、さらにそのフォルダ内に新規にフォルダを作成して、そこにスキャンした Jpeg 画像を保存するような使い方は出来ません。ご注意ください。

4.採点専用画像に変換

ScanData フォルダ内に新規に作成したフォルダにスキャンしたJpeg画像が用意できたら、次にこの画像をマークシートリーダーで読むための専用画像に変換します。その理由はいくつかありますが、主なものは次の三つです。

(1)読み取り原本は、オリジナル状態のまま残しておく。
(2)マーク読み取り後に人の眼でチェックする際、作業しやすい大きさに整える。
(3)Loop 処理するため、画像の名称を統一し、1から始まる連番の番号を付ける。

具体的な方法は次の通りです。

(1)ダウンロードした Zip ファイルを展開したフォルダ内にある MS_Reader を起動します。

上のアイコンをダブルクリックして起動します。


(2)画面左上のメニューの「1 画像変換」をクリックします。


(3)表示されたサブメニューの「専用画像を作成」をクリックします。


(4)次のフォームが表示されます。画面右上の「選択」ボタンをクリックします。


(5)ScanDataフォルダの内容が表示されます。マークの読み取り処理を行いたいJpeg画像を保存したフォルダをクリックして選択します。選択したフォルダ名が下の「Folder」部分に表示されていることを確認して「OK」をクリックしてください。

【重要】選択するのは「フォルダ」で、「ファイル」ではありません!

Folder部分に、選択したフォルダ名が表示されます。


(6)画面は次のようになります。画面の中央左に表示されたサムネイル画像を参考にして、画像の回転の有無及び回転方向を選択し、続けて、リサイズの有無と縮小率を指定します。読み取り解像度が200 dpi ・A4横形式で、マークシートの列が4列ある場合は、「80%に縮小」してください。


(7)変換処理を行った画像データの出力先(書き出し先)フォルダを指定します。「参照」ボタンをクリックしてください。再びフォルダの選択ダイアログが表示されます。


(8)プログラムは、(5)で指定されたScanDataフォルダ内のフォルダと同じ名前のフォルダをProcDataフォルダ内に自動的に作成します。フォルダをクリックして選択し(下のFolder部分に選択したフォルダ名が表示されたことを確認)、OKをクリックしてください。

【参考】ProcData は、Processed(加工済み)の意味です。短くしすぎカモですが・・・


(9)ProcDataに続くPathを確認し、「変換実行」ボタンをクリックします。

〇:ProcData¥(自動的に作成されたスキャンした画像を保存したフォルダと同名のフォルダ)

×:ProcData¥AAA¥BBB¥CCC


(10)次のメッセージが表示されます。MS_Readerは、筆者が別に作成した手書き答案の採点プログラムと併用して使用することもできますが、今回の処理ではそのような形では運用しませんので「いいえ」ボタンをクリックしてください。


(11)専用画像の作成が完了すると、次のメッセージが表示されます。OKをクリックしてください。


(12)「終了」ボタンをクリックして、変換作業を終了します(画面下・中央の「画面の初期化」ボタンをクリックすれば、続けて他のクラス/講座の画像を同様に処理することも可能です)。

変換後のファイル名はProcDataフォルダ内に作成した変換先フォルダの名称+01から始まる連番となります。

5.テンプレートとして登録

筆者の作成したマークシートリーダーでは、マークの読み取りに際し、まず特徴点(マークシート画像内のトリプルドット:■■■ )をコンピュータの眼である OpenCV を用いて探し出し、三つの ■ のうち最も左の ■ の左上隅を座標原点(0,0)として、ここからの距離情報を利用してマークシートの第1列を画像として切り出し、二値化・色の反転処理を行って、さらにそこから1行ずつ「行の画像」を切り出し、この「行の画像」を選択肢数個に切り分けて白面積を計算、それが大きい場合に「マークあり」と判定しています。

このため最初にマークシートの特徴点の位置と各列の左上隅及び右下隅の位置を座標として登録し、これをそのマークシート形式の定型フォーマット(=テンプレート)として利用できるように名前を付けて登録する処理を行います。

テンプレートの名前の意味は、次の通りです。

例:R25C04D19

1列あたりの行数・全列数・選択肢の形式と選択肢数を「行・列・選択肢」順に並べています。
R は Row (=行)、すなわち1列 25 行より成ること、
C は Column (=列)、すなわち4列あること、
D は Double 型、すなわち複数マーク対応で、1行あたりの選択肢数は 19 個。
(ここが S の場合は Single 型、複数マーク不可)

Word や Excel で作成したマークシートを、同じインクジェットプリンタで印刷して使用しているので、試験を実施する度にテンプレートを登録する必要はないはずなのですが、筆者はなんとなく不安で、毎回新しくテンプレートを登録し直して作業しています・・・

テンプレートの登録方法は、次の通りです。

(1)「2 テンプレート」をクリックして表示されるサブメニューから「テンプレートの新規登録」をクリックして選択します。


(2)別のWindowが開き、次の画面が表示されます。画面右上の「取得」ボタンをクリックします。


(3)ファイル選択ダイアログが表示されます。任意のマークシート画像を1枚選んでクリックして選択してください。下のファイル名欄にクリックした画像の名称が表示されていることを確認して、「開く」ボタンをクリックします。


(4)選択したマークシートが表示されます。画面右上の「マーカー」オプションボタンをクリックしてください。


(5)画像左上の特徴点部分が3倍の大きさで拡大表示されます。特徴点画像の左上位置をポイント(マウスのカーソル:+の中心を合わせる)してクリックしたらそのまま指を離さずに特徴点画像の右下へドラッグしてください。ドラッグ中は黒い太い枠線(=ラバーバンド)が表示されます。

赤枠の中、ラバーバンドの様子がよくわかるように、特徴点画像より少し大きめにドラッグしています。


実際は、次の図のように、ラバーバンドが特徴点画像の外側をぴったり包む(両者の幅と高さが同じになる)ようにドラッグします。


(6)ドラッグ終了時に矩形選択された部分が特徴点画像としてコピーされ、下の図のように表示されます。照合手法は自動的に設定されますので、変更しないでください。続けてコンピュータの眼である OpenCV がマークシート画像内の特徴点を見つけることができるか、どうかのテストを実行します。

「マーカー画像の読み取りテスト」ボタンをクリックしてください。


(7)OpenCVが発見した特徴点画像の位置が赤い矩形で表示されます。特徴点画像を完全に一致していることを確認し、表示されるメッセージを読んでOKをクリックしてください。


(8)選択対象グループの「解答欄」オプションボタンをクリックすると、案内バルーンが表示されます。マークシートの1列あたりの行数と、マークシート全体の列数、1行あたりの選択肢数をコンボボックスの選択肢から選んで順に設定します。


(9)次の図は、複数選択可能なマークシートの設定例で、行数・列数・選択肢数をそれぞれ入力した直後の状態です。複数マークを許可するか、どうかの設定を行います。

【重要】
複数マークを許可する場合は「複数マークによる採点を実施」チェックボックスをクリックして、チェックが入った状態 にしてください!!

GUIのデザインが悪いためか、作った本人でも! この設定の操作を時々忘れます。複数選択可能なマークシートのテンプレート設定を行う場合は、くれぐれも注意してください。


(10)マークシートの1列目から順に、その座標を取得します。1列ラベルの左のオプションボタンをクリックしてください。マウスカーソルの形状が+になります。

次の図に示したように、まずマークシートのマーク欄枠の左上をクリックし、そのまま指を離さずに、第1列めの右下隅へ向かってドラッグしてください。なお、ドラッグ中は、細い点線のラバーバンド矩形が表示されます。

設問番号欄を含めないようにご注意願います。
必要な座標は、マークシート欄の座標です!


列の右下隅までドラッグした状態を示します。


ドラッグを終了する(マウスの左ボタンから指を離す)と、選択範囲が赤の矩形で囲まれます。取得できた第1列目の座標が画面右のラベルに表示されます。

第1列めの範囲を指定し、座標を取得したところ

【重要】
この作業にマーク読み取りの成否がかかっています!
くれぐれも慎重に、正確に、作業してください。

うまく列を選択できなかった場合は、「再範囲選択」ボタンをクリックして作業をやり直すことができます。


(11)以降、4列目まで同じように作業します。4列目の座標を取得できたら、「保存」ボタンをクリックして取得した座標を ini ファイルに保存します。

保存処理が完了するとメッセージが表示されます


(12)最後に「終了」ボタンをクリックして、テンプレート作成の画面を閉じます。

ボタンは画面右下隅にあります。


これでマークシートを読む準備ができました!

6.マーク読み取りを実行

(1)最初に使用するテンプレートを選択します。

「2 テンプレート」をクリックするとサブメニューが表示されます。
「テンプレートの選択」を選んでください。


(2)テンプレートの選択画面が開きます。使用したいテンプレート名をクリックして反転表示させ、「決定」ボタンをクリックしてください。


【参考】テンプレートの削除方法
必要のなくなったテンプレート名をクリックして選択、「テンプレートの削除」チェックボックスをチェック、「実行」ボタンをクリックすれば不要なテンプレートを削除できます。削除したテンプレートを元に戻すことはできません。テンプレートを削除する際は、その要不要に十分ご注意ください。

(3)次のメッセージが表示されます。これはメニューの「3 作業フォルダ」をクリックすると表示されるサブメニューの「作業フォルダの選択」をクリックした際に表示されるメッセージと同じものです。「はい」ボタンをクリックしてください。


(4)フォルダの選択ダイアログが表示されます。ProcData フォルダ内の読み取りたいマークシート画像のあるフォルダをクリックして選択してください。下のFoleder部分に選択したフォルダ名が表示されたことを確認して、「OK」ボタンをクリックしてください。

選択するのは「ファイル」ではなく、「フォルダ」です。


(5)テンプレート名と関連付けて保存されている特徴点画像が見つかった場合は、それを赤の矩形で囲んで表示します。また、特徴点画像からの距離座標を用いてマークシート欄第1列の1行目がどこにあるのかを計算し、その位置をこちらも赤い矩形で囲って表示します。

【参考】PCによっては、ここで Python Engine の初期化に時間がかかることがあります!

次の図のように表示されれば、マークの読み取り準備は完了です。

諸設定が意図した通りに反映され、OpenCVが正しく動作していることを確認したら、
「OK」ボタンをクリックしてください。


(6)マークの読み取りを実行します。案内バルーンが表示されますので、その下にある「読む」ボタンをクリックしてください。


Python4Delphi が使用できる環境(組み込みPython環境を入れた Python39-32 フォルダが MS_Reader.exe と同じフォルダにある場合)ならば、P4D チェックボックスに自動的にチェックが入り、Python 用のOpenCVを用いてプログラムは動作します。


Python4Delphi が利用できない場合、プログラムはDelphi用のOpenCVを利用して動作します。

画面下に表示されている Grid コントロールにすべてのマークシート画像の読み取り結果が表示されたら、マークの読み取りは完了です。通常の動作モードでは、マークの読み取り完了を知らせるメッセージは表示されません。

複数選択可能なマークシートの場合、空欄や3個以上マークされている等、
読み取り結果に何らかの問題がある場合は「999」と表示されます。

【重要】二値化閾値の修正方法(20240707追加)

マークが「うすい」場合、これを正しく読み取ることができず、「空欄」と判定して「999」と表示される場合があります。同じ理由で、複数マークされた解答欄の「1の位」が読めなかった場合も、読み取り判定は「マークの状態に問題あり」となり、「999」と表示されます。

これらの場合は、この後、実行する「読み取り結果のチェック」時に、該当箇所の解答欄が赤枠で囲まれて表示されますので、マークの状態をヒトの眼で確認し、読み取り結果を修正できます。

最も困るのが、複数マークされた解答欄の「10の位」のマークは薄くて読めなかったが、「1の位」のマークの読み取りには成功している場合です。この場合は正しく読み取れた「1の位」のマークが読み取り結果として表示されてしまいます。大変申し訳ないのですが、MS_Reader.exe のチェックプログラムは、この誤読を見つけることができません!!(これは原理的な問題なので、チェックする方法がありません)

この事故を防ぐには、事前に読み取りテストを十分に行って、読み取りパラメータを調整するしか方法がありません。具体的には、最もマークの濃度が薄い受験者のマークシートが正しく読めるようになるまで、閾値の値を1ずつ大きくして読み取りテストを実行します。筆者の行ったテストでは、デフォルト設定の閾値(180)では正しく読めなかったマークも、閾値を大きくすれば読めるようになりました。

「塗りつぶす」方式でなく、より簡易な「線を引く」方式でマークするシートを用いて行った読み取りテストの例

「64」と読むべき箇所の「10の位」を「空欄」と判定、「1の位」は正しく読めた場合、プログラムは読み取り結果を「4」と表示してしまう。


閾値を「1」大きくすると・・・

正しく読めるようになりました。


ごく薄いマークも正しく読めるようになるまで、この操作をくり返します。筆者の行ったテストでは、閾値を1ずつ大きくすることで、指示通り普通の濃さ(マーク箇所の数字が読めなくなる濃さ)でマークされたシートであれば、最終的にすべてのマークを正しく読めるようになりました(上記のマークシートを使用して行った実験では、最終的にすべてのマークを正しく読めた段階で、二値化の閾値の値は「184」でした。なお、このマークシートは後日公開する予定です)。

ただ、あまりにもマークの濃さが薄い場合は「原理的に読めません」ので、「対応不可」としてヒトの眼で読むしかないと思います・・・。

そのような事態にならないよう、予め受験者に対し「薄いマークは読み取れない」旨の注意を徹底しておく必要があります。マークシートを用いた試験では、これが最も重要なことかもしれません。


【参考】Grid コントロールの高さを変更する方法

Grid コントロールの高さは自由に変更できます。マークシート画像と Grid コントロールの境界部分にマウスカーソルを持って行く(ポイントする)と、=の上と下に上下向きの矢印のついたポインタ形状に変化する場所があります。この部分を上下にドラッグすることで、Grid コントロールの高さを変更することができます。

Grid コントロールの高さを1行分にしたところ

7.マーク読み取り結果のチェック方法

(1)マークの読み取りが完了したら、結果をチェックします。設問数が100である場合はそのまま「Check!」ボタンをクリックしてください。

Check! ボタンをクリックして、読み取り結果を確認します。


設問数が100 未満の場合、空欄その他の判定フラグである「999」が多数入力されていますので、チェック時にそれらのチェックを省略する設定を行います。

例えば、設問数が「80」である場合、4列目の設問「81」以降の「999」はチェックの必要性がありませんから、Skip チェックボックスにチェックして、4列目のコンボボックスに選択肢から「81」を選択します。「覚」ボタンをクリックすると、この設定を記憶します(他のクラス/講座のマークシートを読み取る場合は記憶させてください)。この設定を行ってから、Check! ボタンをクリックして読み取り結果のチェックを実行してください。


(2)読み取り結果に問題があると判定された場合は、次のように「問題あり」と判定された箇所が赤い矩形で囲まれて表示されます。(この場合は、マークが横に長すぎて隣のマークの領域に入ってしまっているのだと思います)

実際の画面では、14 設問目の「999」は青く反転表示されています。
(画面をキャプチャーした際、青の反転表示が消えてしまいました)


確認を行った結果、マークが「15」であり、正解と見なせる場合は Grid コントロールの「999」を消して「15」と入力し、読み取り結果を修正することができます。

読み取り結果を「15」に変更したところ


ちなみにこのような場合、「P4Dを使用」のチェックを外し、判定領域をマーク画像の中心付近のみに設定して読み直せば正しく読めるようになります。(ただし、動作速度は遅くなります)


読み取りパラメータを上のように変更して再度マーク読み取りを実行した場合、

「999」表示は消え、さっきは読めなかったマークを正しく読むことができました。


(3)再度「Check!」ボタンをクリックして、チェックを続行します。

10の位に二つマークがある場合等、不正解と見なせる場合は、「999」を修正せず、チェックを続行します。
続けて「Check!」をクリックしてください。


次のメッセージが表示されたらチェックは完了です。

8.CSV形式で出力

マークの読み取り結果の確認作業が完了したら、Grid コントロールに表示されているデータをCSV形式でファイルに出力できます。このCSVファイルは筆者作の採点結果通知作成プログラムから読み込んで採点結果通知票の作成に利用できます。Excel Book にもこのデータを書き出すことができますが、複数マーク対応の採点結果通知を作成できる Excel のワークシートは作成しておりませんので、ここではその処理方法の詳細は説明しません。

CSV形式で出力する方法は、次の通りです。

画面右下の「ファイルへ出力」グループの CSV オプションボタンをクリックして選択し、「書き出し」ボタンをクリックします。


書込みが完了すると、次のメッセージが表示されます。


表計算ソフトその他を用い、ご自身で採点結果を処理される場合は、出力先をメモしてください。

出力されたCSVの内容は、次の通りです。

1行目は「設問番号」、A列が「マークシート番号(=出席番号)」です。

9.採点結果通知シートの作成方法

採点結果通知の作成方法は、当Blogの過去記事をご参照下さい。

10.お願いとお断り

このサイトの内容を利用される場合は、自己責任でお願いします。記載した内容及びダウンロードしたプログラムを利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。

100選択肢に対応したマークシートリーダー


先日、電車にゆられていたら先輩が。

「選択肢がたくさんあると、マークシート使うの、難しいかなー?」って。

なんかおもしろいこと、ないかなー☆って、毎日、ひまなんだもん。

すぐに出来そうな気がしたので、さっそく大語群に対応したマークシートリーダー作成にチャレンジ。

今までのは数学や教科「情報」の試験用に作成した16選択肢が最大だったが。

数学の試験用に作成したマークシート(マーク部分は-記号)
マークの色が濃いのは、開発初期のマークシートの画像であるため。
教科「情報」の試験用に作成したマークシート(選択肢の番号はゼロ始まり)
経験を積む中で、誤判定を防止するため、マークの色はどんどん薄くなった。

今回、作成したマークシート(最終的なかたち・Excel で作成)。

大語群に対応したマークシート(選択肢の番号は0ー99、合計100)、遂に完成!

正直、思ったほど、かんたんではありませんでした!!

【もくじ】

1.最初に作ったのはB4縦型のマークシート
2.次に作ったのはA4横型のマークシート
3.Excel でマークシート作成に挑戦
4.マークシートを最適化
5.読み取りプログラムも修正
6.発見した問題点と解決策
7.まとめにならないまとめ
8.プログラムのダウンロード
9.お願いとお断り

1.最初に作ったのはB4縦型のマークシート

選択肢の数が多いことを、ここでは『大語群』と呼ぶことにする。この大語群に対応したマークシートを作るにあたり、最初に決めておくべきことはもちろん選択肢の最大数。30個もあれば十分な気もしたが、「大きいことはいいことだ!」とも言うし、どうせ作るなら100個まで対応できるようにしようと決心。

数年前にマークシートリーダーを作ったとき、選択肢数50個に対応した複数マーク読み取り可能なプログラムを書いた記憶があり、10の位と1の位を分けてマークする次のような形式のマークシートがすぐに思い浮かぶ。1行あたり、2個までのマークを読み取れるようにコードを修正すれば、このマークシートで選択肢の番号を0-99として、計100個の大語群を使った試験にも対応できるはずだ。

10の位のマーク欄が空欄なら、プログラムは1の位のマークのみを読むよう設定


このイメージを実現するにあたり、差し当たって問題になるのはマークシート用紙の基本サイズ。さすがにA3サイズの用紙は、マークシートとして使うには巨大すぎる気がする・・・が、1行あたり10の位のマークに①~⑨で9個、1の位のマークに⓪~⑨で10個、合計19個のマークを用意して、得点設定は1設問1点で合計100点とするためには、当然100設問分の行を用意しなければならない。

1列50行で2列作成するとなると・・・、やっぱり、B4版で、縦置きか?

これまでのマークシートは Word で作成していたので、今回も Word を利用。・・・と言うか、本当は印刷設定の自由度が大きい Excel を使いたいのだが、Excel で縦楕円の丸囲み数字を上手に作成する方法がわからない。そこで縦楕円の丸囲み数字が簡単に作成できる Word を利用した・・・というのが正直なところ。

ちなみに Word で縦楕円の丸囲み数字(=「囲い文字」というらしい)を作成する方法は・・・

Word なら、Font は「メイリオ」を選択(フォントサイズを大きくしない場合)、丸囲みしたい数字を半角で入力、入力した数字をマウスでドラッグして選択してから、フォントリボンの「囲い文字」アイコンをクリックすると・・・

赤い枠で囲んだのが「囲い文字」を作成するアイコン。
数字を入力して、ドラッグして選択したのち、これをクリック。


ダイアログが表示されるので、スタイルを設定して・・・

スタイルは「文字のサイズを合わせる」を選択。


さらに数字の選択状態は解除しないまま、段落リボンの「拡張書式」をクリックして、表示されるサブメニューのいちばん下にある「文字の拡大/縮小」をクリックして、さらに表示されるサブメニューの「66%」をクリックすれば・・・

マークシートのマーク領域(縦楕円の囲い文字)を作成


思った通りの囲い文字が完成!


あとはマークシート用途に利用できるよう、色の設定を薄めに変更する等して、必要な選択肢の数だけこれを作成すればいいんだけど・・・

これを Excel で実現する方法がわからない・・・


そのような理由から、とりあえず Word で作成してみたB4版・縦置き型のマークシート。
思ったより巨大で、マークするのがたいへんな気が。

マークするだけで疲れた・・・


読み取り実験用に設問番号1から順に、読み取りデータが設問番号と同じになるようマークしてみる。
100個目は1の位の「0:ゼロ」をマーク。これで1から99と0(ゼロ)で、合計100の選択肢が使える大語群対応型マークシートが完成・・・したと思ったんだけど。

複数マークの読み取りを可能にするため、リーダーのプログラムを少し変更。

Delphiを起動して、マークシートリーダーのプロジェクトファイル一式をコピーして、新たな複数マークの読み取りに対応したプロジェクトを作成。

マーク読み取り手続き部分のコードを次のように変更。最初に手直ししたのは、P4Dを使ったスクリプト部分。

      //複数マークの読み取り方法
      if (Copy(strMS_Type,10,2)='19') and (chk_MultipleMarks.Checked) then
      begin
        //選択肢数が19で、複数マーク許可であった場合
        StrList.Add('                var1.Value = str(res)');
      end else begin
        //複数マークは不許可であった場合
        StrList.Add('                var1.Value = "99"');
      end;

Python側で読み取った値をDelphi側で処理する部分も変更(一部を抜粋)。

//選択肢の始まりは「ゼロ」
  if (Copy(strMS_Type,10,2)='19') and (chk_MultipleMarks.Checked) then
  begin
    //複数マークに対応
    //strAnsList[intSG_k]の文字数を調査
    strCount:=ElementToCharLen(strAnsList[intSG_k],Length(strAnsList[intSG_k]));

    //チェック内容は、以下の通り
    {
    文字数が2文字の場合、末尾の1文字を取得する
    10 -> 0
    11 -> 1
    19 -> 9
    末尾1文字がマークした選択肢の番号になる

    文字数が5文字の場合、
     1 10 -> 2文字目が1、末尾2文字が10 -> 10
     2 11 -> 2文字目が2、末尾2文字が11 -> 21
     3 12 -> 2文字目が3、末尾2文字が12 -> 32
    (2文字目×10)+(末尾2文字 - 10)がマークした選択肢の番号になる
    }

    case strCount of
      2:begin
        //2文字の場合は、末尾1文字が選択した選択肢の番号
        StringGrid1.Cells[intSG_Col,intSG_Row]:=RightStr(strAnsList[intSG_k],1);
      end;
      3:begin
        //空欄と判定された場合
        if strAnsList[intSG_k]='999' then
        begin
          StringGrid1.Cells[intSG_Col,intSG_Row]:=strAnsList[intSG_k];
        end;
      end;
      5:begin
        //(2文字目×10)+(末尾2文字 - 10)がマークした選択肢の番号
        StringGrid1.Cells[intSG_Col,intSG_Row]:=IntToStr(
          (StrToInt(Copy(strAnsList[intSG_k],2,1)) * 10) +
          (StrToInt(RightStr(strAnsList[intSG_k],2))) - 10);
      end;
    end;
  end else begin

次に、P4Dを使用しないDelphi用のOpenCVを利用したマーク読み取り部分のコードも変更(一部を抜粋)。

  //1行につき選択肢数分Loopする_複数選択肢に対応(New)_20240614
  if (Copy(strMS_Type,10,2)='19') and (chk_MultipleMarks.Checked) then
  begin
    //複数選択可能な場合_選択肢の数だけLoopする
    for p := 0 to intCol-1 do
    begin
      //対象値pが平均値の3倍より大きいか、どうかでマークありと判定
      if AryVal[p]>dblAvg * intKeisu then
      begin
        //マークありとした判定の数を記録
        q:=q+1;
        //マークした番号(記号)を記録
        //intMark:=p+1;
        //10の位(0-8)
        case p of
          0:strMark_A:='1';
          1:strMark_A:='2';
          2:strMark_A:='3';
          3:strMark_A:='4';
          4:strMark_A:='5';
          5:strMark_A:='6';
          6:strMark_A:='7';
          7:strMark_A:='8';
          8:strMark_A:='9';
        end;
        //1の位
        case p of
           9:strMark_B:='0';
          10:strMark_B:='1';
          11:strMark_B:='2';
          12:strMark_B:='3';
          13:strMark_B:='4';
          14:strMark_B:='5';
          15:strMark_B:='6';
          16:strMark_B:='7';
          17:strMark_B:='8';
          18:strMark_B:='9';
        end;
      end;
    end;
    //Loop終了時にマーク数を判定
    if q=0 then
    begin
      //マークした番号がない場合
      iArr[i,Rep]:=999;
    end else begin
      //マークした番号があり、それが一の位である場合
      if (q=1) and (strMark_A='') then
      begin
        //マーク数が1、かつ十の位が空欄であったら
        iArr[i,Rep]:=StrToInt(strMark_B);
      end else begin
        //マーク数は1だが、それが十の位であったら
        iArr[i,Rep]:=100;
      end;
      if (q=2) and (strMark_A<>'') and (strMark_B<>'') then
      begin
        //マーク数が2、かつ十の位と一の位がともに空欄でなかったら
        strMark:=strMark_A+strMark_B;
        iArr[i,Rep]:=StrToInt(strMark);
      end;
      if q>2 then
      begin
        //トリプル以上のマーク数を見分けるフラグは100
        iArr[i,Rep]:=100;
      end;
    end;
  end else begin


Delphiでマークシートリーダーを作成する方法の基本は過去記事をご参照ください。

マークの読み取りそのものは「絶対成功する」自信があった(?)ので、複数マークの読み取り処理を既存のプログラムに追加すれば、速度的なことも含めて楽勝でプログラムは完成するはず・・・だったんだけれど。

実際に上のB4版・縦置き型マークシートをスキャンして読み取りテストを行ってみると・・・

P4D利用時の読み取り結果は期待した通り、100 %正確にマークの読み取りに成功するが、P4Dを利用しない場合に不具合が発生。50設問目は正しくは「2」と読み取らなければならないはずなのに、読み取り結果の表示には、なぜかトリプルマークの判定結果である「100」が表示されている。

※ この時点では、必要数以上にマークがあった場合の表示フラグとして「100」を使用していた。
※ 最終的に、読み取れない解答欄は全て空欄の表示フラグ「999」で示すようプログラムを修正した。

「100」は3つ以上のマークがあった場合に表示されるはずなんだけど?


さらに、よく見てみると 100 設問目もヘン。91、92、・・・、97、98、99 と順調に読み取って、最後は「 0:ゼロ」とくるはず!なのに、読み取り結果はトリプルマーク以上の判定結果である「100」がここにも登場。ヒトならともかく、機械が勢い余るはずもなく、誤認識の原因はまったくもって不明。

どうみてもマークしたのは「0:ゼロ」なんだけど。


今までさんざんテストして、読み取りパラメータ設定も変更の必要が「ない」ところまで煮詰めたと思っていたのに、この結果には唖然とするしかなく、悪夢を見ているのではないかと思ったが。

現実は現実。

変更したコードを見直してみるが、おかしなところは見当たらない(ように思う)。
実際、大多数のマークは「ちゃんと読み取ってる」し・・・

なんで、部分的に読めない箇所があるのか???

読めないなら読めないで、「全部」間違うのが機械だと思うんだけど。

仕方がないから、パラメータ設定をいじってみるが・・・

これがデフォルトのパラメータ設定。
(自分的には、変更の必要がないと思えるところまで、さんざん修正を繰り返して決めた値)


パラメータをどう設定しても、一部のデータを誤って読んでしまう・・・。

例えば、閾値を「200」、判定領域を「20」に変更した場合、

47、48、49ときて、次は2のはずなのになぜか「42」
機械のクセに、勢い余ってるとしか思えない・・・。

こんなプログラムは使えない!!

2.次に作ったのはA4横型のマークシート

誰も助けてくれる人なんていないから、問題は自分で解決するしかない。まぁ、問題そのものを自分で作り出しているとも言えるわけで、自業自得と言えばそれまでのこと。自分以外の誰も困ってないし、それを幸いにあきらめてしまうのがいちばん簡単なことだが、それは最終手段。

原因はわからないが、今まで起きたことのないことが起きている、つまり、今までと違うことをしてるから、そこに問題の発生する原因そのものがあるはずだ。何が違うのか、そこを考えてみる。

今までと違うのは、まず、マークシートのサイズそのもの。B4版なんて使ったことがない。もしかしてそれが原因か? 判定プログラムでは行を図として切り出して、さらにマーク1つずつに分解し、二値化して「白」面積が大きいものを「マークあり」と判定しているから、二値化の閾値の設定にもよるが面積的な部分にも誤認識の原因があるような気もしてきた。そうでなくても、実際に使ってみて、やはりB4サイズは「マークシートとして大きすぎる」気がしたのは、ほんとう・・・。

もし、マークの読み取りプログラムそのものに誤りがあるなら、全てのマークを正しく読めないはずだが、ほとんど正しく読めているから、読み取りプログラムそのものに致命的な問題はない・・・はず。

そこで、これまでに正しく読み取れたものとサイズ的に同じ「A4版・横置き型」のマークシートを作成して実験してみることにする。

そう思って作成したのがこちらのマークシート。縦置きにしなかったのは、「今までと同じにする」という部分にあくまでもこだわった結果。

かなり無理して「詰め込んだ」感、満載。


さすがにA4版・横置き型で1列50行のマークシートは(自分の技術では)Word で作成できず、作成にあたっては(念願の?)Excel を使用。Excel で作成した際の画面はこんな感じ。

縦に長い楕円の囲い文字の作り方がわからず、仕方がないから丸囲みの番号でマークを作成
どこか、なにかが「チープな感じ」で、出来栄えもいまいち。


これで実験すると・・・、P4Dを使った場合からして

まったく読めてない!


高速読み取り処理が可能なP4D環境で正しく動作しなかった時点で(これはダメだ)と思ったが、とりあえず非P4Dモードで動かしてみると・・・

B4版使用時より、さらに悪い結果に。


B4版使用時は「唖然」とする思いであったが、今度は「暗澹たる」思いが。

この結果を目の当たりにしたときは、驚愕のあまり、言葉を失い、ついでにやる気もほぼ全部失い、PCの蓋を閉じて(この表現でいいのか?)、火酒を求めてバイクで現実から逃走・・・

あの土曜日の夜は、まじで、つらかった。

ひー(こころの声)

今、冷静になって考えると、P4Dモードでほとんど読み取れてないのはおそらくマークシートの罫線に問題があり(太すぎ)、これが複数マークの判定につながったのではないかと思えてならないが、最初からマークシートの作り自体が気に入らなかったこともあり、自分史的には・・・この実験自体を「なかった」ことにして、心のバランスを保つことに決定。

ただ、まだ「あきらめる」という気持ちには到底なれないので、このピンチをまたとないチャンスと前向きに捉え、Excel で縦長楕円の丸囲み文字を作成するところからチャレンジを再開。

あきらめられない以上、自分も、プログラムも良くなるしか「ない」。
それが嫌なら、はじめからこんなこと、しないほうがイイ。

このチャレンジは、僕にある唯一の「自由」なんだ。
暗澹たる思いなんかで、終わりにはしたくない。

3.Excel でマークシート作成に挑戦

Excel を起動し、何も入力されていない白い画面をじっと見つめて考える。

(縦長の楕円で、囲い文字をつくるには・・・)

経験から唯一思いつく方法は、図形(楕円)をセル内に収まるように挿入して右クリック、テキストの編集を選択して、中に数字を入力する方法だ。楕円の挿入方法は次の通り。

挿入タブをクリック ⇨ 図のリボンにある図形から楕円を挿入


まずは、準備作業。

(楕円を挿入するより先に)あとあと作業しやすいよう、画面右下の「ズーム」で画面表示を拡大(238%くらいにした)して、全セルを選択(下図を参照)。で、列幅を28ピクセル、行の高さを32ピクセルくらいに設定。

全セルを選択し、列幅と行の高さを変更する


B2のセルに収まるよう、楕円を挿入(ズーム300%)。

楕円を挿入


挿入した楕円を右クリックして、表示されたサブメニューから「テキストの編集」を選択(左クリック)。

図形の中にテキストを挿入


半角で1と入れてみた。

数字は入ったが、位置がよくない・・・


ホームタブをクリックして、配置リボンにある「上下中央揃え」と、その下の「中央揃え」をクリック。

数字の位置はいい感じになった。


あとはマークシートのマークらしくするため、楕円をクリックして選択すると表示される図形の書式タブをクリックし、図形のスタイルリボンのコマンドを使い、楕円の中を白くして、囲いを灰色に設定。

マークシートのマークらしくする


フォントの色も灰色に変更。

数字の色も灰色にする


で、セル内での図形オブジェクトの位置を微調整。

セル内で中央に揃うよう、楕円を選択して左右の矢印キーで位置を微調整


できた!

スキャナーで実際にスキャンしてみた結果から言うと、
実際に使用するマークシートではもう1段階濃い灰色を選んだほうがよさそう


あとは、コレを等間隔で上下の位置もそろえて並べれば・・・いい・・・んだが、その方法がわからない。

藁にも縋る思いで、Google先生に訊ねると・・・

単に「楕円を挿入したセルをクリックして選択し、オートフィルの機能を使って右方向へコピー」するだけ! だよって。

半信半疑で、やってみた。

楕円ではなく、セルを選択


表示された緑の枠の右下隅にあるハンドルをクリックして選択して、そのまま右へドラッグ。
すると・・・

祈るような気持ちとは、まさにこのことか・・・


マウスのボタンを離すと、表示されたのは・・・

思った通りにコピーできた!


これだ。これ!
これを待っていたんだ。

Google先生、ありがとう!

あとは数字を変更すれば・・・

マークシートのマークができた!


こうして得た知識をベースに、これまでの経験を加えて Excel で作成したマークシートがこちら(枠の線の設定状態がよくわかるように、枠線の表示はONに設定した状態)

マークを塗りつぶす際に、上下左右のマークに影響が及ばないだけの間隔を確保


ページレイアウトタブの配置リボンの「配置」をクリックして、「枠線の表示」をオフに設定。

「枠線の表示」をクリックする度に、表示のONとOFFが切り替わる


先の実験では、罫線の太さで痛い思いをした(?)ので、再びその轍を踏まないよう、罫線はいちばん細いものを選び、色もオレンジに設定。こうしておけば二値化する際に、罫線は完全に消えるはずだ。

注意:この時点では、そう、考えて罫線の色を設定しましたが、以下で述べる通り、いちばん細い罫線に対する色の設定は、印刷時に無効になり、印刷色は必ず黒になります!

確か、いちばん細い罫線はこれだったはず・・・


で、罫線の色も文字の色(=マークの色)と同じ薄い灰色に設定して試しに印刷してみると、なにか違和感を感じる印刷物がプリンターから排出された。よく見ると罫線の色が濃い! 指定した灰色でなく、普通の黒のような気がしてならない。他の灰色を選んでも、印刷すると罫線の色は「まったく変わっていない」ように見える。もしかして、色の濃さの設定が反映されていない?

(オレンジ色にしてみるか?)

早速、設定 ⇨ 印刷を実行して確認 ⇨ 結果は「黒いまま!」

再び、Google先生にお伺いをたてて知った驚愕の事実。

「Excel はその仕様のため、いちばん細い罫線は印刷時に必ず黒で印刷されてしまう」とのこと。

まじですか? まったく知りませんでした!!

あわてて2番目に細い罫線に変更。色はオレンジ色を指定(この色がなぜか、すーぱー気に入った)。

そうこうして、ようやく、これなら大丈夫と思えるマークシートが完成。

上記の方法で枠線の表示はOFFに設定した状態の画面。
自分的には、満足できる出来栄え。

4.マークシートを最適化

完成したマークシートの全体のイメージはこんな感じ。

1列あたり25行×4列、1行について19選択肢(10の位:1-9、1の位:0-9)、A4版・横置き、
最大100設問に対応


ちょっとマークが小さいような気もしたが、数学・情報用に作成した1行あたり16選択肢のマークシート同等に、隣り合うマークどうしの間隔もあけることが出来た気がする。これが近すぎると乱暴にマークされた場合、「複数マークあり」と判定してしまう危険性が高まってしまう。

また、1列あたり25行の設定としたことで、上下のマークの間隔も十分広くなった。B4版・縦置きの1列50行やA4版・横置きの1列50行よりも遥かに圧迫感は減少している気がする。

でも、試しに100設問分ぬってみたら、やっぱり、疲れた・・・。100設問分マークするってことは、その約2倍マークしなければならないから、疲れて当然と言えば、当然。

これをスキャンして、最終動作確認。


上のマークシートを、実際にスキャナーでスキャンしてJpeg画像に変換し、マークの読み取り処理を行ってみた。結果はPython環境を使っても、使わなくても、読み取り成功率は100%、ようやく期待通りに動くようになってきた。でも、途中、マークシート作成作業での失敗がなければ、Excel を使って(縦長楕円の囲い文字で)マークシートを作る技術は習得できなかった。「失敗は成功のもと」というけれど、今回あらためて諺の重みを実感。

次はマークシートの印刷の濃度の調整(最終仕上げ)。・・・と言うのも、実はマークの読み取り結果をExcel で処理して採点結果の通知シートを作ってる時は全く問題にならなかったことが、読み取ったマークシート画像に直接 〇 や × 、個々の設問の得点や配点、合計点等を入力して返却するように処理系全体を改良したら、これまで思っても見なかったことが重大な問題となってきたのだ。

次の画像を見れば、それは一目瞭然。

マークシートがほとんど見えない!


マークシートの印刷濃度を薄くしすぎると、スキャナーで読み取る際に枠やマークが本当に薄くなってほとんど見えなくなってしまうのだ。単にマークの有無を読み取るだけなら、■■■ からの距離でマークを切り出して二値化し、白面積を計算しているだけだからマーク以外の画像は真っ白でも何の問題もない(むしろ、それくらいの画像の方がより確実にマークを読み取れる)のだが、スキャンした画像そのものを採点結果通知に再利用するとなると、枠やマークがある程度は「見える・読める」ようにスキャンしなければならない。

マークシートのマークや罫線枠の灰色の濃度を少し濃く(下図を参照)して画像をスキャン、どの程度見えるようになったか、確認してみる。

1段階濃い灰色を指定(実際には Ctrl + A でオブジェクトをすべて選択してから設定)


スキャンして画像を表示してみると・・・

マークは読めるようになったが、罫線枠はまだ見えにくい。


罫線の色をオレンジ色に設定したら、それがとても気に入ってしまったのだが、残念ながらスキャンすると罫線枠はほとんど消えて見えなくなってしまう(マークの有無のみを正確に読み取るという意味では、それは実に理想的なのだが)。ただ、マークシート情報の取得プログラムでは、マークひとつひとつを切り出すために罫線の枠の座標を利用しているから、罫線枠の左上隅と右下隅は座標を取得する場面では確実に見えるようにしておきたい。そこで、罫線枠の左上隅と右下隅だけは線の色を灰色にすることにした。(実は、上の画像はそれがほどこしてある画像)

まず、左上隅を設定。

罫線枠の左上隅の「 部分のみ灰色に変更


同様に、右下隅も設定。

罫線枠の右下隅の 」部分のみ灰色に変更


マークシートの情報を取得する際に、罫線枠が十分よく見えることを確認。

すみっこはよく見える!

5.読み取りプログラムも修正

最終的に実用上問題のないプログラムにするため、思いつく様々なパターンで(誤りを含む)マークを作成し、これをプログラムがどのように判定するか、テストしてみた。

テスト用に、次のマークシートを作成。

動作検証用に作成したマークシート
(スキャンしたら、画像中央やや上に横線が入っていた。原因は不明。)


マークシートのスキャンに使っているスキャナーでスキャンすると、時々、黒い線の入ったJpeg画像が生成される。しかも、この黒い線はマークの読み取り判定になぜか?影響を与えない。

上半分だけ塗りつぶしたマークでも、正しく「4」と読んでいる。
複数マークありと判定されないのはなぜ?


この不思議な現象の原因はまったくわからないが、判定に影響を与えないから、これまでは(まぁいいか)としてきたが・・・。

いずれにしろ、このマークシートを使って動作検証を行った結果、先に記した判定プログラムでは対応できない問題が複数あることが判明。検証をくり返し実行して、一つ一つの問題に対応。最終的に完成したのが次のコード。

  //選択肢の始まりは「ゼロ」(1の位を基準)
  if (Copy(strMS_Type,10,2)='19') and (chk_MultipleMarks.Checked) then
  begin
    //strAnsList[intSG_k]の文字数を調査
    strCount:=ElementToCharLen(strAnsList[intSG_k],Length(strAnsList[intSG_k]));

    //チェック内容は、以下の通り
    {
    文字数が2文字の場合、末尾の1文字を取得する
    10 -> 0
    11 -> 1
    19 -> 9
    末尾1文字がマークした選択肢の番号になる

    文字数が5文字の場合、
     1 10 -> 2文字目が1、末尾2文字が10 -> 10
     2 11 -> 2文字目が2、末尾2文字が11 -> 21
     3 12 -> 2文字目が3、末尾2文字が12 -> 32
    (2文字目×10)+(末尾2文字 - 10)がマークした選択肢の番号になる
    }

    case strCount of
      1:begin
        if StrToInt(strAnsList[intSG_k])<10 then
        begin
          StringGrid1.Cells[intSG_Col,intSG_Row]:='100';
        end;
      end;
      2:begin
        //2文字の場合は、末尾1文字が選択した選択肢の番号
        StringGrid1.Cells[intSG_Col,intSG_Row]:=RightStr(strAnsList[intSG_k],1);
      end;
      3:begin
        //空欄と判定された場合
        if strAnsList[intSG_k]='999' then
        begin
          StringGrid1.Cells[intSG_Col,intSG_Row]:=strAnsList[intSG_k];
        end;
        //3文字と判定された場合、十の位の1~9のダブルマークの場合、
        //2文字目は必ず半角の空欄になる
        if Copy(strAnsList[intSG_k],2,1)=' ' then
        begin
          StringGrid1.Cells[intSG_Col,intSG_Row]:='999';
        end;
      end;
      5:begin
        //文字列の置き換え(先頭2文字を抽出&半角スペースを削除する)
        strData:=StringReplace(Copy(strAnsList[intSG_k],1,2),
          ' ', '', [rfReplaceAll, rfIgnoreCase]);
        //Case 5で先頭2文字が10である場合はダブル以上のマークあり
        if StrToInt(strData) > 9 then
        begin
          StringGrid1.Cells[intSG_Col,intSG_Row]:='999';
        end else begin
          //2文字目が半角スペースでなければ処理可能
          if Copy(strAnsList[intSG_k],2,1)=' ' then
          begin
            StringGrid1.Cells[intSG_Col,intSG_Row]:='999';
          end else begin
            //(2文字目×10)+(末尾2文字 - 10)がマークした選択肢の番号
            StringGrid1.Cells[intSG_Col,intSG_Row]:=IntToStr(
              (StrToInt(Copy(strAnsList[intSG_k],2,1)) * 10) +
              (StrToInt(RightStr(strAnsList[intSG_k],2))) - 10);
          end;
        end;
      end;
      6..99:begin
        StringGrid1.Cells[intSG_Col,intSG_Row]:='999';
      end;
    end;
  end else begin

    //複数選択を許可しないマークシートの処理

  end;

end;


ここでいちばん困ったのは、必要以上にマークされていた場合の処理。

複数マークを容認しないプログラムなら、1行について2個以上マークされていた場合は「複数マークあり」を意味するフラグとして「99」、マークなしの場合(=空欄)は「999」というフラグを用意して対応したが、今回のように複数マークを許可し、読み取り結果を 0 – 99 の100分類で表示する場合、空欄すなわち「マークなし」を「999」と表示するのは同じでよいとしても、十の位や一の位のマーク欄それぞれに2つ、ないし、3つ以上マークされていた場合のフラグをどうしたらいいのか? 最適と思われる答えが見つからずにかなり悩んだ。

出来れば、既存かつ(プログラムによっては)数年をかけて動作検証済みの、読み取り結果のチェックプログラムや、採点結果通知のプログラムを修正せずに、それらをこの複数マーク対応採点システムにもそのまま適用できるように、処理の流れを作りたい。

必要数以上のマークがあった場合、当初、選択肢としては決して使うことのない「000」、「100」、「XXX」等をフラグとして利用することも、かなり真剣に考えたが、これらのフラグを新規に採用した場合、これまでに書いてきたマークシートの読み取り結果を記録したCSVファイルを利用して動作するプログラムをことごとく修正しなければならない。そして、それは新しいバグを生むことに、間違いなく直結する。それだけは、どうしても避けたい。

この際、読み取りエラーをすべて「999」で処理すれば、これまでの経験から、読み取り結果のチェックプログラムは確実に「空欄」=「999」位置を教えてくれるし、もし、それが本当に「空欄」である場合は、人が見ればそれは一目瞭然、もし、それが空欄でない場合は、それを見た「人」に、マークの有無 or 空欄 or その他複数マークの判断を委ねればいい。そしてもし、「人」が見て、マークが正しければプログラムの判定結果を正しく修正、そうでなく、マークが「空欄でない」・「必要数以上にマークされていた」場合は、そのまま「空欄として処理(999)」してもらえば、採点結果には一切影響を与えないはずだ。

そう考えて、「トリプル以上のマークあり」をユーザーに伝えるフラグは用意せず、10の位に1つ、1の位に1つ以外のマークがあった場合はすべて同一に「空欄」フラグの「999」で処理することにした。

6.発見した問題点と解決策

上記動作検証用に作成したマークシートで、実際に動作確認を行った結果、ひとつだけ気になった点があった。それは、マークから横にはみ出て(横に広く)マークされると「トリプル以上のマークあり」という判定が出やすいこと。

「12」を読み取れていない。
おそらく1の位の「2」の横棒マークが「1」の領域に侵入している?


マークとマークの間隔を、これ以上広くするのはさすがに困難。1行19選択肢+行番号というマークシートの形式そのものに起因する問題だから、これは試験の問題用紙の表紙に図付きで「横棒型の塗りつぶし禁止」&「なるべく横に広がらないようマークする」注意を載せて、読み取り不能の「999」判定がなるべく出ないようにすることくらいしか、対策を思いつかなかった。

(縦に長い四角形、もしくは縦型の [ ] で数字を囲うことも考えたが、実際には試していない)

Python環境を利用しない場合は、判定領域をマークの中心付近のみに狭めることで読み取り精度を上げることができる。ただし、読み取り速度はかなり遅くなってしまう。

Python環境を使わず、判定領域を70→50に狭くして、実行すれば上の読み取りエラーは解消できる。


マークシートリーダー作成の初期、まだPython環境を利用できなかった頃、少しでも読み取り速度を早くできないかといろいろ考え、二値化後の白面積の計算領域をマークの中心付近のみとすれば、読み取り速度を向上させることができるのではないかと思って作った機能が思わぬところで役に立った!

以上が、解決策とは言えない対策と、読み取り速度を気にしなければ使える解決策。

7.まとめにならないまとめ

(1)実用的ではないかもしれないが、大語群(100選択肢)に対応したマークシートリーダー完成
(2)Excel を使って1行19選択肢で25行4列、A4横置きのマークシートを作成して試験を実施
(3)採点結果通知は付属の ReportCard.exe で作成

ReportCard.exe の使い方は、下のリンク先をご参照ください。

プログラム本体は、この下のダウンロードリンクから取得可能なZipファイルに同梱しています)
同梱した採点結果通知作成用のプログラム


ただし、選択可能な採点オプションの指定は「採点のみ」or「採点と配点」のみとなります(下図の赤枠)。観点別評価の区分を含めて表示するオプションを選択しても、プログラムは複数マークに対応していないバージョンの流用なので、正解マークの位置を正しく表示できません。少なくても観点別評価の区分は表示できるよう、今後プログラムを改良する予定です。

追記 改良したプログラムに更新しました(20240625)

複数選択可能なマークシートについても、観点別評価の区分や正解マークの位置を表示できるよう、プログラムを更新しました。

採点及び観点別評価の区分と、不正解の設問について正解マークの位置を表示
得点は返却用シートの右下に表示

この大語群専用マークシートを使用した試験の実施方法と、試験後の処理方法について次回の記事で詳細を説明する予定です。よろしければ、そちらの記事もご参照ください。

8.プログラムのダウンロード

大語群に対応したマークシートリーダーは、下のリンク先からダウンロードできます。
なお、バックグラウンドでPython環境を利用し、より高速にマークの読み取り処理を実行するには、別途Python環境の組み込みが必要です。動作に必要なライブラリをインストール済みのPython環境は、当Blogの過去記事へのリンクからダウンロードできます。

Python環境の組み込みはカンタンです。ダウンロードしたZipファイルをダウンロードした後、任意のフォルダに展開、生成されたPython39-32フォルダを、そのまま MS_Reader.exe があるフォルダにコピーするだけです。

こちらの大語群に対応したマークシートリーダーは、当ブログの過去記事に掲載した複数選択不可のマークシートリーダーを、複数選択を前提として設計された専用マークシートのマーク読み取りに特化させたバージョン、いわば派生版です。複数選択不可のマークシートの読み取りにも使用できると思いますがテストは行っておりませんので、ダウンロードしたZipファイルを展開して出来る MS_Reader.exe は複数選択可能なマークシートの読み取り処理専用にお使いください。

この記事で紹介した通り、様々な要因から、マークシートによっては(基本設計が同じであっても)マークを正しく読み取れないことがあります。
また、添付した動作検証済みのマークシートをご利用いただいた場合でも、その印刷方法(輪転機使用等でマークが濃く印刷されていた場合)によっては、マークを正しく読み取れないことがあります。印刷用紙は再生コピー用紙で十分ですが、印刷には 必ずインクジェットプリンタを使用 し、スキャンしたJpeg画像において、マークや罫線枠がうっすらと判別できる程度の濃さで印刷していただく必要があります。

以下、読み取りテスト実行時の環境です。

・A4用紙は、(白くない)再生コピー用紙
・スキャナーは有名メーカー製複合機のスキャナー(カラー/読み取り解像度200 dpi)
・PCはPanasonic製Let’s Note CF-QV
・スキャンした画像をこのプログラム用に変換する際の倍率は80%を指定

「1 画像変換」をクリックすると表示されるサブメニューの「専用画像を作成」をクリック
倍率は80%を指定


上記の環境で、筆者がテストした結果を記事としてここに掲載しました。発見した不具合も正直に書きましたが、筆者が発見していない不具合が他にまだあるかもしれません。ですので、ダウンロードしたマークシートリーダーのご使用はあくまでも自己責任でお願いします。

また、派生版であるため、プログラムには Excel Book に読み取り結果を出力する機能がありますが、大語群に対応した採点結果通知作成用の Excel ファイルは、Zipファイルを展開後、 eFile フォルダ内にあるテンプレートから生成できる Excel ファイルをマクロ有効な Excel Book として保存し、これを元にご自身で作成していただく必要があります。※ Zip ファイルに添付した Excel Book は、大語群マークシートに対応しておりません。

採点結果通知が必要な場合、Zipファイルに同梱した ReportCard.exe をお試しください。こちらは「まとめ」で紹介した通り、動作検証済みです。同梱の ReportCard.exe は選択肢が「ゼロ」から始まる教科「情報」用のマークシートにも対応したものです。

※ お使いのPC環境により、Python Engine の初期化に異様に時間がかかったり、おまけの機能である成績一覧表作成時に、罫線位置が誤って描画される不具合があります。罫線の描画に問題がある場合は、罫線機能をOFFにしてデータのみを出力してください。こちらの成績一覧表はメモ程度にお使いください。

なお、大語群対応のマークシートリーダーでは、マークの読み取り結果を最後に一括して表示する速度優先モードは使用できません。

本記事で紹介したマークシートを同梱しました。Zipファイル展開後に作成されるSample_MarkSheet フォルダ内にある R25C04D19.xlsx をご参照ください。

次のリンク先からマークの高速読み取りを可能にする Python 環境をダウンロードできます。なお、プログラムの動作に必要なライブラリはすべてインストール済みです。



この記事で紹介した100選択肢対応マークシートリーダーの使い方は、次の記事をご参照ください。

9.お願いとお断り

このサイトの内容を利用される場合は、自己責任でお願いします。記載した内容及びダウンロードしたプログラムを利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。

マークシートの採点結果通知(個票)及び成績一覧表の作成

ただし、表計算ソフトは使わずに。

【ご案内】

※ 記事中の「マークシートリーダー」とは、僕がDelphiで作ったマークシートリーダーのプログラムのことです。その最新バージョンと、この記事で紹介するプログラム一式を同梱したZipファイルをダウンロードできます。

【はじめに】

マークシートを利用する際、マークをミスなく読み取れたら、次に読み取り結果を適切に処理する作業が待っています。読み取り結果をCSVファイルに出力し、表計算ソフトで作業するのが一般的な処理の流れだと思いますが、表計算ソフトに苦手意識を持つ方が多いのも事実です。

そこでアンケート集計用途ではなく、試験の成績処理用途専用という「但し書き付き」ですが、『表計算ソフトを使わない』・『入力作業は必要最小限度に留める』・『作業はほぼクリックするだけでOK!』というコンセプトを決めて、マークシートリーダーで読み取り処理を行ったマークシート方式試験の採点結果通知シート(=生徒さんに返却する答案)と成績一覧表(教科担任用)の作成にチャレンジしました。

完成までに、ほぼひと月を要しましたが、マークシートリーダーへの「後付け」成績処理プログラムが出来ました(僕自身が採点現場で動作検証を行った範囲では、期待通りに動作しました)。

マークシートリーダー(最新版のVersion 1.1.4を同梱)と合わせて、次のダウンロードリンクからプログラム一式をダウンロードできます。

【マークシートリーダー & 生徒向け採点結果通知シート と 教員用成績一覧表作成プログラムのダウンロード】

※ 僕のマークシートリーダーは、1問1答の採点用途のみに対応(複数マークを抱き合わせて採点する等、複雑な採点方法には対応しておりません)。また、今日(2024年5月30日)気がついた!のですが、マークシート画像に採点結果を出力する機能の一部は、選択肢がゼロ始まり(教科情報用)の場合に対応していません。選択肢がゼロ始まりのマークシートに対応したバージョンは後日掲載します(プログラムは完成していますが、GUIを作り直す必要があります)。

追記 選択肢がゼロ始まり他のマークシートに対応しました!

選択肢がゼロで始まっているマークシート他に対応した採点結果通知シート作成プログラムはこちらからダウンロードできます。プログラム名は「ReportCard.exe」です。簡単な取り扱い説明のPDFをつけてZipファイルとして掲載しています。展開後、上のリンクからダウンロードした exe と入れ替えて(上書きコピー)お使いください。

追記 100選択肢に対応したマークシートリーダー

大語群(番号0~99)に対応した、複数マークの読み取りを可能にしたマークシートリーダーです。複数マーク読み取りに対応した採点結果通知の作成プログラムも同梱しています。

【もくじ】

1.ダウンロードしたプログラムとその高速化について
2.マークシート画像に採点結果を出力
3.成績一覧表も作成
4.使い方
5.まとめ
6.お願いとお断り

1.ダウンロードしたプログラムとその高速化について

今回紹介する採点結果通知シート作成用のプログラム『ReportCard.exe』は単体では動作しません。動作には、僕のマークシートリーダーが作成したCSVファイル等が必要です。また、動作に必要なフォルダ配置その他の動作環境も、僕のマークシートリーダー専用に作成したものをそのまま利用していますので、僕のマークシートリーダー(MS_Reader.exe) が「確実に動作する環境」で実行していただく必要があります。

ここでは採点結果通知シート作成用のプログラム『ReportCard.exe』の動作検証が手軽に行えるよう、Python環境を除いたマークシート読み取りプログラム(最新版Version 1.1.4)に『ReportCard.exe』を同梱する形でダウンロード用zipファイルを作成し、(上に)掲載しています。もちろん、過去記事で紹介しているPython環境を組み込めば(・・・と言っても、ダウンロードして解凍したPython39-32フォルダをMS_Reader.exeがあるフォルダにコピペするだけですが)、マーク読み取り部分は、より一層高速に動作します。

Python環境:Python4Delphiを利用して Object Pascal に埋め込んだ Python Script を実行し、Python用の OpenCV でマークシートのマークの有無、マークした番号を読み取ります。(PCによっては)Python Engine の初期化になぜか?すごく時間がかかることもありますが、1回初期化すれば、どのPCでも大変高速に動作します。拙作マークシートリーダーの動作に必要なライブラリをすべてインストールしたプログラム埋め込み用の Embeddable Python 一式が下記リンク先からダウンロード可能です。

この「採点結果通知シート作成」プログラムも、Python環境があれば自動的にそれを利用して動作するように設計してありますが、テストしてみた結果で率直な感想を言うと、やはり初回起動時の(必須)Python Engine の初期化に(PCによりますが)かなり時間がかかる(数分!)ことがあります。

僕のPC:Panasonic製Let’s Note CF-QV ではそのようなことはまったく起きませんが、職場で使っているPCではそれが必ず起こります。

とにかく Python Engine の初期化に「それなりに時間がかかる」PCでこのプログラムを使う場合は、例えPython環境があっても、起動直後に画面左上の「✅P4D」のチェックを外し、Python環境を利用せずにプログラムを実行していただいた方が良いかもしれません。

【過去記事へのリンクです】

2.マークシート画像に採点結果を出力

採点結果通知シートのイメージは、こんな感じ(確認画面として表示する手続きは作成しましたが、画像データとして保存する手続きは「その必要なし」と考え、作成しなかったので、これは確認用画面のハードコピーです)。

シートの左上部分を切り取り
シートの右下部分(得点合計等はここに表示)


・・・ですので処理は、採点結果を画面に表示 → そのまま印刷という流れになります。採点の計算は一瞬で終わり、採点画面はすぐに作成できるから、データは保存しません(そもそも保存しておいて、何回も利用するようなモノではないと思いますから)。

まず最初に考えたのは(当たり前ですが)、マーク読み取り結果と配点をマークシート画像に出力(〇の場合は配点=得点となります)し、得点を観点別評価とともにシートの余白(設問番号付近)に表示することです。

正答ならば採点マークと配点(=得点)を表示
不正解の場合、採点マークと配点を表示


採点マークのサイズと水平方向の表示位置は微調整が可能です(ただし、調整結果を保存する機能はありません)。

採点結果の表示位置は、負の数で左・正の数で右に微調整可能

ここで、配点に加え、不正解の場合は正解も表示したくなりました。ただ、記号フォントに縦長の楕円はなかった?・・・と思うので、フォントは好みに応じて選択できるよう、思いつくままにいろいろ設定。

カタチ的には「θ」が最もマークの形状に近い気がします。


ふと、思い立って数字も選べるように設定。

「Num」を選択すると正解のマークの上に数字を表示します


あと、新教育課程では、観点別評価が導入されているので、観点別評価の「知識・技能」は K1、「思考・判断・表現」は K2 として評価の分類も出力できるように設定。正解マークと合わせて表示すると、こんな感じです・・・。

自分的には、コレがいちばん気に入りました!

正解マークの番号を、マークすべき場所に数字で表示する


得点合計と観点別評価ごとの得点合計は(デフォルト設定)シート右下に表示します。もちろん、フォントの大きさは任意の値を設定でき、表示位置は水平・垂直両方向に微調整が可能ですが、こちらも調整後の座標を保存することはできません。


フォントの大きさや表示位置の微調整は、凝り始めたらキリがなくなりそうで、それが表計算ソフトに代わる高い敷居となる可能性(=危険性)を感じ、デフォルト設定で(この程度でまぁいいか?)とユーザーに判断してもらえるよう設定値を調整しました。

すべて控えめな数値を設定しました!
足りない場合は、ちょっと増やせばOKかな?

3.成績一覧表も作成

これがないと採点結果を記録簿に転記し(ここで間違いが発生する可能性があります)、電卓をパチパチ叩いて平均点等を計算するか、一歩進んで、プログラムが出力したCSVファイルを表計算ソフトで処理して、成績一覧表を作成しなければなりません。

転記したり、電卓を使うのは昭和のスタイルだし、働き方改革の流れにも逆行します。CSVファイルを自由自在に操れる方なら、拙作マークシートリーダーには、マーク読み取り結果をCSVファイルに出力する機能を付けてありますから、そちらをご利用ください・・・ってことでOKかな?・・・なんだけれど、「表計算はちょっと苦手で」という方も少なくありません。

PCを使って何かの処理を行うこと自体が、手作業で行ってきた作業を効率よく自動化することに他なりませんから、・・・ほんとうのことを言えば、マークシートリーダーに付属の一機能として最初から成績一覧表の作成機能を付けたかったのですが・・・マークシートリーダー開発当初は、何よりもまず、確実にマークを読み取れることが最重要課題で、それが可能になった時点で実はもう僕自身が(精神的に)ヘトヘトになっていて、(読み取り結果をCSVファイルに出力できれば、あとは表計算ソフトで・・・)みたいな思い(と強い思い込み)があり・・・

新教育課程で導入された観点別評価も、プログラミングして処理するより、表計算ソフトで処理した方がずっと簡単そうに思えたし・・・

同僚からの要望に応え、マークシートリーダーとは別に作成した「手書き答案の採点プログラム」と、マークシートによる解答を併用した採点に対応する場合でも、表計算ソフトは便利だったし・・・

このような諸々の理由から先延ばしになっていた成績一覧表の作成でしたが、2024年、冬、ここで一念発起して、マーク読み取り後の処理に表計算ソフトを一切使わず、ソフトウェアの機能として必要な帳票を出力できるプログラムを書くことに決め、ダミーデータを使って動作確認をくり返し、不具合箇所を発見するたびに少しずつ手直しして、実際に使ってみてどうかという段階にたどり着いたのが、まさに今です。

ただし、どちらかと言えば「採点結果通知シートの方が主」で、成績一覧表は「読めればイイ」程度の、言わばメモみたいなもの・・・表計算ソフトが苦手な方でも、CSVファイルに出力された採点結果を表計算ソフトで開き、得点データを他のワークシートへコピペする作業は可能で、それさえ出来ればあとは協働作業で現場はなんとか動く・・・という勝手な理由で作りは大いに簡素化。

様々な理由から、氏名は「最初の3文字のみ表示」することにしました。
罫線も、横一線のみ。

(氏名と成績はダミーデータです)

ほんとにナイよりマシ・・・というレベルで完成。T_T

プログラムは技術的な知識不足から(だと思うのですが)、罫線が上手く描画されたり、(同じプログラムなのに)PCによっては罫線が予定位置に描画されなかったり・・・。この罫線が上手く描ける場合と、描けない場合の違いがいまだによくわからないのですが、次のようにして無理やり解決?(しましたが、最終的に問題のあるコードは全面的に書き直しました)

【罫線描画問題解決用GUI の勇姿】

CheckBoxとButtonを一つずつ用意


(1)設定 → システム → ディスプレイ設定変更画面の表示を1クリックで行えるボタンを作成。非常の場合は、これで画面の拡大率を100%に戻してもらう。拡大率100%なら確実に予定の位置に描画されるハズ。

・・・と、思ったのですが、結論から言うとこれはダメでした!!

その後、奮闘努力して問題を解決 → (3)へ

ディスプレイ設定を呼び出すコードは1行でOK!

procedure TForm1.btnDispSettingClick(Sender: TObject);
begin
  //usesにWinapi.ShellAPIが必要
  ShellExecute(0, 'open', 'ms-settings:display', nil, nil, SW_SHOWNORMAL);
end;

(2)CheckBoxを利用して「罫線を描画しない」設定を用意する。チェックOFFだと・・・

ある意味では、究極ともいえる罫線問題解決方法。
(これは、ほとんどムチャですな・・・)


(3)罫線の描画に使っていたコードそのものを新たに書き直し、TImage の Canvas と TPrinter のCanvas それぞれに罫線を描画するようにしたところ、罫線が予期しない位置に描画されてしまう問題は解決できました。最初に書いたコードで、(PCにより)罫線が正しく描画される場合とされない場合がある、その本当の理由は未だにわかりませんが・・・

4.使い方

使ってくださる方がいるとも思えませんが、使い方のマニュアルは以下の通りです。

(1)プログラムを起動

「MS_Reader.exe」と同じフォルダにある「ReportCard.exe」をダブルクリックしてプログラムを起動します。

次のメッセージが表示された場合は、「詳細情報」(画像中、赤い枠で囲んで示した部分)をクリックします(プログラムの発行元が不明である場合に、Windows のDefender機能である SmartScreen がこの表示を出すそうです。自分の責任で実行すれば、次回からこのメッセージは表示されなくなります)。

「詳細情報」をクリックします。


すると、次の画面が表示されます。「実行」(画像中、赤い枠で囲んで示した部分)をクリックしてプログラムを起動してください。

「実行」をクリックします。

アメリカでは、採点結果を通知する個票のことを、高校段階までは “Report Card” と呼ぶそうです。Python4Delphiを使用していることを考えると、プログラムの名称に漢字を使用することは、極力、避けたいところです(これは、Pathに含まれる全角文字に関連するエラーに、Pythonスクリプトを書いていて、これまでさんざん悩まされた経験から)。

また、当初、アイコンは濃い目にデザインしたのですが、100 が赤だと目に痛い。そう、痛切に感じた経緯があって、通常アリエナイ色の 100点 をモチーフにしたアイコンにしました。Report Card の文字は、ほぼ読めませんが!「枯れ木も山の賑わい」とお考えいただけたら幸いです。

100 という数字さえ読み取れれば、何をするプログラムなのか?
お使いいただけた方には、わかってもらえるんじゃないかと・・・。

(2)「開く」ボタンをクリックして、ProcDataフォルダ内にある採点結果通知シートを作成したいクラス(or 講座)のマークシート画像を保存したフォルダを選択。

Python環境が利用できる場合は、P4Dに自動的にチェックが入ります。
※ Python Engine の初期化に時間がかかるPCでは、起動時にチェックをOFFにしてください。


選択するのは「ファイル」ではなく「フォルダ」です。


(3)採点結果通知シートを新規に作成する(既存の採点作業の設定ファイルがない)場合は、次の表示が出るのでOKをクリックし、設問数を入力して、画面左に表示されるGridコントロールに必要事項を入力します。


設問数を最初に入力します。


次に、作業の「入力」を選択(オプションボタンをクリック)します。


配点は最も多く設定する値をデフォルト配点として指定(入力)します。


正解とするマークの番号を入力します。

最初だけフォーカスを与えるために入力するGridをクリックしてください。


配点を変更する箇所があれば、正解に続けて入力します。
最後に観点別評価の区分を入力します。「知識・技能」は半角数字で 1 を、「思考・判断・表現」は半角数字で 2 を、それぞれ間違えないように入力してください。

m(__)m:「主体的に学習に取り組む態度」の評価は、この採点システムでは行えません。

観点別評価の入力を行っているところ。


全項目の入力が完了したら、入力に間違いがないことを必ず確認してください。もし、誤りがあれば、ここで確実に発見し、訂正しておかないと・・・、後から大変なコトに・・・。

必要事項をすべて入力し、内容を確認したら採点設定を保存します。

「MySettei.csv」が(上で指定した)マークシート画像のあるフォルダに保存されます。


保存が完了すると、次の確認メッセージが表示されます。

(4)採点ボタンをクリックして、採点を実行します。


表示されている画像の座標情報を記録したテンプレートを選択します。
(テンプレートの作成は、マークシートリーダーで実行)

テンプレート名をクリックして、決定ボタンをクリック。


適切な採点オプションを選択します。


「観点含全部」を選択した場合は・・・

採点記号と配点(正解の場合は得点)、観点別評価の区分を設問番号付近に表示
得点合計と観点ごとの得点合計を右下に表示
空欄と不正解の場合は、正解を表示


前述した通り、正解記号は選択肢から選択して指定できます。

Numを指定した場合は、正解マークの番号が表示されます

(5)画像の切り替え

表示している画像の切り替えはボタンクリックで実行できます。

ボタンは左から順に「先頭へ」・「一つ前へ」・「一つ次へ」・「最後へ」

(6)印刷

「印刷」ボタンをクリックして、採点結果通知シートを印刷します。


クリックすると表示されるメッセージに答えて、全員分 or 個別 印刷のいずれかを選択してください。


用紙の縦横指定を間違えないように注意してください。

(7)成績一覧表の作成

最初に「学年」と「クラス」を選択してください。


選択制の授業等、特別な編成(=「講座」と表現)の名票は出席番号順・氏名のみのデータを予めsNameフォルダ内に分かりやすい名前を付けて、CSVファイルで準備してください。

学年・組は空欄のままにして、「講座名票」ボタンをクリック


ファイルの選択ダイアログが表示されるので、予め作成・保存しておいた講座の名票を選んでOKをクリックしてください。採点結果一覧がGridコントロールに表示されます。

得点等はダミーデータです。


続けて、平均点を正しく計算するため、未受験者の処理を行います。「編集」チェックボックスをチェックしてください。


未受験と思われるデータがある場合、次のメッセージが表示されます。

テストを受験しており、採点結果が0点の場合は「いいえ」をクリックしてください。


得点「0」はすべて未受験として処理した場合、採点結果の一覧は次のようになります。

未受験者のデータを空欄に変更。


「再計算」ボタンをクリックして、平均点等を更新します。


続けてプレビューをクリックするよう案内が出ます。
プレビューをクリックして成績一覧表を表示します(設定はA4・縦、50名/枚で、この設定を変更することはできません)。

なお、受験者数が51名以上の場合でも、プレビュー画面には最初の1枚目の成績一覧表が表示されます。また、任意のページをプレビュー画面に表示する機能は、このバージョンにはありません。

印刷されるデータをプレビュー画面で確認してください。


プレビューに問題がなければ、プレビューのチェックをOFFにして(外して)ください。
印刷ボタンがクリックできるようになります。

印刷ボタンをクリックすると、プリンターへデータが送信されます。受験者数が50名を超える場合は、プリンターへのデータ送信後、印刷最終ページが画面に表示されます。

5.まとめ

今回、拙作マークシート・リーダーのCSV出力を利用するかたちで作成したプログラム(新教育課程観点別評価「知識・技能」及び「思考・判断・表現」の評価に対応)の概要は以下の通りです。

(1)表計算ソフトを使わずに、マークシート方式試験の採点結果通知(個票)を作成。
(2)表計算ソフトを使わずに、マークシート方式試験の成績一覧表(教科担任用)を作成。
(3)マークシート方式試験の成績一覧表をCSVファイルに出力。

6.お願いとお断り

このサイトの内容を利用される場合は、自己責任でお願いします。記載した内容及びダウンロードしたプログラムを利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。

マークシートリーダーを教科「情報」用に設定

マークシートリーダー関連の第4回は、ゼロ始まりの選択肢への対応 です。

マークシートの選択肢には「1」始まりと「0」始まりが存在します。教科「情報」の試験で使用されるマークシートは「0」始まりが標準?のようです。そこで自作のマークシートリーダー及びマークシートを、それぞれ選択肢「ゼロ(0)」始まりにも対応できるよう改良しました。

選択肢「ゼロ(0)」始まりに対応したマークシートリーダーはこちらからダウンロードできます。

Version 1.1.1は、選択肢のゼロ始まりに対応していますが、マークシートの種類の表示に誤りがあります。
Version 1.1.2で、マークシートの種類の表示の誤りを修正しました(数学->数学/情報)
上の記事内に、無料で使えるマークシートリーダー(MS_Reader.zip)のダウンロードリンクがあります。
重要 MS_Reader の使用はあくまでも自己責任でお願いします。動作保証は一切ありません!


マークシートの読み取り速度をPython環境を組み込んで高速化します。

Python環境を組み込むとマークシートリーダーをさらに高速化できます。
重要 組み込みPython環境のご使用もあくまでも自己責任でお願いします。動作保証は一切ありません!

【もくじ】

1.教科「情報」用マークシートのダウンロード
2.選択肢ゼロ始まりの設定方法
3.重要!テンプレート作成時の注意事項
4.お願いとお断り

1.教科「情報」用マークシートのダウンロード

教科「情報」用マークシートはこちらからダウンロードできます。


選択肢はゼロ始まりで、1設問あたり16個まで設定可能です。

ゼロ始まりで15まで、選択肢数16の教科「情報」用マークシート(Version 1)。
ダウンロードできるのは、これを改良した Version 2 です。

2.選択肢ゼロ始まりの設定方法

MS_Readerを起動すると、画面上部のパラメータ設定入力用コントロールの左側に、選択肢の開始番号を指定するComboBoxがあります。教科「情報」用のマークシートを読み取る場合は、ここに「0」を指定(∨マークをクリックして選択)してください。

教科「情報」用である場合、選択肢は「0」始まりを指定

3.重要!テンプレート作成時の注意事項

マークシート情報を記録するテンプレートを作成する際には、選択肢の枠幅を間違って設定しないよう、十分注意してください。指定は、次の図のように行ってください。

列枠そのものではなく、設問番号部分は含めずに、選択肢部分のみを指定してください。
指定する際は、左上の「⊥」マークから、右下の「T」マークまでの範囲をドラッグします。


この枠の指定が何を意味するかと言うと、マーク読み取りの際、プログラムはまず、上で指定された範囲の「高さ」を行数で割って1行分を切り出し、次に「幅」を選択肢数で割ってマークを1つずつ切り出します。次の図のようなイメージです。


ですので、マークシートを作成する際は、このことを考慮して、各マークの幅と余白が均等になるように作成します。逆に言えば、各マークを上手く切り出せるように選択肢の枠幅を指定する必要があるわけです。

Wordで作成したマークシートの設問01欄の選択肢番号部分を範囲選択すると、
各マークの幅と余白が均等になっているか、どうか、確認することができます。


プログラムは、この切り出したマークの塗りつぶし面積を計算して、それが最大であるものを「マークあり」と判定しています。

各マーク間の余白も重要です。広すぎても、狭すぎても、判定に影響します。広すぎると計算が遅くなり、塗りつぶし面積の閾値の設定が難しくなります。逆に、狭すぎると大きめにマークされた場合、隣までマークしたことになり、複数マークの判定が出やすくなります。選択肢数が多くなると、どうしても各マーク間の余白は狭くなります。大きめにマークされた場合のことを考え、故意にマーク位置を変えて、どれくらいズレに強いか、試行してみました。

ちゃんと読めています!


上の図にある程度のズレであれば、プログラムは正しくマークを読み取れるようです。

もう少し、ズレ幅を大きくしてみました。

「99」は「複数マークあり」を意味します。


さすがに、ここまでズレると判定に影響します。しかし、完全な誤判定でなく、「複数マークあり」という判定なので、読み取り後のチェックで採点者の目視による判断が求められることになります(私はこの結果を見て、安心して使えると判断しました)。

マークシートの作り方については、MS_Readerの操作・設定マニュアルにも詳しい解説があります。オリジナルのマークシートを作られる際は、どうかそちらもご参照いただけますようお願い致します。

マニュアルはPDFです!

4.お願いとお断り

このサイトの内容を利用される場合は、自己責任でお願いします。ここに記載した内容及びダウンロードしたプログラムを利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。

マークシートリーダーを数学用に設定

マークシートリーダー第3弾!
今回は MS_Readerを数学の試験の採点用途に使用するための設定セット を紹介します。
数学用途での採点は、その解答方法から複数選択肢を抱き合わせて採点する設定が必須となります。
MS_Reader.exe単体では、その実現が困難であるため、マークシートの読み取り結果を出力するエクセル・ブックをこの用途向きに専用プログラムで最適化して利用します。
なお、専用エクセル・ブックは大問6個までの数学採点用途に対応しています。

マークシートリーダー第1回の記事はこちらからどうぞ

上の記事内に、無料で使えるマークシートリーダー(MS_Reader.zip)のダウンロードリンクがあります。
重要 MS_Reader の使用はあくまでも自己責任でお願いします。動作保証は一切ありません!

マークシートリーダー第2回の記事はこちらからどうぞ

Python環境を組み込むとマークシートリーダーをさらに高速化できます。
重要 組み込みPython環境のご使用もあくまでも自己責任でお願いします。動作保証は一切ありません!

【もくじ】

1.数学用設定セットのダウンロード
2.数学用設定セットの使い方
3.お願いとお断り

1.数学用設定セットのダウンロード

数学用設定セットは、数学採点用のExcel Bookのマクロ有効テンプレート、このテンプレートから生成したExcel Bookに対して採点設定を書き込む専用プログラム、数学用マークシートのテンプレート3種類(1枚につき大問3個、各問について25設問ア行~カ行~サ行~タ行~ナ行まで設定可能、選択肢は-、±、0~9、a~dの16選択肢 ⇨ これを2枚、大問6まで使用可能)、A3サイズのマークシート画像をA4横サイズの画像2枚に分割・指定フォルダに保存する専用プログラム、練習用フォルダ(2つ)と練習用画像、各プログラムの使い方のPDFファイル等から出来ています。

【数学用設定セットの内容】

数学用設定セットの内容


数学用設定セットは、こちからからダウンロードしてください。なお、ご使用にあたっては同梱の「必ずお読みください_Math.txt」にあります免責事項への同意が必須となります。

2.数学用設定セットの使い方

プログラムの使用順序は、次の通りです。

  1. MS_Reader.exeのバージョンを確認。必要であれば同梱のVersion 1.1.2を古いMS_Reader.exeに上書きします。
  2. ImageCutter.exe、MathFunctionCreator_2024.exeをMS_Reader.exeがあるフォルダにコピーします。
  3. 同梱の数学用採点シート_2024.xltm(マクロ有効テンプレートファイル)をダブルクリックして生成されるマクロが無効な「.xlsx」ファイルに適切な名前を付け、その拡張子を「.xlsm(マクロ有効Book)」に変えて、MS_Reader.exeがあるフォルダ内のeFileフォルダ内に保存してください。同様に「マークシートのテンプレート」フォルダ内のファイルも全てWordのテンプレートです。ダブルクリックすると新しいWord文書が生成されますので、適切な場所に適切な名前を付けて保存し、ご活用ください。
  4. 保存したExcel Bookに試験の受験者のクラス、番号、氏名、ふりがな、性別、試験の正解となる選択肢の番号、配点、観点別評価を入力し、上書き保存します。
  5. MathFunctionCreator_2024.exeを起動し、4.のExcel Bookに対して、抱き合わせ採点の設定を追加します。⇨ 抱き合わせ採点の設定方法は、同梱の「MathFunctionCreator_2024の使い方.pdf」を参照してください。
  6. 同梱のマークシートのテンプレートから作成したマークシートを印刷、試験(試行)を実施(欠席者がいる場合は未使用のマークシートを適切な位置に挿入する等の事後処理を行い)、スキャナーでスキャンして画像化、MS_Reader.exeがあるフォルダ内のScanDataフォルダ内に適切な名前を付けたフォルダを作成し、そこに保存します。
  7. 使用したマークシートがA3サイズである場合は、ImageCutter.exeを起動してマークシート画像をMS_Readerで読み取り可能なA4横サイズの画像2枚に分割します。分割した画像は、MS_Reader.exeがあるフォルダ内のProcDataフォルダ内に自動的に作成されるフォルダに保存します。⇨ A3判画像の分割方法は、同梱の「ImageCutterの使い方.pdf」を参照してください。
  8. MS_Readerでマークシートを読み取り、読み取り結果のチェックを実行後、結果をCSVファイル、及び5.で抱き合わせ採点を設定済みのExcel Bookに書き込みます。
  9. 読み取り結果を書き込んだExcel Bookを開き、成績一覧表及び採点結果通知用の成績個票を印刷します。
  10. 試験の受験者にマークシート及び成績個票を返却し、マークの読み取り結果と成績を本人が確認(マークシートの画像があるので不正は絶対に不可能であることを受験者に周知した上で、マークシートは返却してもOKですが、必要であれば確認作業後、回収して保管。完全に不要となった時点でシュレッダーにかけて処分してください)。採点結果の訂正が必要な場合は、MS_Readerを再起動・マークシート情報を記録したテンプレートを選択・採点対象のマークシートがあるフォルダを選択すると8.で保存したCSVのデータのデータが表示されるので、必要な個所を訂正し、Excel Bookへ再出力、必要な成績個票を印刷して返却し、再確認作業を行ってください。

3.お願いとお断り

このサイトの内容を利用される場合は、自己責任でお願いします。ここに記載した内容及びダウンロードしたプログラムを利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。

マークシートリーダーをP4Dで高速化

マークシートリーダー第2弾!
今回は Python環境を組み込んで、マークの読み取り速度を高速化 します。
出来る限り丁寧に組み込み方法を説明しますので、どうか最後までお付き合いください。

前回の記事はこちらからどうぞ

前回の記事に、無料で使えるマークシートリーダー(MS_Reader.zip)のダウンロードリンクがあります。
重要 MS_Reader の使用はあくまでも自己責任でお願いします。動作保証は一切ありません!

【追記_P4D環境で読み取り実行時、エラーが発生するときは?】

Python環境を組み込んで、これを利用してマークシートの読み取りを実行する場合、次のエラーが発生することがあります。エラーの内容からは推測すると、エラーはテンプレートマッチングの際に利用するテンプレート画像のサイズに起因して起きているように見えますが、ほんとうの原因は違います。

このマークシートリーダーは、Python環境を利用して動作する際は、マークの有無を読み取るJpeg画像の名称(及びフォルダの階層)が次の規則に従っていることを前提としています。

ProcData\XXX\Sample-01.jpg
ProcData\XXX\Sample-02.jpg
ProcData\XXX\Sample-03.jpg
・・・
ProcData\XXX\Sample-40.jpg
ProcData\XXX\Sample-41.jpg

この命名規則にJpeg画像のファイル名(及びフォルダの階層)が従っていない場合、読み取りエラーが発生します。例えば、次のような場合です。

ProcData\XXX\Sample-01a.jpg
ProcData\XXX\Sample-01b.jpg
ProcData\XXX\Sample-02a.jpg
ProcData\XXX\Sample-02b.jpg
・・・
ProcData\XXX\Sample-40a.jpg
ProcData\XXX\Sample-40b.jpg
ProcData\XXX\Sample-41a.jpg
ProcData\XXX\Sample-41b.jpg

特に数学(や情報)用途で2枚1セットのJpeg画像を処理する際は、注意してください。このエラーを防止するには、ファイルメニューの「1 画像変換」⇨「専用画像を作成」を利用してファイル名が必ず連番になるように読み取り専用Jpeg画像を生成して、この画像に対して、マークの読み取りを実行してください。

以下、発生するエラーメッセージの一覧です。

このメッセージは2回表示されます


なお、Python環境を利用しないモード(P4Dを使用のチェックボックスをOFF:下図右上)であれば、読み取り対象Jpeg画像ファイルの名称は動作に関係しないので(読み取り速度は低下しますが)、読み取り可能です。

画面右上の □ P4Dを使用のチェックを外して、Delphi用のOpenCVで読み取りを実行。
読み取り速度は低下しますが、マークを正しく読み取っています。


【読み取り実行前に、選択肢の始まり番号も指定してください】

選択肢の番号は、デフォルト1始まりに設定してあります。教科「情報」用途で読み取りを実行する場合は、読み取り実行前に、選択肢が「1」始まりであるのか、「0」始まりであるのか、その指定を画面上の設定欄で必ず指定してください。

【もくじ】

1.Python環境を準備する組み込み用Pythonのダウンロードリンクがあります
2.Python環境のドッキング
3.高速化の確認
4.システムにC++ランタイムライブラリがない場合は?
5.Python Engine の初期化の問題?他
6.まとめ
7.お願いとお断り

1.Python環境を準備する

Qiita の記事で「 Embeddable Python 」なるものの存在を知り、ほぼ同時に Delphi に Python のスクリプトを埋め込んで、VCL で GUI を作成、内部的に Python のスクリプトを実行する方法を学びました。

この辺の詳しい経緯は、かなり前に記事として書いた通りです。

2022/01/01
2022/01/02


こうして出来上がった、マークシート読み取りに必要なライブラリだけをインストールした、組み込み用のPython環境の内容は、こんな感じです(組み込み用途に作成した Embeddable Python があるフォルダをコマンドプロンプトで開き、「 Python -m pip list 」コマンドを実行した結果です)

ライブラリの主役は Numpy と OpenCV-Python。
Pillow は、日本語を含む Path を読むためにインストール。


最初に用意した Embeddable Python が14MBくらいで(おー!ちいさい☆)と喜んだけど、上記のライブラリを三つ入れたら 158MB に・・・。

ライブラリを構成しているファイルの依存関係がわかれば、必要ないファイルを消しまくって、もっと小さく出来ると思うのですが・・・、その具体的な方法がわかりません!!

仕方がないので、そのまま組み込み用の「Python39-32」フォルダを作成。

フォルダ名の Python は、「Python関連のフォルダだよ!」ってコトが一目でわかるように工夫(?)しました。その次の 39 はVersion番号、ハイフンで繋いだ 32 は 32bit 用って意味です。

これを前回紹介したマークシートリーダーにドッキングさせます。

展開に少々時間がかかりますが、もし、よかったら使ってください。
MS_Reader 組み込み用 Embeddable Python です。

2.Python環境のドッキング

ダウンロードした「Python39-32.zip」を MS_Reader.exe のあるフォルダにコピー・貼り付け、展開してください。※ 動作確認が完了したら「Python39-32.zip」は削除しても OK です!

【展開前】

MS_Reader.exe とダウンロードした Python39-32.zip を同じ階層に置き、
zipファイルを展開(右クリックして「すべて展開」を選択)してください。
展開にはしばらく(1~2分)時間がかかります。

展開時のPC環境?によっては「ものすごく(20~30分)」時間がかかることが実際にありました!!(原因はわかりませんが、時間がかかるだけで、展開そのものは正しく行われました)

【展開後】

重要 MS_Reader.exe と Python39-32 フォルダは同じ階層に置いてください。

MS_Reader.exe と Python39-32 フォルダは必ず同じ階層に置いてください。


ここで念のため「Python39-32」フォルダの構造を必ず確認してください。

〇:Pathに注目してください。これならOK!

MS_Reader\Python39-32\Lib であり、また、
MS_Reader\Python39-32\Scripts であります。

これはダメです。Pathが二重になってます。

MS_Reader\Python39-32\Python39-32\Lib
MS_Reader\Python39-32\Python39-32\Scripts

上の「ダメな例のようにならない」ようにPython39-32.zipを作成しましたから、大丈夫だと思いますが・・・念のため、必ずご確認いただけますようお願いいたします。

以上が『 ドッキング作業 』です!!

MS_Reader.exe と同じフォルダに、Python39-32.zip をコピペして、展開すれば Python環境のドッキングは完了です。

これを夢見て、ンか月。マジ、挫けそうな時もあった・・・ けど。

MS_Reader.exe をダブルクリックして、マークシートリーダーを起動してみてください。

僕のマークシートリーダーは、自動的に、高速動作モードで、起動します。

3.高速化の確認

Python環境がないと(MS_Reader.exe がある場所に Python39-32 フォルダがない場合)・・・

MS_Reader 起動時、マークシートの読み取りを高速化するP4D(PythonForDelphi)モードは利用できませんが、

Python環境があれば(MS_Reader.exe がある場所に Python39-32 フォルダがある場合)・・・

マークシートの読み取りを高速化するP4D(PythonForDelphi)モードを利用する状態で、MS_Reader は起動します。

当たり前ですが、ダミー(中が空っぽ)の「Python39-32」フォルダを作成し、設定を偽ってMS_Readerを起動しても、メリットは何一つありません!

エラーが2つ出るだけです。

実際に、空の「 Python39-32 」フォルダを作成して実験してみました!


もう一つ。


こんなコトする方は皆無と思いますが。あくまでも、プログラムの動作検証として、ご参考まで。

【動作確認】

前回、設定したテンプレートを利用して動作確認します。

いったん、「P4Dを使用」のチェックを外して読み取りを実行します。前回試行した3列25行8選択肢の1枚あたり600マークあるシート3枚の読み取りにかかる時間は・・・


1枚0.805秒で読んでます(PC環境により、数値は当然異なります)が・・・

「P4Dを使用」のチェックを ON にして再び読み取りを実行します。私の PC での結果は・・・


1枚0.245秒強で読みました。

これが速度的に「はやい」か・どうか、このソフトウェアをお使いいただく方により、その判断基準は異なりますから、その思い(感じ方)は違って当然ですが、Python環境を利用しない場合に比較して、Python環境を組み込み、これを利用した場合は(PC環境により、その数値は悉く異なると思われますが)マークの読み取り速度は間違いなく高速化されるはずです(僕の環境では、「それがない」場合に比較して、「それがある」場合は3.3倍速で動作しました)。

ただ、Python環境を組み込んだ場合、プログラム全体の大きさは、12倍以上に巨大化します・・・

プログラムサイズを選ぶか、動作速度を優先するか、
ご使用目的、お使いのPC環境に合わせて選択していただけたら幸いです。

僕は・・・

今日の空みたいな・・・

プログラムを書きたかった・・・だけです *(^_^)*♪

僕が、この世から消えたあとも、動く。

いつか、夢みたとおりの・・・ プログラムを。

だいすきな・・・

大好きな Delphi と・・・

僕の Object Pascal で。

4.システムに Visual C++ランタイムライブラリがない場合は?

お使いのシステムに Visual C++ランタイムライブラリがインストールされていない場合は、MS_Reader 起動時に次のエラーが発生します。

『アプリケーションを正しく初期化できませんでした(0xc0150002)。「OK」をクリックしてアプリケーションを終了してください。』

英文の場合もあるようです。

PCの解像度の関係だと思うのですが、画像がボケていてごめんなさい!


このエラーが発生する原因を調べてみたところ、組み込みPython環境内にある「Python39.dll」が Visual C++ランタイムライブラリを必要とするようで、これがシステムにない場合は、プログラム起動時にバックグラウンドで行っているPython Engine の初期化に失敗して、上記のエラーメッセージが表示されることがわかりました。

お使いのPCで、Visual C++ ランタイム ライブラリのインストール状況を確認するには、[スタート] ボタンを右クリックし、「ファイル名を指定して実行」をクリックして、appwiz.cpl と入力して[Enter]を押します。Python環境を組み込んだ MS_Reader が動作する環境であれば、システムにインストールされている Microsoft Visual C++ ランタイム ライブラリが以下のように表示されるはずです。

現在、私のシステム(Windows 11 Pro 23H2)にインストールされているC++ランタイムライブラリの一覧。
もちろん、このシステムでPython環境を組み込んだマークシートリーダーが正常に動作しています。


システム内で起きていた別のエラーを解決するために、2023年12月上旬に工場出荷状態に戻すリカバリ作業を行いました。同時にOSを最新のバージョンに更新しました。それ以前のシステムの状態は次の通りです(OS のバージョンは 22H2)。※ 私のPCでの話です。

現在の状況とは異なっています。
この状態でもPython環境を組み込んだマークシートリーダーは正常に動作していました。


エラーを解決するには、Visual C++ランタイムライブラリをインストールすればいいわけですが、上の例のように Visual C++ ランタイムはたくさんあるので、手動でひとつひとつダウンロードしてインストールするより、Visual C++ ランタイムインストーラーを使って全ての Visual C++ ランタイムを一括インストールする方が簡単です。

システムをリカバリする前は、次のようにして Visual C++ ランタイムをインストールしていました。

【ご注意願います!】
ここで紹介する方法で Visual C++ ランタイムをインストールする場合、他のプログラムの実行環境との整合性は、一切保証できません。また、最悪の場合、Windowsが起動しなくなるトラブルが発生することも十分に考えられます。インストール作業の全てが自己責任であることを十分ご理解の上、重大な問題が発生した場合は元の環境に戻せるよう、システムのバックアップを取る・現在の設定をメモに記録する等、不具合の発生に備え、必要かつ十分な準備を整えた上で、Visual C++ ランタイムのインストールを行ってください。

以下のサイトから「Visual C++ v56.exe」をダウンロードしてインストール(私の環境にインストールする分には、なんの問題も起きませんでした。もちろん、マークシートリーダーも問題なく起動し、安定動作しました)。

Visual C++ Runtime Installer (All-In-One) v56

https://www.majorgeeks.com/files/details/visual_c_runtime_installer.html

こちらのWebサイトでも(次のリンク先Webページの下の方で)、このインストーラが紹介されています。

Microsoft Visual C ++ 再頒布可能ファイルを削除して再インストールする方法

https://www.autodesk.co.jp/support/technical/article/caas/sfdcarticles/sfdcarticles/JPN/How-to-remove-and-reinstall-Microsoft-Visual-C-Runtime-Libraries.html

インストーラーを立ち上げると、本当にインストールするかどうかを「YES」か「No」かで尋ねられるので、インストールする場合は「Y」をタイプします。その後はPCの画面に表示される英文の指示にしたがって操作してください。

ここから先は、上記のインストーラーを用いて Visual C++ ランタイムをインストールした際、私が実際に経験したトラブル?です(最終的にインストールは成功しました)。

お決まりのUAC起動後(PCの設定によっては)管理者ID 及びパスワードの入力が求められますが、これを入力すると、そのままPCがフリーズしたような状態になり、数分待機しても進展が見られないので、いったん作業を Ctrl+Alt+Delete でキャンセルし、再度、「Visual C++ v56.exe」を起動して Visual C++ ランタイムのインストール作業を実行、今度はトラブルなくインストールに成功する事例です。これは「ある特定のAD環境下にあるPCのすべてに共通して見られた」現象です。現在もその原因はわかりませんが、ご参考まで。

また、システムの状態によっては(現在システムにあるランタイムをアンインストールしているのか?)複数回(と言っても最高2回ですが)、再起動を求められることも(何度も)経験しました。

C++ランタイムライブラリのインストールについて、経験を加味して私がわかるのはここまでです(実は、何もわかってないのとイコールなのですが)。これ以外のエラーメッセージが表示されてインストーラーが起動しない場合も、もしかしたらあり得るかもしれません。大変恐縮ですが、そのような場合は原因の究明を含めて、自己責任でご対応ください。

5.Python Engine の初期化の問題?他

MS_Reader では、マーク読み取り時の体感速度を上げるため、FormCreate時にバックグラウンドで Python Engine の初期化を行っています。MS_Reader.exe のあるフォルダに小さなマークシートの画像とマーカー画像があるのにお気づきになった方がいらっしゃるかもしれません。これは Python Engine 初期化用に用意した画像です。

Python Engine 初期化用の画像をリソースに埋め込み、もし、それがない場合は再生して、
プログラム起動時に Python Engine の初期化が必ず行われるようにしています。


この初期化を「するか・しないか」で、MS_Reader 起動後、初めてマークを「読む」ボタンをクリックした際のプログラムの挙動がまるで違ったものになります。初期化を行った場合は、ごくスムーズにマーク読み取りが始まるのに対し、行わなかった場合は PC が一瞬フリーズしたような状態になり、その後、息を吹き返すかのようにマークの読み取りが始まります。

Python Engine の初期化コードです。

  AppDataDir:=ExtractFilePath(Application.ExeName)+'Python39-32';

  if DirectoryExists(AppDataDir) then
  begin
    //フォルダが存在したときの処理
    CheckPython.Enabled:=True;
    CheckPython.Checked:=True;
    PythonEngine1.AutoLoad:=True;
    PythonEngine1.IO:=PythonGUIInputOutput1;
    PythonEngine1.DllPath:=AppDataDir;
    PythonEngine1.SetPythonHome(PythonEngine1.DllPath);
    PythonEngine1.LoadDll;
    PythonDelphiVar1.Engine:=PythonEngine1;
    PythonDelphiVar1.VarName:=AnsiString('var1');
    PythonEngine1.Py_Initialize;
    //イニシャライズされたことを記憶
    P4D_ini:=True;
  end else begin
    CheckPython.Checked:=False;
    CheckPython.Enabled:=False;
    PythonEngine1.AutoLoad:=False;
    P4D_ini:=False;
  end;

(どこに問題があるのでしょうか?)

PC によっては、この Python Engine の初期化に非常に長い時間を要することがあるようです(エラーメッセージは出ません。この沈黙の時間が終わった後、プログラムは問題なく動作します)。偶然、ある PC でこの現象に巡り合い、あわてて時間を計ってみたところ、その PC では初期化に4分必要でした! なぜ、このような現象が発生するのか、その理由がわからないのですが、「そのようなことがある」ことだけは経験的に明らかですので、ここに書いておくことにしました。

また、マーク読み取り開始時に、マーカー画像の位置をテンプレートマッチングで確認して、それが「本当に見えている」ことをユーザーに明示的に知らせていますが、ここでもその処理に少し時間が必要なことがあります。私のPCでも、この現象は「起きたり・起きなかったり」するような気が・・・。エラーが出るわけでもなく、ただ・・・「ん?」みたいな時間があるだけなのですが・・・。こちらもその原因がよくわかりません。

以上が、現象としてはわかっているのですが、原因が解明できていないPython環境を使う上での問題点です。

それから、私の想定外の操作が行われた場合、メモリーリークが起きる可能性があります。Python環境をドッキングさせた当初は、このメモリーリークにかなり悩まされました。どう頑張っても小さなメモリーリークが発生するのを取り切れず、( Python環境はそういうもの? )と割り切ってしまおうかと思ったこともあったくらいです。

そのたびに思い直し、メモリーリークが発生する原因を突き止めて対応することを繰り返しました。なので、私が想定した操作範囲内でのメモリーリークは全て取り切れたと思います。が、もし、それが発生した場合は、その発生を知らせるメッセージがプログラム終了直後に表示されます( FormCreate時に実行されるコードの中にメモリーリークがあれば検出するコードを残してあります)。

  //メモリーリークがあれば検出
  ReportMemoryLeaksOnShutdown:=True;
メモリーリークが起きたことを伝えるメッセージ
(上の例のメモリーリークは故意に発生させたものです)

6.まとめ

(1)Python環境を利用するとマークシートリーダーは高速化できる。
(2)高速化できるかわりに、プログラム全体のサイズは大きくなる。
(3)原因不明のフリーズのような現象が発生することがある。

7.お願いとお断り

このサイトの内容を利用される場合は、自己責任でお願いします。ここに記載した内容及びダウンロードしたプログラムを利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。

マークシートリーダー

自分的に必要と思った機能は全部搭載しました・・・が、
プロが作った有償販売できるレベルのソフトウェアではありません。
見た目も、使い勝手も、よくないと思います。
もちろん、無料でお使いただけますが、サポートも、動作保証もありません。
ダウンロードから設定まで、ALL自己責任でお願いします。

快適と感じる速度で動作させるには、かなり高性能なCPU搭載のマシンが必要です。
私のプログラミング技術が足りない部分を、CPUパワーでカバーしてもらってます。
マシンによっては、読み取り結果のチェック等がかなりトロいかもですが・・・

それでも、もし、よかったら使ってください。
Delphiで作ったマークシートリーダーです。


参考 後日、別途ご案内するPython環境を用意していただくと、より高速に動作します!
参考 Python環境で動かす場合、Python Engineの初期化に4~5分かかることがあります・・・

ここで紹介しているマークシートリーダーを用いて読み取った結果をCSVファイルに出力し、これをもとに「採点結果を試験の受験者に通知するシート」及び「試験の成績一覧表」を(表計算ソフトを使わずに)作成できるプログラムです。次のリンク先からダウンロードできます。

リンク先からダウンロードできるマークシートリーダーは、消音機能を追加したバージョンアップ版です。

【使い方のご案内】

1.デスクトップにMS_Reader.zipを展開(解凍)
2.高解像度ディスプレイへの対応
3.マークシート画像の読み取り準備
4.テンプレートを作成
5.マークの読み取りを実行
6.読み取り結果のチェック
7.CSVファイルへの書き出し
8.Excel Book の準備作業
9.Excel Book への書き出し
10.マークシート印刷用紙について
11.まとめ
12.お願いとお断り

どんな環境でも、100%動作する保証はできません・・・が、
私と同じ環境・条件を揃えていただければ、きっと動くと思います。

使用したPC及びOS、開発環境は、次の通りです。

・プロセッサ 11th Gen Intel(R) Core(TM) i7-1185G7 3.00 GHz
・実装 RAM 32.0 GB

・Windows 11 Pro 64ビット版
・バージョン 23H2
・OS ビルド 22631.2861

・Embarcadero® Delphi 12 バージョン 29.0.50491.5718

・設計時の画面解像度は「1366 × 768」です。これ以上の解像度でお使いください。

使い方をなるべく丁寧に説明しますので(マニュアルも同梱してありますが)、まず、ここに書かれている順番で、一通り操作してみていただけたら幸いです。

1.デスクトップにMS_Reader.zipを展開(解凍)

ダウンロードした MS_Reader.zip をお使いのPCのデスクトップにコピペして右クリックするとサブメニューが表示されます。この中の「すべて展開」をクリックしてください。

デスクトップに MS_Reader.zip を展開します。


無事、展開に成功したら、MS_Readerフォルダをダブルクリックして開きます。

フォルダ内に展開されたファイルの中に MS_Reader.exe があります。これをダブルクリックしてマークシートリーダーを起動します。


次のメッセージが表示された場合は、「詳細情報」(画像中、赤い枠で囲んで示した部分)をクリックします(プログラムの発行元が不明である場合に、Windows のDefender機能である SmartScreen がこの表示を出すそうです。自分の責任で実行すれば、次回からこのメッセージは表示されなくなります)。

「詳細情報」をクリックします。


すると、次の画面が表示されます。「実行」(画像中、赤い枠で囲んで示した部分)をクリックしてMS_Readerを起動してください。

「実行」をクリックします。

2.高解像度ディスプレイへの対応

高解像度ディスプレイをお使いの場合の対応方法です。高解像度ディスプレイをお使いの場合、設定から「システム」⇨「ディスプレイ」と順にクリックすると、次のように表示されると思います。

拡大縮小に「150~200」という値が設定されていれば、高解像度ディスプレイです。


この場合、起動したマークシートリーダーの画面が小さくて見えにくいと感じることがあるかもしれません。その場合は、次のように操作してください。

MS_Reader.exe を右クリックして、表示されるサブメニューのプロパティをクリックします。

MS_Reader.exe のプロパティを表示します。


「互換性」タブをクリックします。


高DPI設定の変更をクリックします。


「高いDPIスケールの動作を上書きします。」にチェックを入れて、「拡大縮小の実行元:」は「システム」をComboBoxの選択肢から選択して指定。OKボタンをクリックします。


続けて「適用」⇨「OK」とクリックして設定は終了です。これで画面が見やすい大きさで表示されるようになります。

3.マークシート画像の読み取り準備

デスクトップに展開した MS_Reader フォルダ内に「ScanData」フォルダがあります。この中に練習用のサンプル画像が2種類(解像度150dpiと200dpi)入っています。この画像を用いて説明します。

重要 マークシートは、解像度150~200dpiでスキャンしてください。

重要 1回の操作で読み取り可能な枚数は最大99枚です。

MS_Reader.exe をダブルクリックしてマークシートリーダーを起動したら、画面左上の「画像変換」をクリックし、表示されるメニューの「専用画像を作成」をクリックします。


画像変換用のWindowが表示されたら、画面右上の「選択」ボタンをクリックします。


「フォルダの選択」ダイアログが表示されます。ここでは「Scanner_A」フォルダを選択します。フォルダ名をクリックして、下のFolder欄に「Scanner_A」と表示されたことを確認し、「OK」をクリックします。

スキャンしたマークシート画像は「ScanData」内に適切な名前を付けたフォルダを作成し、必ずその中に保存してください!

重要 フォルダ名にハイフン(-)を使わないでください。

参考 フォルダ名には、文字の他、アンダースコア(_)が使用できます。

注意してください。選択するのは「フォルダ」で、「ファイル」ではありません。
(Scanner_Aをダブルクリックして開いても何も表示されません)


画面は、次のようになります。赤い枠で囲んだ部分にマークシート画像のサムネイルが表示されます。回転の必要性の有無と回転方向を確認してください。


この場合は、回転の必要性「有り」で、回転方向は左90°です。これを「画像の回転」のオプションボタンをクリックして指定します。

左90°のオプションボタンをクリックします。


必要であれば、次に画像のリサイズ指定を行います。リサイズを指定「する・しない」の判定基準は、スキャナーでマークシート画像をスキャンした際の解像度の数値で判断してください。

「Scanner_A」フォルダ内のマークシート画像は、ScanSnap iX1500 のノーマルモードでスキャンした画像で、その解像度は 150dpi です。この場合は、ちょうどよい大きさでマークシート画像が表示されますので、画像をリサイズする必要はありません。

重要 解像度150dpi ・A4横置きの場合、リサイズは必要ありません!

重要 解像度200dpi ・A4横置き・解答マーク欄4列の場合、80%の大きさにリサイズしてください。読み取り後のチェックまで含めて、作業しやすくなります。

マークシート画像の読み取り解像度が 200dpi でも、マークシートがA4横置き、解答マーク欄の列数が3列の場合、リサイズは必要ありません

また、A4以外の大きさのマークシートは使ったことがありません!
(用紙の左上にマーカー画像■■■を入れ、その他はここでダウンロードできるサンプルと同様に作成していただければ、用紙サイズに関係なく動作すると思いますが、試行したことがありませんので確かなことは言えません。ただ、画像のサイズが大きくなればなるほど、動作速度は間違いなく低下します。また、複合機のスキャナーを用いて、マークシートを画像化する際も、B4やA3の大きさだと私が使用している機材ではメモリがいっぱいになるのでしょうか? 30枚程度読み込んだあたりで一旦動作が停止します。数百枚単位での読み取りにはそれなりに時間がかかります。そのような理由から、マークシートに使う紙の大きさはA4サイズ以下が適切だと思います。)

参考:プログラムを書いた本人が言うのもナンですが、自動でのリサイズはおまけ程度にお考えください。
ScanDataフォルダのScanner_Bフォルダに保存されたサンプル画像の大きさは、2338 × 1653
これを自動リサイズオプションボタンを指定して、変換してみます。
ProcDataフォルダのScanner_Bフォルダに保存されたサンプル画像の大きさは、1760 × 1248
いちおう、これでマークシート画像が横方向のはみ出し「なし」で表示されました。

重要 画像のリサイズの有無を必ずメモ(記録)してください!

⇨ 複数クラスのマークシート読み取り時に、同じ設定を適用する必要があります。

重要 大きな解像度の画像を扱う場合、動作速度が大幅に低下します!

回転の有無と方向、リサイズの有無を指定したら、画面中央右にある「参照」ボタンをクリックして、保存先のフォルダを選択します。


「フォルダの選択」ダイアログが開きます。Pathを見ると「ProcData」フォルダが指定されていることがわかると思います。なお、Procは「Processed(加工済み)」という意味です。

プログラムは「ScanData」フォルダで指定したフォルダと同名のフォルダを「ProcData」フォルダに自動作成します。この自動作成されたフォルダをクリックして選択します(しつこいようですが、選択するのは「フォルダ」で、「ファイル」ではありません)。下のFolder欄に「Scanner_A」と表示されたことを確認し、「OK」をクリックします。

読み取り用のマークシート画像は、必ず「ProcData」内の自動作成されたフォルダに保存してください!

重要 ProcData以外のフォルダには画像を保存しないでください。

読み取り用画像を保存するフォルダは自動で作成されます!
(自動作成されたフォルダをクリックして選択してください)


「変換実行」をクリックします。

回転とリサイズの有無を指定して「変換実行」をクリック!


次に表示される案内メッセージには「いいえ」を選択してください。


このマークシートリーダーとは別に、手書き答案の採点プログラムを作成しました(準備が整い次第、公開する予定です)。このマークシートリーダーは、そちらと連動しての動作も可能な設計にしてあるため、このメッセージが表示されます。

画像の変換が完了すると、メッセージが表示されますので、OKをクリックします。


変換された読み取り専用画像のサムネイルが表示されます。作成された読み取り用の画像ファイルには連番の名前が自動的に付きます(自動生成されたファイル名は変更しないでください)。

重要 Python環境を利用する場合はファイル名は必ず連番にしてください。

画像処理のアルゴリズムは、GDI+を利用しています。画像の回転とリサイズが伴う場合は、変換に時間がかかります。処理が完了するまでお待ちください。

(後日、別途ご案内する予定の)手書き答案の採点プログラムと併用する場合は、採点やり直しのために必要な画像もここで作成します(Loopが二重にまわり、時間も2倍かかります)。

クラス別に処理する場合は、「画面の初期化」ボタンをクリックします。
変換元フォルダの選択から、画像の変換処理を再実行できます。

画像の変換処理が完了したら、「終了」ボタンをクリックして、この画面を閉じます。

参考:画像を変換する理由は以下の3つです!
(1)Jpeg画像のサイズを最適化するため(全体が画面内に収まるようリサイズしてください)。
(2)画像の名前が連番になるよう、自動的にリネームするため。
(3)証拠画像としてのオリジナルを残したまま、読み取りに最適な大きさの画像を生成するため。

4.テンプレートを作成

次に、マークシートの情報を記録した読み取り用のテンプレートを作成します。これを作成することにより、同じ採点を複数クラスに対して実行したり、設定(縮小処理の有無を含む)が同じマークシートを異なる考査での使いまわしが可能となる・・・

・・・ように設計したのですが、実際には使いまわしがなんとなく不安なので、考査毎にテンプレートを再生成して運用しています。ですので、同じ設定(大きさ)のマークシート画像の情報を記録したテンプレートの使いまわしが可能か・どうか、これについては未確認です。

「確実なマークシート読み取りを実行する」ためには、お手数をおかけしますが、試験ごとに使用したマークシートのテンプレートを作成していただくのが最良の方法であると思います。

メニューの「2 テンプレート」をクリックして表示されるサブメニューの「テンプレートの新規登録」をクリックしてください。別のWindowが開きます。


画面右上の「取得」ボタンをクリックします。


今度は「ファイルを選択」するダイアログが表示されます。任意のマークシート画像を選んでください(1番のファイルを選ぶ方が多いのではないでしょうか?)。ファイルをクリックしてファイル名を取得し、「開く」をクリックします。

マークシート画像のサムネイルをクリックするとファイル名が取得できます。


画面は次のようになります。

このプログラムでは、マーカー(特徴点)画像を利用してマークシートのマーク位置を計算しています。ですので、このプログラムで処理するマークシートには必ずマーカー(特徴点)画像が必要です。

重要 マークシート左上にマーカー画像(■■■)を必ず用意します。

重要 マーカー画像は、マークシート1枚に1つだけ用意します。

画面右の操作パネル上段にある「マーカー」オプションボタンをクリックして選択状態にします。

「選択対象」の「マーカー」オプションボタンをクリックしてください。


マークシートの画像が拡大表示され、マウスのカーソルが大きな「+」になります。

マーカー画像の「左上」をクリックし、ボタンを押したまま「右下」へドラッグしてください。画像上には点線のラバーバンドが表示されます。

マーカー画像の左上を左クリックして、マウスの左ボタンを押したまま、マーカー画像の右下へドラッグ。
点線のラバーバンドでマーカー画像が囲まれます。


ドラッグ中の画像です(わかりやすさのため、マーカー画像より大きめにドラッグしています)。

黒点線がラバーバンドを示します。


マーカー画像の座標を正しく取得できる例です。

マーカー画像とラバーバンドがぴったり重なるようドラッグしてください。


マウスの左ボタンから指を離すと、取得できたマーカー画像が画面右側に表示されます。

数値は、画像左上からの距離です。

マークの読み取り時、プログラムは、コンピュータの眼である「OpenCV」のテンプレートマッチングの機能を利用して、まず、最初にマークシート画像中にあるこのマーカー(特徴点)画像を探し出します(これはマークシート画像1枚1枚について必ず行います)。

次に、マーカー(特徴点)画像左上隅を原点(0,0)として、テンプレートに記録されたマーク欄の座標からマーク一つ一つの位置を割り出して、これを切り抜いて画像化(正確に言うと、マークの切り抜き処理前に、ボカシ・二値化・白黒反転の各処理を行い、マークの切り抜き後に白面積計算処理を行って)、マークの有無を判定しています。

この方式の利点は、印刷そのものが左右にズレでも、マーカー画像と解答欄の相対的な位置関係は一定で変わりませんから、印刷がズレすぎて解答欄が印刷されなかった場合以外は、必ずマークの位置を探し出せる(=マークの有無を判定できる)ことです。

事実、輪転機で印刷(非推奨ですが!)して、チェックから漏れた(チェックしなかった?)、正しい位置から印刷が5cmくらいズレたマークシートも、このプログラムでなんの問題もなく読み取れました・・・。印刷のズレを申告せず、そのまま解答して提出する受験者も受験者ですが・・・。A4横・4列のシートで、解答には3列めまでしか使わなかったから「4列めはなくてもOK! 大丈夫」と思ったのでしょうか? それともただ単にめんどくさかったのでしょうか? たぶん、後者だと思いますが・・・

次は、そのテンプレートマッチングの機能をテストします。画面右にある「マーカー画像の読み取りテスト」ボタンをクリックしてください。テンプレートマッチングが正しく実行されると、マーカー(特徴点)画像が太い赤枠で囲まれます。


表示されるメッセージをお読みいただき、「OK」をクリックしてメッセージを閉じてください。


結果が良好であれば「選択対象」グループの「解答欄」をクリックします。


次に、マークシートのマーク(解答)欄の「行数」と「列数」及び「選択肢の数」を指定します。


マークシートの列数・行数・選択肢数の数え方は次の通りです(Scanner_Aフォルダにあるマークシート画像は、A4横置き・3列・25行・8選択肢の形式です)。


ですので、これを次のように設定します。


ComboBox に正しく設定を入力したら、その下の「採点方法の設定」の座標「1列」のオプションボタンをクリックして選択状態にします。マウスのカーソルが大きな「+」になります。


第1列目のマーク(解答)欄の座標を取得します。マーカー(特徴点)画像の時と同様、第1列の枠のうち、設問番号欄の矩形を除いた選択肢のマークが印刷されている欄の矩形の左上隅を(左)クリックして、そのままボタンを離さずに、枠の右下隅へドラッグします。この作業は正確に、慎重に行ってください。この作業の良し悪しでマークの読み取りの可否が決まります。

極めて重要 設問番号欄を含めて指定してはいけません!

極めて重要 指定するのはマーク欄のみ!

プログラムは、ここで取得した座標値(矩形の高さ)を行数で割り算して列を設問毎1行ずつに切り出し、さらに切り出した1行を選択肢数で割って1つ1つのマークを切り出し、その塗りつぶし面積を計算して、マークの有無を判定しています。

マーク(解答)欄の枠線と、表示されるラバーバンドがぴったり重なるようにドラッグしてください。

※ 下図は2つともドラッグ直後の結果を示しています(〇はドラッグ開始点と終了点です)。

マーク欄第1列めの左上隅を(左)クリックしてそのまま指を離さずに右下隅へドラッグ


ドラッグ中は、黒点線のラバーバンドが表示されます。これを目安に位置決めを行ってください、

画像中の〇印の位置までドラッグします。


指を離すと、ドラッグした範囲が赤い矩形で囲まれます。画面右側に取得できた座標が表示されます。


「再範囲選択」ボタンをクリックして、座標の取得をやり直すこともできます。


1列目が済んだら、同様にして2列目の座標を取得します。この作業を「マークシートの列数」分だけ繰り返します。

すべての列の座標を取得できたら、「保存」ボタンをクリックして取得した座標を保存します。


「保存」処理が完了すると、次のメッセージが表示されます。

参考:テンプレートの名前について
例 N_R25C03S08
N:ノーマル(通常の大きさ:解像度150~200dpi)
R:Row(行数)は25行
C:Col(列数)は3列
S:Selection(選択)は8個


「二値化テストの実行」ボタンをクリックすると、第1列めを「平滑化(ぼかし)処理&白黒反転して二値化」した画像の状態が確認できます。「マークあり」の部分が白く表示されていればOKです!
(プログラムは、この白部分の面積を計算して、マークの有無を判定しています)

「終了」ボタンをクリックして画面を閉じ、マーク(解答)欄座標の取得作業を終了します。

二値化テストを実行した場合は、終了ボタンをクリックする前に、保存ボタンをクリックすることを忘れないでください!

二値化の閾値と平滑化(ぼかし処理)のパラメータは、まずデフォルト設定でお試しください。

5.マークの読み取りを実行

これでマークを読む準備ができました。メニューの「テンプレート」をクリックし、表示されるサブメニューの「テンプレートの選択」をクリックします。


次のように、テンプレートを選択するWindowが表示されます。マークシートの形式に合ったテンプレートをクリックして選択し、決定をクリックします。

シングル/ダブルとあるのは、数学や教科「情報」のテストで、マークシート2枚1セットの採点を行うための設定です。選択肢数が16のマークシートを選ぶと、この設定も選択できるようになります(選択肢数が16未満のマークシートでは、この設定は利用できません)。

数学及び教科「情報」用の設定は、後日別記事として掲載する予定です。


次のメッセージが表示されます。「はい」をクリックしてください。


マークの読み取りを実行したいマークシート画像のあるフォルダを選択し、「Ok」ボタンをクリックしてください。


保存してあるマーカー(特徴点)画像をもとに、自動的にテンプレートマッチングが行われ、見つかったマーカー(特徴点)画像から、マークシートのマーク(解答)欄第1列第1行目の座標が計算され、それぞれが赤い矩形で囲まれて表示されることを確認してください。

Python環境を利用する場合(ここでワンクッション置くような感じで)テンプレートマッチングにしばらく時間がかかることがあります。同じプログラムを走らせているのですが、PCにより、このフリーズしたような時間の長さが極端に違うようです・・・、その辺の理由が私にはさっぱりわかりませんが・・・。

Python環境利用時に、この画面が表示されるまで、フリーズしたようになることがあります!


ここまでの設定操作が順調に進行していれば(抜け・落ち・欠けがなければ)、間違いなくテンプレートマッチングが成功し、マーカーと1列1行目が赤い矩形で囲まれるはずです。次のメッセージが表示されますので、お読みになったら「OK」ボタンをクリックしてください。


「読む」ボタンをクリックすると、マークシートの読み取りがスタートします。


画面下部の StringGrid に読み取り結果がリアルタイムで表示されます。また、読み取り完了後、処理にかかった時間が画面左下に表示されます。


8選択肢・25行・3列だから、合計600マーク ×3枚=1800マークの読み取りで、早ければ2013ミリ秒、遅くて2467ミリ秒で読んでます(PCの性能により、この値は変わります)。


遅かった方で1マークあたりの読み取り時間を計算すると、

2.467秒 ÷3≒ 0.82秒/枚
0.82 ÷ 600 ≒ 1.4ミリ秒/1マーク

そう書くと、すごく早いような気がしますが・・・

600マーク3枚で2.5秒だから、30枚ならその10倍で25秒かかります。平均的な高校の1学年分の生徒数を1学年8クラス320名とすると、さらに10倍で280秒程度、約5分処理時間が必要です。

300名分、5分だと慣れてくるとちょっと遅く感じてしまうかな? みたいな気が・・・

このプログラムには、内部的にPython環境を組み込んで高速動作させるモードがあります。数学用途の16選択肢・25行・3列で1200マーク/枚のマークシートで処理速度を計算・比較してみます。
(組み込みPythonの利用方法は後日ご案内します)

まず、Python環境を利用しない場合、1200マーク×40枚=1クラス分の48000マークを読むのにかかった時間が・・・


約78秒です。2枚1セットのダブルモードならその倍になります。
1枚(1200マーク)読むのに1.95秒かかってます。

次に、Python環境を利用した場合です。同じ読み取り条件で実験すると・・・


約11.5秒。8クラスあっても2分かかりません。ダブルモードでも4分未満。
1枚0.3秒未満で読み取ってます。

何やってもダメな自分にしては、よく頑張ったって正直、思います・・・。
よほど、びみょーなマークでない限り、期待した通り、ほぼ正しく読み取ってるし・・・。
かあさん、オレ、がんばったよ☆☆☆

まぁ このプログラム作成そのものに50万枚くらい採点できる時間をかけてますから・・・

それと合算すれば、
たぶん、プラマイ0ですー!!

6.読み取り結果のチェック

マークシートリーダーで最も重要な部分は、マーク読み取りの正確さであることは言うまでもありませんが、読み取り結果のチェック機能も非常に重要であると考えます。

人によってマークの濃さや大きさは少しずつ異なり、また、マークを訂正した箇所に残る消し跡も判定に少なからぬ影響を及ぼします。常に100%正しい読み取り結果が保証されないのが現実ですから、如何に効率よく、読み取り結果をチェックできるかで、プログラムの使用感はずいぶん変わってくると思います(CPUパワーにかなり依存したプログラムを書いておいて、そう言うのもナンですが・・・)。

自分自身の書いたものがベストだなんて、到底、思えませんが、このプログラムを書くにあたり、マークの読み取り部分と同等か、それ以上に頑張って書いたのが、この読み取り結果のチェック部分です。

機械との協働。機械との融和。これをテーマに、ヒトと機械とが一体化しての「快適なチェック作業」の実現を目指しました。

・・・が、プログラミング技術の未熟。自分自身の勉強不足。見い出した妥協点。等々の理由により、視覚による機械と協働してのチェックも、聴覚(音声出力)による機械と協働してのチェックも、いずれも全面的にマシンのCPUパワーに依存した、もっさりした感のある処理となってしまいました・・・。

処理性能の高いマシンなら、それなりに快適に作業できると思うのですが。以下、チェック機能の使い方です。

マーク読み取り結果のチェックを実行。


上の図の左のGUIから説明します。

白紙にチェックすると、マークがひとつもないシートのチェックは行わない(飛ばす)設定で動作します。この機能はデフォルトでON(チェックあり)です。

マーク(解答)がなかった場合の読み取り結果の表示が「999」です(デフォルトOFFです。このプログラムでは、「空欄」のフラグを「999」としています。マークの番号にも、得点にも「999」は通常ないことがその理由です。ちなみに複数マークは「99」と表示しています。色は「999」が「青」、「99」が「赤」です。少しでも視覚に訴えた方がチェックしやすいと考えました)。

ごく薄い色でマークされた答案が混じっていないことが大前提ですが、答案全体(1クラス分!)のマークの濃さが十分「濃い」と保証されていれば、チェック開始時のみ「999」のチェックを外してチェック(機械がきちんと空欄を識別していることをヒトが目視して確認)、で、確実に空欄を見分けていることが確認できたら、「999」にチェックして続行。こうすれば大変スムーズな確認作業を実現することができます。あくまでもごく薄くマークされたシートがないことが大前提ですが・・・

いずれにしても「Check!」ボタンをクリックすると、プログラムは次の「空欄(999)」もしくは「複数マーク(99)」を探し、それが見つかった場合は該当箇所を赤い矩形で囲んで表示します。処理性能の高い(CPUパワーのある)マシンであれば、それなりに快適に動作しますが、そうでない場合は、かなり「もっさり」した動作になりますので、イライラするかも知れません。ごめんなさい。

【空欄と判定した場合】

マークがない場合の表示例(設問番号25が空欄であった場合)

【複数マークありと判定した場合】

複数マークと判定した場合の表示例(設問番号43が複数マークであると判定)


複数マークの判定はパラメータ設定を厳し目にしてあります(上の図はそれがわかるよう、大きめに表示しました)。ごく小さなシミは「平滑化(ぼかし)」処理である程度消えますが、ある程度の面積があるシミや汚れは上のように複数マークと判定されます。

いずれの場合も、ヒトの眼で確認して、訂正の必要がなければ「Check!」ボタンをクリックしてチェックを続行。読み取り結果の訂正が必要な場合は、正しい値を直接入力します(上の場合であれば「2」と入力してください)。


【処理をスキップして次のシートへ】

「Skip」ボタンをクリックすると、現在チェックしているシートの残りの部分のチェックを省略し、次のシートのチェックへ移動します。チェック対象シートの残りの行が全部空欄であった場合などに利用してください。


【チェックの再実行】

「ReDo」をチェックすると、初めからチェックを再実行できます。

ReDoにチェックすると表示されるメッセージ①
ReDoにチェックすると表示されるメッセージ②


【音声読み上げ】

読み取り結果が表示されているStringGridの任意の行をクリックして、「▶」ボタンを押すとWindowsに標準搭載されている日本語の音声合成エンジン(Microsoft Haruka Desktop)の音声で読み取り結果をアナウンスしてくれます。

マークの読み取りが正しく行われているか・どうか、少しでもラクに確認できないかと考え、この機能を搭載しました。処理性能の高いマシンでないと快適な動作は期待できませんが、CPUパワーのあるマシンであればそれなりに使えると思います。

「▶」ボタンの下にある「×」ボタンをクリックすると、音声読み上げを途中で中止することができます。


【列を指定して、任意の行からその列の最後の行までのチェックをスキップ】

数学用のマークシート等で、第1問の解答をシート第1列にマーク、第2問の解答をシート第2列にマーク、第3問の解答を・・・というような設定にしたい場合、「指定列の任意の行から最後の行までをチェックの対象から外す」ことができます。以下、その方法です。

任意の行を指定して、その行以降のチェックをスキップできます。


図のいちばん左にある「Skip」にチェックすると、この機能が有効になり、続けて「Check!」ボタンをクリックすると、ここでの設定に基づいたチェックを実行できます。

上の例であれば、1列目25設問あるうちの20設問目以降25設問目までのチェックをスキップ(チェックは19設問まで実行)、2列目は設問番号26から始まるので34設問目以降50設問目までを、3列目は設問番号51から始まるので70設問目以降75設問目までのチェックをそれぞれスキップします。スキップの設定はComboBoxへ入力した指定値「以降」であることにご注意ください。

また、シートの型式により、列の指定の可否をプログラムが自動的に判断し、ComboBoxのEnabled プロパティが設定されます(上の例では4列目は指定不可)。

「覚」ボタンをクリックすると、現在の設定を ini ファイルに書き込んで記憶します。「消」ボタンをクリックすると「設定なし」の状態に初期化できます。

数学用途等で2枚1セットの処理を実行する場合は、1枚目と2枚目を分けてスキップ処理の設定を行うことができます(数学用途の処理方法は後日掲載します)。

7.CSVファイルへの書き出し

マークの有無の読み取り結果は、CSVファイルとExcel Book への書き出しが可能です。

【CSVファイルへの書き出し】


「ファイルへの出力」にある「CSV」をクリックして選択し、「書き出し」ボタンをクリックしてください。


上記の場所にCSV形式で、読み取り結果が出力されます。

フィールド名として1行目に「設問番号」、レコード名としてA列に「マークシート番号」が書き込まれます。

8.Excel Book の準備作業

【Excel Bookへの書き出し準備】

Excel Book への読み取り結果の書き出しは、自分用に(あれば便利かなー☆)と思って作成したものです。ですので、式の入ったセルを保護する等、第三者が使うことへの配慮は何一つ行っていません。セルに入力された式やVBAの内容をご自身でメンテナンスできる方なら、お使いいだけるかな? という程度のシロモノです。

添付した Excel Book はこれまでに何度も「実際に使用して動作に誤りがないことを確認済み」ですが、誤って式を削除したりした場合は(当然ですが)意図した通りに動作しません。ですので、こちらも動作保証は一切ありません。ご使用はあくまでも自己責任でお願いします。この Excel Book に対しても、このプログラムの使用要件にあります免責事項がそのまま適用されますことを申し添えます。

以下、試験実施前に行っておくとよい採点準備作業です。

eFile フォルダに「一般用マークと手書き併用採点シート.xltm」というマクロ有効テンプレートがあります。これをダブルクリックすると「一般用マークと手書き併用採点シート1.xlsx」という名前で新しい Excel Book が作られます。拡張子に注意してください。「.xlsx」です。このままでは期待通りに動作しませんので、適切な名前を付け、拡張子を「.xlsm」(マクロが有効な Excel Book )に変更して eFile フォルダ(必ずこのフォルダに保存してください!)に保存します。

ここでは test.xlsm という名前で保存したことにして説明を続けます。

「コンテンツの有効化」をクリックしてマクロが実行できるようにしてください。


【インターネットからダウンロードしたマクロ有効 Excel Book の取り扱い】

いつからこうなったのか、わかりませんが、インターネットからダウンロードした拡張子 xlsm の Excel Book をダブルクリックして開くと、次のメッセージが表示されるようになりました。

「編集を有効にする」をクリックすると・・・
マクロを動かすことができません!


こうなった時は、いったん Book を閉じて、その Excel ファイルを右クリックして表示されるサブメニューのプロパティをクリックして、全般タブのいちばん下にある「セキュリティ:」の「許可する」にチェックします(チェックする=マクロの実行をご自身の責任で行うことになります。どうか、ご注意ください)。

全般タブの下の方にあるセキュリティの設定。
マクロの実行をご自身の責任で行う場合は、「許可する」にチェックしてください。


「許可する」にチェックした状態で、「適用」をクリックすると「セキュリティ」の表示そのものが消えます。「あなたの責任でマクロの実行が可能になりました」ということなのでしょう。「OK」をクリックしてプロパティの設定画面を閉じます。


これでマクロが実行できるようになります。


【欠席者がいた場合】

Excel Book を利用して採点する場合、大変重要な注意事項があります。それは欠席者がいた場合の処理です。該当試験に欠席者がいる場合は、その欠席者の出席番号位置に未使用のマークシートを挿入し、シートが確実に出席番号順に並んでいることを確認してから、スキャナーでスキャンしてください。
※ 可能であれば、この用途専用に未使用のマークシートを複数枚、最初から手元に準備しておくとよいと思います。

重要 未使用のマークシートを欠席者の出席番号位置に挿入しておく!

これを忘れると、あとから「すーぱーめんどくさい」コトになります(もし、忘れたらマークシートのスキャンからもう一度、採点をやり直した方が効率がいいかもしれません)。


【受験者の氏名データを準備する】

test.xlsm をダブルクリックして開き、「コンテンツの有効化」を行ったら、いちばん最初に「名票への貼付元名票」シートをクリックして開き、ここに「採点対象者全員分の氏名」を準備してください。

もっとわかりやすく言うと、採点したいテストを受験した生徒全員の「クラス・出席番号・氏名・ふりがな・性別」データを「クラスごと」に「出席番号順」で、「名票への貼付元名票」シートに用意します。なんで「ふりがな」まで必要なのか? 疑問に思う方もいらっしゃるかもしれませんが、最近の若い方々のお名前は難読である場合が多く、採点結果を個票でお知らせする際に、個票の氏名欄のところに「ふりがな」も印刷しておくとスムーズに答案返却が行えます。そのための「ふりがな」準備です。

また、テストの受験者全員分の氏名データを1シートに準備する理由は、次のような使い方を想定しているからです。

(1)同じテストを受験 ⇨ クラス毎に採点用 Excel Book を用意するのは非効率的。
(2)採点用 Excel Book は1個だけ作成し、これをコピーして全クラス分を作る。

具体的には、eFile フォルダの Excel Book(test.xlsm)をコピーして、クラス別(AHR.xlsm)に名前を変えて MS_Reader.exe がある場所に保存。採点結果もコピーした Excel Book(AHR.xlsm) に書き込みます。さらに、この作業はすべてプログラムから自動実行します。

採点者は、採点結果が書き込まれた Excel Book(AHR.xlsm)を開いて、「名票への貼付元名票」シートに用意した氏名データから「A組の受験者の氏名データ(クラス・出席番号・氏名・ふりがな・性別)を範囲選択してコピーし、「名票」シートに値のみ貼り付けます。

こうすることで同じ内容のファイルを複数個準備することなく、言わば「採点原本」として利用する Excel Book を1つ作成するだけで、試験を実施した全クラス分の採点が可能となります。

ここでは「クラス」と表現しましたが、用意する氏名データを適宜変更すれば「講座」等の採点もまったく同じように行えます。※ プログラムの仕様としては、1回の採点作業で採点する人数を100名以下と想定していますが、実際の採点作業は1採点40名程度で行っています。ですので、40名程度を1つのまとまりとして採点していただく方向でお考えください。


【正解を入力】

氏名データの準備が完了したら、「正解」シートをクリックして表示し、設問毎に「正解」の選択肢の番号を入力します。設問がない場合(無解答でよい設問番号の欄)は空欄のままにしておきます。入力したら、入力内容に間違いがないか、よく確認し、上書き保存してください。

正解の入力を間違えるとたいへんなコトになります!
慎重に入力し、最低2回は間違いがないことを確認してください。


【配点を入力】

次に、「マークシート配点」シートをクリックして「配点」を入力します。入力と同時に合計が自動的に計算されます。入力が完了したら上書き保存してください。なお、この配点表の下には観点別評価の表もありますが、この表には一切入力しないでください(観点別評価の表は入力禁止です)。

配点を入力すると「合計」が自動計算されます。
確認作業にお役立てください。


【観点別評価の区分を入力】

次に、「マーク&手書き観点別評価」シートをクリックして「観点別評価の区分」を入力します。
「知識・技能 ⇨ 1」、「思考・判断・表現 ⇨ 2」として設問毎に、半角数字で入力してください。デフォルト設定では、すべての設問に「1」が入っています。解答を要しない設問は「空欄」にしてください。入力したら上書き保存します。


以上で、試験実施前の準備は終了です!

9.Excel Book への書き出し

重要 すべての Excel Book を閉じてから実行してください!

危険 Excelが起動した状態で実行すると重大なエラーが発生します!

Excel へデータを書き込む際は、上記注意事項を必ずお守りください。この注意を忘れて Excel が起動したまま、Excel Book への書き込みを実行すると最悪の場合、Excel のプロセスが幽霊のように残り、これを終了することが出来なくなって、復旧するには、システムの再起動しかない状態になります。未保存の重要なデータがあるような場合、当然そのデータは失われます。Excel Book へのデータ書き込み時は、Excel が起動していないことを(タスクバーに眠っている Excel Book がないことも含めて)十分確認した上で、書き込み作業を行ってください。


【書き出し処理】

マークシートを読み取り後、読み取り結果のチェックまで完了したら、Excel Book への読み取り結果の書き出しが可能となります。次のようにマークシートリーダーを操作してください。

最初に、ファイルへ出力の Excel のオプションボタンをクリックして選択します。すると、その右側にある「選択」ボタンがクリックできるようになりますから、このボタンをクリックしてください。


ファイル選択のダイアログが表示されますので、読み取り結果を書き込む Excel Book をクリックして選択し、その後、下にある「開く」ボタンをクリックします。Pathの指定は、デフォルトで eFile フォルダになっています。準備作業で作成した test.xlsm を eFile フォルダに保存したのは、この読み取り結果を書き込む Excel Book を選択する作業を円滑に実行するためです。


次のメッセージが表示されます。

重要 ここで Excel が起動していないことを必ず確認してください!

選択した Excel Book が書き込み先として表示されていることを確認し、「書き出し」ボタンをクリックします。


書き込みには、しばらく時間がかかります。次のメッセージが表示されるまでお待ちください。


すぐに書き込み結果を確認する場合は、「はい」をクリックします(ここでは「はい」をクリックしたものとして説明を続けます)。

「はい」をクリックした場合は、エクスプローラーが自動的に開きます。先ほど選択した「test.xlsm」のコピーが「Scanner_A.xlsm」として、eFile フォルダではなく、MS_Reader.exe のあるフォルダに生成されています。


ファイル名がなぜ「Scanner_A.xlsm」になったかというと、マークシートの読み取り元フォルダとして選択したのが、ProcData\Scanner_A であったためです。プログラムは、マークシートの読み取り元フォルダの名称をそのまま、原本「test.xlsm」をコピーして生成する読み取り結果書き込み先 Excel Book の名称として利用します。

マークシートの読み取り元フォルダの名称が、Excel Book の名称になります!


マークシートの読み取り元フォルダの名称が「R05_情報Ⅰ_1A」であれば、MS_Reader.exe のあるフォルダに「R05_情報Ⅰ_1A.xlsm」が生成されます。

ここは、この仕様に慣れるまで混乱が生じやすいところと思われます。しかし、この仕様(仕組み)を十分に理解して、マークシートリーダーを使いこなしている職場の同僚からは「よく考えられた採点システムだと思います」と言ってもらえました。うれしかったなー!!


【成績一覧表の印刷】

生成された Excel Book をダブルクリックして起動します。起動したら「名票への貼付元名票」タブをクリックして開き、採点対象クラス(等)の氏名データを範囲選択してコピーし、「名票」タブをクリックして B3 セルに値のみ貼り付けます。次に「採点」タブをクリックしてください。次のような画面が表示されます。「氏名がある場合のみチェックする」ボタンをクリックしてください。画面上方に表示されている平均点が正しく再計算されます。なお、欠席者の得点は「0」と表示されていますので、この場合は手動で「受験確認」のチェックを外し、平均点の計算対象から除外してください。

「氏名がある場合のみチェックする」をクリックしてください。


このシートは通常の印刷操作で印刷できます。ただし、デフォルト設定で100名分を2枚に分けて印刷する仕様となっているため、成績一覧表が1枚でよい場合は、次のように指定して1ページ分のみ印刷を実行してください。

成績一覧表を1枚だけ印刷したい場合の設定


【観点別評価を行う場合】

観点別評価を行う場合は、「正答率」タブをクリックして、上と同様の操作を行ってください。欠席者がいた場合の処理も同じです(このシートは印刷しません)。


【個票の印刷】

最後に、試験の採点結果を受験者に知らせる成績個票を印刷します。よー書いた。さすがに私も疲れました。あと、もぉちょっとです!

「個人表」タブをクリックします。次のような画面が表示されます(表示倍率は異なります)。まず、考査名と科目名を入力してください(忘れやすい部分です! ご注意願います)。印刷はVBAでマクロを組んであります。設問数に合った「印刷(QXX)」ボタンをクリックしてください。

重要 セルを保護していません。誤って式を消さないでください!


次の印刷フォームが表示されます。開始番号と終了番号を入力し、「印刷実行」をクリックします。

重要 印刷は途中で中止できません!

VBAではプログラム書いてない!のに、Engterキー押し下げでフォーカスが移動します・・・

この印刷は Excel の仕様上、印刷データをためてからイッキにプリンタへ送信という方法が取れません。1枚ずつ送信しますので、ちょっとギクシャクした感じで印刷が実行されます。プリンタが壊れているわけではありません。


【個票を個別に確認したい場合は?】

受験者個々の個票を確認したい場合は、A2 セルに「採点」シートの通番を入力します。いろいろなクラスの生徒が混在した講座の処理に対応するため、入力値は「出席番号とは異なる」ことにご注意願います。

採点シートの通番を入力します!


個票を確認したい受験者の通番は「採点」シートを表示して確認してください。

受験者の通番を確認します。


【壊しちゃったときは?】

個人表シートを壊してしまった時は、次のようにすれば直せます。「個人票_Back」タブをクリックします(このシートは絶対に非表示にしないでください)。A 列の左、1行めの上(図の〇印を付けた部分)を右クリックしてシート全体を選択し、表示されるサブメニューのコピーをクリックします。

A 列の左・1行めの上を右クリック!
シート全部をコピーします。


個人表シートに戻って、先ほどと同じ A 列の左・1行目の上を右クリックして表示されるサブメニューの「数式fx」をクリックします(罫線データ等を壊してしまった場合は、すべてを貼り付けます)。

数式が壊れた場合は数式を貼り付けます。
面倒な場合は、いちばん左の全部「貼り付け」でもOK!

10.マークシート印刷用紙について

紙の「白さ」の度合いを「白色度」というそうです。このマークシートリーダーで読み取りに使用したマークシートはすべて「再生紙 or 再生コピー用紙」と呼ばれる紙に印刷したものです。

ですから、ここで紹介したマークシートの読み取り結果は、すべて「白色度70%」前後の「再生紙」に印刷してのもので、ホームセンターで一般的に販売されているような「白色度」が「再生紙」よりはるかに高い「真っ白に見える」用紙を用いての読み取り結果ではないことに、十分ご注意願います。

マークシートの印刷に使用する紙の「白色度」によっては、読み取りパラメータ設定の見直しが必要になるかもしれません(私自身は、実験・試行していませんので正確なことはわかりませんが)。入手可能なすべての紙について、実験することは現実的に無理でありますので、マークシートを印刷する用紙については、本ソフトウェア使用者の責任で十分な試行を行い、確実に動作するパラメータ設定を行った上で、このプログラムをお使いいただけますよう、お願いいたします。

印刷はインクジェットプリンタで行うことを推奨しましたが、長期にわたって使用していない(メンテナンスもしていない)インクジェットプリンタ(複合機)では、インクの吸い込みに問題が生じ、「いくら調整しても・何度クリーニングを行っても」期待した濃度での印刷ができないということも経験しました。サービスマンの方に伺ったところ、「こういう状態になると通常のクリーニングではなかなか復旧しない」と教えていただき、あらためて日常的に使用してインクを動かすことと、不具合が見えたらすぐにメンテナンスをお願いすることの大切さに気づいたこともあります。

そのサービスマンの方からは、マークシートに付着していた消しゴムの「屑」がスキャナーのローラー等可動部の動きを悪くして、マークシートがやや斜めにスキャンされたりする原因となり得ることも教えていただきました。実際に大量のマークシートを読み取ってきた複合機のスキャナー部分からは、かなりの量の消しゴム屑が・・・。受験者には消しゴム屑をよーく落としてから答案(マークシート)を提出するよう注意しておく必要があります。まさに塵も積もればなんとやら・・・です。

また、ご使用のスキャナーの読み取り設定によっては(デフォルトの読み取り設定が)0~255段階のグレースケールでなく、カラーであったり、ある閾値で白黒二値化しての読み取りであったりという、私の想定外の設定であることも、当然のようにあり得ると思います。それがカラー画像であった場合の影響はほとんどないと思われますが、ある閾値での白黒二値化画像であった場合は、判定に重大な影響を及ぼす可能性があります。ですので、マークシートの読み取りに、使用されるスキャナーの読み取り設定に関して、予め、使用者様の責任で十分ご確認いただけますよう、併せてお願い申し上げます。

11.まとめ

このマークシートリーダーで出来ること、出来ないことをまとめました。

【出来ること】

・マークシートのJpeg画像を回転&適切なサイズに縮小
・マークシート画像のマーク読み取り(1設問当たり最大16選択肢まで対応)
・読み取り結果の確認(GUI & 音声出力)
・読み取り結果のCSVファイル出力
・読み取り結果を採点結果通知用Excel Bookへ出力(新教育課程に対応)
・共通テスト形式の数学試験に対応(選択肢:-、±、0-9、記号:a~d)※ 後日掲載します。
・共通テスト形式の情報Ⅰ試験に対応(選択肢:0始まりの設定も可能)※ 後日掲載します。
・使用環境に合わせて各種パラメータ設定を変更可能
 ⇨ ScanSnap iX1500のノーマルモード(解像度150dpi相当?)、もしくはEPSON PX-M7110F(解像度200dpi)でスキャンしたJpeg画像のマーク読み取りに最適化した値をデフォルト値に設定済み。

【出来ないこと】

・1設問について、複数の解答が設定された採点
・前問の解答内容に応じて、次の問いの解答が変わる採点
・その他、答案1枚のみの採点等、このプログラムで想定外の採点全て
・1回の読み取り操作で処理できるJpeg画像は99枚までで、100枚を超える枚数は処理できません。

【その他の使用方法】

MS_Reader.exe の「ヘルプ」にある「PDFを表示」をクリックすると利用方法の手引きがお使いのPDFリーダーで表示されます。マークシートの作り方等、このブログの記事にないことも書いてありますので、必要に応じてこちらも併せてご参照いただけますよう、お願いいたします。

12.お願いとお断り

このサイトの内容を利用される場合は、自己責任でお願いします。ここに記載した内容及びダウンロードしたプログラムを利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。

Mark Sheet Reader (Basic version)

「マークシートリーダーをつくる(基礎編)」

DelphiでGUIを作成、マークシート画像はPythonにインストールしたOpenCVとNumpyで読み取り&計算処理して、結果をMemoに表示するマークシートリーダーの練習プログラム。

0.準備
1.使用するプログラムとマークシート画像について
2.マークシート画像を読み込む
3.マークシート読み取り処理のアルゴリズム
4.マークシート読み取り処理の実際(Object Pascalのコード)
5.さらに進化
6.著作権表示の記載方法
7.お願いとお断り

ここで紹介している練習用プログラムを、実際の採点業務で使用できるようにした拙作マークシートリーダーです。

0.準備

マークシートリーダー作成にあたって、以下の事前準備が必要です。

・PythonForDelphiのインストール
・Embeddable Pythonのダウンロードと必要なライブラリのインストール
(作業後、このプログラムへの埋め込み用にフォルダ名を「Python39-32」に変えて、このプログラム(マークシートリーダー)のexeがある場所へコピーする)
・アプリケーションの表示画面のリサイズ対応(縦編)

(いずれも、当Blogの記事で過去に紹介)

重要 上の記事の手順で、OpenCVとNumpyをインストールしたEmbeddable Pythonが入ったフォルダを「Python39-32」という名前で、以下のフォルダ内にコピーする。

C:\Users\ xxx \ Project1.dprojファイルのあるフォルダ \Win32\Debug\

1.使用するプログラムとマークシート画像について

当Blogの過去記事『~主として「高さ」の変更に関する覚書~』で作成したDelphiのGUIをそのまま使用します。

必要なVCLとその構造(親子関係)

画面サイズの変更に対応できるよう、以下のコードを記述。

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  Vcl.ExtCtrls, Vcl.Grids, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    Panel2: TPanel;
    Panel3: TPanel;
    Splitter1: TSplitter;
    ScrollBox1: TScrollBox;
    Image1: TImage;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure FormResize(Sender: TObject);
    procedure Splitter1Moved(Sender: TObject);
  private
    { Private 宣言 }
    //Panel1の幅とFormの高さを記憶する変数
    intPH, intFH:integer;
    //Formの表示終了イベントを取得
    procedure CMShowingChanged(var Msg:TMessage); message CM_SHOWINGCHANGED;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TForm1 }

procedure TForm1.CMShowingChanged(var Msg: TMessage);
begin
  inherited; {通常の CMShowingChagenedをまず実行}
  if Visible then
  begin
    Update; {完全に描画}
    //Formの表示終了時に以下を実行
    Panel1.Height:=intPH;
    intPH:=Panel1.Height;
    intFH:=Form1.Height;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  //Panel1とFormの高さを記憶する変数を初期化
  intPH:=200;
  intFH:=480;
end;

procedure TForm1.FormResize(Sender: TObject);
begin
  //比率を維持してPanel1の高さを変更
  Panel1.Height:=Trunc(Form1.Height * intPH/intFH);
end;

procedure TForm1.Splitter1Moved(Sender: TObject);
begin
  //Panel1とFormの高さを取得
  intPH:=Panel1.Height;
  intFH:=Form1.Height;
end;

end.

マークシート画像は、以下の画像を使用。

「ms01.Jpg」

マークシート画像は、以下の場所に「MarkSheet」という名前のフォルダを作成して、その中に保存。

C:\Users\ xxx \ Project1.dprojファイルのあるフォルダ \Win32\Debug\Marksheet

2.マークシート画像を読み込む

Delphiを起動して、Project1.dproj(マークシート読み取り用GUIの保存してあるフォルダ内のDelphiのプロジェクトファイル)を開き、Panel3をクリックして選択しておいて、Panel3上にButton1を作成。Button1のNameプロパティはButton1のまま、Captionプロパティを「画像を表示」に変更。Button1の位置は下図を参照。

Captionプロパティを「画像を表示」に変更
Button1の位置は画面下・Panel3の左に寄せる

OpenDialog1をForm上に置く。

OpenDialogをダブルクリック
Form上のOpenDialog1

次に、Form上のButton1をダブルクリックして、procedure TForm1.Button1Click(Sender: TObject);を作成。

procedure TForm1.Button1Click(Sender: TObject);
begin

end;

作成した手続きではJpeg画像を扱うので、画面を上にスクロールして、implementation部の下に Vcl.Imaging.Jpeg を uses する。

implementation

uses
  Vcl.Imaging.Jpeg; //Jpeg画像を読み込む

{$R *.dfm}

Button1Clickプロシージャにvar宣言を追加して、Jpeg画像読み込み用の変数jpgを宣言。

procedure TForm1.Button1Click(Sender: TObject);
var
  jpg: TJPEGImage;
begin

end;

beginとend;の間に、以下のコードを記述。

  //OpenDialogのプロパティはExecuteする前に設定
  With OpenDialog1 do begin
    //表示するファイルの種類を設定
    Filter:='JPEG Files (*.jpg, *.jpeg)|*.jpg;*.jpeg';
    //データの読込先フォルダを指定
    InitialDir:=ExtractFilePath(Application.ExeName)+'MarkSheet';
  end;

  if not OpenDialog1.Execute then Exit;  //キャンセルに対応
  //オブジェクトを生成
  jpg := TJPEGImage.Create;
  try
    //読み込み
    jpg.LoadFromFile(OpenDialog1.FileName);
    //Image1に表示
    Image1.Picture.Assign(jpg);
  finally
    //オブジェクトを破棄
    jpg.Free;
  end;

上書き保存(Ctrl+S)して、実行(F9)。データの読み込み先を指定しておくと、目的のフォルダが一発で開くので便利。

マークシート画像が表示される。が、ごく一部しか見えない。

これはImage1のAutoSizeプロパティがデフォルトFalseに設定されているため。 Image1 のAutoSizeプロパティをTrueにするコードを追加(オブジェクトインスペクタで Image1 のAutoSizeプロパティを 直接指定してもOK)。

  try

    //読み込み
    jpg.LoadFromFile(OpenDialog1.FileName);
    //Image1に表示
    Image1.Picture.Assign(jpg);

    //追加
    Image1.AutoSize:=True;

  finally

上書き保存(Ctrl+S)して、実行(F9) 。画像の表示を確認する。

うまくいったように見える。Formを最大化してSplitterを下げて、さらに確認。
画像の表示位置を修正する必要がありそうだ

画像が表示される位置を、画面の左側へ移動するコードを手続きの先頭に追加する。

begin

  //Imageの表示位置を指定
  Image1.Top := 25;
  Image1.Left := 40;

  //OpenDialogのプロパティはExecuteする前に設定しておくこと
  With OpenDialog1 do begin

上書き保存(Ctrl+S)して、実行(F9) 。画像の表示を再度確認する。

ほぼイメージに近い出来栄え?

参考:画像読み込みのコード(全体)

implementation

uses
  Vcl.Imaging.Jpeg; //Jpeg画像を読み込む

{$R *.dfm}

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
var
  jpg: TJPEGImage;
begin

  //Imageの表示位置を指定
  Image1.Top := 25;
  Image1.Left := 40;

  //OpenDialogのプロパティはExecuteする前に設定しておく
  With OpenDialog1 do begin
    //表示するファイルの種類を設定
    Filter:='JPEG Files (*.jpg, *.jpeg)|*.jpg;*.jpeg';
    //データの読込先フォルダを指定
    InitialDir:=ExtractFilePath(Application.ExeName)+'MarkSheet';
  end;

  if not OpenDialog1.Execute then Exit;  //キャンセルに対応
  //オブジェクトを生成
  jpg := TJPEGImage.Create;
  try

    //読み込み
    jpg.LoadFromFile(OpenDialog1.FileName);
    //Image1に表示
    Image1.Picture.Assign(jpg);

    //追加
    Image1.AutoSize:=True;

  finally
    //オブジェクトを破棄
    jpg.Free;
  end;

end;

3.マークシート読み取り処理のアルゴリズム

まず最初にマークシートの左上にある特徴点(マーカー)画像: ■■■(トリプルドット)をOpenCVのテンプレートマッチングで探す。

特徴点(マーカー)画像が見つかったら、 特徴点(マーカー)画像左上位置を基準にして、「マークシートの周囲の枠部分のみ」を矩形選択して切り出し。

参考①:あらかじめ測定しておいた特徴点(マーカー)画像の位置(単位はピクセル)
左上のX座標=65
左上のY座標=28
右下のX座標=121(マークシート矩形の座標計算には使用しない)
右下のY座標=43(マークシート矩形の座標計算には使用しない)

参考②:あらかじめ測定しておいたマークシート矩形の座標 (単位はピクセル)
左上の X座標=65
左上の Y座標=61
右下の X 座標=419
右下の Y 座標=497

参考 上記の各座標をマークシート画像から計測し、テンプレートとして用意したマークシートごとに登録(座標値を保存)するプログラムを別途作成した。なお、座標原点(0,0)は画像の左上である(使い慣れた数学の座標系とちょっと違うことに注意!)。

赤が左上、青が右下の座標で、緑がマークシート枠の矩形

この座標を元にして、 特徴点(マーカー)画像からの距離で、マークシート矩形を切り出す。

マークシート矩形において、(W1、H1)が左上位置を、(W2、H2)が右下位置を示す座標となる。

上の例では、マークシートの列数は「1」、行数は「10」と数えることにする。列数が「1」の場合、W1は「ほぼ0(ゼロ)」になり、値としての意味がないように思われるが、このプログラムを実用化した場合は、下の例のように、複数の列があるマークシートを用いることになるので、2列めのマークシート矩形の座標は、左上が(W3,H3)、右下が(W4,H4)、3列めのマークシート矩形の座標は左上が (W5,H5)、右下が(W6,H6)のように指定でき、W値が0ではない場合が生じる。

マークシート用紙の作成に、私はWordを用いたが、Wordのバージョンによっては、あろうことか、上書き保存時に、マーカー画像(■■■)の位置が数ミリ程度、勝手に左へ移動するという予期しないトラブル(Wordの仕様?)が発生。このような点も考慮して、W1の座標は敢えて(0として)定数化していない。

マークシートの作成例(実験用に使用)
列数3、1列あたりの行数25、1行あたりの選択肢の数は16
この用紙の場合、総マーク数は3×25×16=1200個/枚となる
つまり用紙1枚につき、1200回マークの有無の判定が必要

実際の作業では、マークシート画像をスキャナーで読み取って、グレースケールのJpeg画像としてデータ化するので、マークシート(用紙)に「しわ」があったり、状況によっては「折られ」ていたりする関係上、読み取り画像を1枚ずつ比較すると、その上下・左右にどうしても微妙なブレ・ズレが生じてしまう。しかし、同じ印刷機で、同時に印刷したマークシートであれば、特徴点(マーカー)画像とマークシートの行列位置の関係は絶対であり、これが1枚ごとに変化することはありえない。つまり、スキャンした画像が余程大きく傾きでもしていない限り、テンプレートマッチングで、特徴点(マーカー)画像さえ発見できれば、予め測定・記録しておいた座標の相対的位置関係からマークシート矩形は容易に切り出せる。

次の画像は、別データとして保存してある特徴点(マーカー)画像を元に、OpenCVのテンプレートマッチングをマークシート画像に対して行ったもの。類似度の高い部分を赤枠で囲んで示すようプログラミングしている。

マーカー
テンプレートマッチングを行った画像

次に、上に述べた方法で計算したマークシート矩形を列単位で切り出す。切り出した画像は、マークの(=列)数・行数の整数倍のサイズになるようリサイズする(これは、このあと画像を細かく分割して処理するので、切り出す行や列の計算を簡単にするための工夫 → 整数倍にリサイズすれば、列数分&行数分廻すLoop処理の中で処理しやすい)。

列単位で切り出したマークシート矩形

マークシート用紙は、一般的なマークシート用紙のような厚みのある(高級感あふれる)専用紙でなく、ホームセンターでも「売ってない!」ような見た目が灰色の再生紙を用いている。このためか、あちらこちらにゴミのような黒い点や、細いすじが入っていることがある。これらの黒点やすじを判定プログラムが「マークあり」と誤認しないようにするため、次に「平滑化(ボカシ)処理」を行う。

平滑化(ボカシ)処理には「ガウシアンフィルタ」を用いた。これは、正規(ガウス)分布を利用して「注目画素からの距離に応じて近傍の画素値に重みをかける」という処理を行うもので、自然な平滑化が実現できるとのこと。次の画像は、上の切り出したマークシート矩形に対して、この平滑化処理を行ったもの。

img = cv2.GaussianBlur(img,(35,35),0) ※引数は奇数を指定する必要がある

引数の値が大きいほど正規分布のピークが低く、広がりは広くなる(=より均一に、より全体にボカシがかかる)。ここでは引数をかなり大きめにとり「35」としている。こうすることで、ゴミやシミを画像からほぼ完全に除去できる。

ガウシアンフィルタ処理を行い、ゴミやシミを除去する

さらに、この画像を「ある閾値」を元に白と黒に二値化処理する。この処理で枠線やマークされていないマーク部分が「すべて白」になり、鉛筆で濃くマークされている部分だけが「黒」になった白黒画像が得られる。当初は、以下のように引数を指定して二値化画像を作成した。

ret, img = cv2.threshold(img, 140, 255, cv2.THRESH_BINARY)

現在は、次のように閾値の設定を自動で行う「大津の二値化」を利用している。

ret, img = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

式中の第2引数は閾値だが、大津の二値化では自動計算させるので0(ゼロ)を指定。第3引数は0-255の256段階でグレースケール化しているから、最大値の255を指定する。これによって、次の画像が得られる。

大津の二値化で作成した白黒画像

さらに、これを白黒反転させた画像を作成する。式は以下の通り。

img = 255 - img

これにより、次の画像が得られる。

マーク部分を「白」に変換した画像

次に、この画像を「行」単位に分割して切り出す。

1行目を切り出した画像

次に、選択肢の数で、均等に分割する。ここでは選択肢の数が「8」なので、上の画像を等幅で8個に分割する。下は、その1個目の切り出し画像である。

このように細かく分割して切り出した画像1つ1つについて、画素が白なら値を255・黒なら0として面積あたりの合計値を計算し、マークされている部分の面積の中央値を算出、これを閾値として、下の式では、マークされている(白い部分の)面積が他より3倍以上あるものを「マークあり!」と判定している。この数値が大きいほど、判定はきびしくなる。

result.append(area_sum > np.median(area_sum) * 3)

このマークシート読み取り処理のアルゴリズムの主要部分は全て、GitHubの次の記事に紹介されていたものです。素晴らしい記事を投稿してくださった作成者の方に、心から感謝申し上げます。

PythonとOpenCVで簡易OMR(マークシートリーダ)を作る

URL:https://qiita.com/sbtseiji/items/6438ec2bf970d63817b8

参考 列が複数あるマークシートの読み取り処理について

上記記事では、特徴点(マーカー)画像をマークシートの上下に複数個用意し、テンプレートマッチングを行っています。確かに、マークシートの左上と右下に特徴点(マーカー)画像を用意すれば、より簡単にマークシート矩形の切り出しが可能でした。これは素晴らしいアイデアです。

私も当初は特徴点(マーカー)画像を複数個用意してマークシートを作成していたのですが、列数を2列、3列と増やすと、さまざまな問題が生じることに気が付きました。

第一に、特徴点(マーカー)画像を変えないと、列ごとの切り出しが困難だということです。つまり、3列あるマークシートでは、最も左の列用の特徴点を■■■、真ん中の列用の特徴点を■□■、最も右側の列用の特徴点を■□□として、Loop処理の中でテンプレートマッチングに使用する特徴点(マーカー)画像を切り替えて、目的とするマークシート矩形を切り出せるようにしてみた(□□■や□□□も含めればさらに多くの列が作成可能)のですが、この方法では、うまく特徴点(マーカー)画像を認識してくれないことがあり、安定感に欠ける気がしました。

第二に、万一、回答者が特徴点(マーカー)画像に意図的に変更を加える(例: ■□□ → ■■□)等の暴挙に出た場合、対応が難しいこと。

第三に、マーカー画像が多いと、マークシートの見た目もなんだか騒がしくて、個人的にはマーカー画像を複数個用意する方法はなるべく避けたいと考えたこと。

これらの理由から、「なんとか特徴点(マーカー)画像が1個で済まないか」と、私なりに工夫して、当ブログで紹介した方法を考えました。

創意工夫の過程で一時は、回答者が意図的に変更できるようなマーカー(例: □ )がなければOKかとも思い、別の特徴点(マーカー)画像も使ってみたのですが、それはそれでまた別の問題を起こすことがわかりました。

例えば、下のように、ヒトなら簡単に両者の違いを判別できる画像を用意します。

用意した特徴点(マーカー)画像

これに対して、左側の画像でテンプレートマッチングを行うと・・・

機械はヒトと違うモノの見方をしていることが、大変良くわかりました。

4.マークシート読み取り処理の実際(Object Pascalのコード)

Form上に、Buttonを1つ、PythonForDelphi関連のVCLコンポーネントを3つ配置する。Button2は、Panel3の中央付近に置き、Nameプロパティはそのまま、Captionプロパティを「読み取り」に変更する。PythonForDelphi関連のVCLコンポーネントは、すべて非ビジュアルコンポーネントなので、位置はどこでもよく、Nameプロパティもデフォルトのままとする。 PythonForDelphi関連で配置するコンポーネントは以下の通り。

以下のように、PythonForDelphi関連のコンポーネントのプロパティとイベントを設定

・PythonEngine1のAutoLoadプロパティはFalseに設定。

・PythonEngine1のDllNameプロパティはpython39.dllを指定(埋め込みPythonのバージョンに合わせて設定する)。ここでは3.9.9以下のバージョンのPythonでないとNumpyが非対応(2021年12月現在)であり、用意した埋め込みPythonのバージョンは3.9.9なのでpython39.dllに変更する。

・PythonEngine1のIOにはPythonGUIInputOutput1を指定。

・PythonGUIInputOutput1は他で利用するならプロパティのOutPutに「Memo1」などとするところだけれど、ここでは何も設定しない。

・PythonDelphiVar1のVarNameはプログラムコードの記述に合わせて「var1」とする。var1と入力後、Enterで確定すること!(青く反転表示されるのを確認する)

Formが生成される時、PythonEngine1を初期化する。Formのタイトルバーの上をクリックして選択し、オブジェクトインスペクタのイベントタブをクリックしてOnCreateイベントの右に表示されている「FormCreate」をダブルクリックして、コードの入力に切り替える。

参考:エラー対応方法(20220724追加)

P4D使用時にImageコントロールの bsClear を使うとエラーが発生します。

[dcc32 エラー] Unit02_MSReader.pas(1199): E2010 'TBrushStyle' と 'Enumeration' には互換性がありません

これはPythonEngine.pasの中で bsClear が定義(使用)されているためです。次に示す例のように、Image1の方のbsClearを明示的に Vcl.Graphics.bsClear として対応します。

  //矩形を描画
  with Image1 do
  begin
    //Canvas.Brush.Style:=bsClear;
    Canvas.Brush.Style:=Vcl.Graphics.bsClear;
  end;

以上、エラー対応でした。解説を続けます。

表示は次のようになっている(はず)。ここにコードを追加する。

procedure TForm1.FormCreate(Sender: TObject);
begin

  //Panel1とFormの高さを記憶する変数を初期化
  intPH:=200;
  intFH:=480;

end;

追加するコード

procedure TForm1.FormCreate(Sender: TObject);
var
  //Python39-32へのPath(追加)
  AppDataDir:string;
begin

  //Panel1とFormの高さを記憶する変数を初期化
  intPH:=200;
  intFH:=480;

  //以下のコードを追加
  //embPythonの存在の有無を調査
  AppDataDir:=ExtractFilePath(Application.ExeName)+'Python39-32';

  if DirectoryExists(AppDataDir) then
  begin
    //フォルダが存在したときの処理
    MessageDlg('Embeddable Pythonが利用可能です。',
      mtInformation, [mbOk] , 0);
    PythonEngine1.AutoLoad:=True;
    PythonEngine1.IO:=PythonGUIInputOutput1;
    PythonEngine1.DllPath:=AppDataDir;
    PythonEngine1.SetPythonHome(PythonEngine1.DllPath);
    PythonEngine1.LoadDll;
    //PythonDelphiVar1のOnSeDataイベントを利用する
    PythonDelphiVar1.Engine:=PythonEngine1;
    PythonDelphiVar1.VarName:=AnsiString('var1');  //プロパティで直接指定済み
    //初期化
    PythonEngine1.Py_Initialize;
  end else begin
    MessageDlg('Embeddable Pythonが見つかりません!',
      mtInformation, [mbOk] , 0);
    PythonEngine1.AutoLoad:=False;
  end;

end;

ここでMessageDlgを使用しているので、以下のように System.UITypes を uses に追加する。

implementation

uses
  Vcl.Imaging.Jpeg, System.UITypes;  // <-追加

  //Jpeg:Jpeg画像を読み込む
  //System.UITypesはMessageDlgの表示に必要

{$R *.dfm}

プライベートメンバー変数 intCnt(カウンタとして利用する)と strAnsList(Pythonから返された計算結果を保存する) を2つ、Private宣言で新しく宣言する。

  private
    { Private 宣言 }

    //for Python(追加)
    //Counter
    intCnt:integer;
    //Pythonから送られたデータを保存
    strAnsList:TStringList;

    //Panel1の幅とFormの高さを記憶する変数
    intPH, intFH:integer;
    //Formの表示終了イベントを取得
    procedure CMShowingChanged(var Msg:TMessage); message CM_SHOWINGCHANGED;

  public
    { Public 宣言 }
  end;

Form上のButton2(読み取りボタン)をダブルクリックして、手続きを作成し、以下の内容を入力する。

procedure TForm1.Button2Click(Sender: TObject);
var
  StrList:TStringList;
  strJCnt,strColCnt,strRowCnt,strSelCnt:String;
  TopLX, TopLY, TLX1, TLY1, BRX1, BRY1:integer;
  strPicName:string;
begin

  //初期化
  Memo1.Clear;
  intCnt:=1;

  //座標
  TopLX:=65;
  TopLY:=28;
  //BtmRX:=121;
  //BtmRY:=43;
  TLX1:=65;
  TLY1:=61;
  BRX1:=419;
  BRY1:=497;

  //マークシート数Check(+1することを忘れない)
  strJCnt:=IntToStr(2);

  //列数Check(+1することを忘れない)
  strColCnt:=IntToStr(2);

  //1列あたりの行数Check
  strRowCnt:=IntToStr(10);

  //選択肢数Check
  strSelCnt:=IntToStr(8);

  //マークシート名
  strPicName:='ms';

  //結果を保存するStringList
  strAnsList := TStringList.Create;

  //Scriptを入れるStringList
  StrList := TStringList.Create;

  try

    //Python Script
    StrList.Add('import cv2');
    StrList.Add('import numpy as np');

    //for JPN(日本語に対応)
    StrList.Add('def imread(filename, flags=cv2.IMREAD_GRAYSCALE, dtype=np.uint8):');
    StrList.Add('    try:');
    StrList.Add('        n = np.fromfile(filename, dtype)');
    StrList.Add('        img = cv2.imdecode(n, flags)');
    StrList.Add('        return img');
    StrList.Add('    except Exception as e:');
    StrList.Add('        return None');

    //マーカー画像を読み込む
    StrList.Add('template = imread("marker.png", cv2.IMREAD_GRAYSCALE)');

    //マークシートの枚数
    StrList.Add('for j in range(1,'+strJCnt+'):');

    //列数
    StrList.Add('    for i in range(1,'+strColCnt+'):');

    //マークシートへのパスを取得
    StrList.Add('        if j < 10:');
    StrList.Add('            MS_Name = r".\Marksheet\'+ strPicName +'0"+ str(j) +".jpg"');
    StrList.Add('        else:');
    StrList.Add('            MS_Name = r".\Marksheet\'+ strPicName +'"+ str(j) +".jpg"');

    //画像を読み込む
    StrList.Add('        img = imread(MS_Name)');
    //画像をグレースケールで読み込む
    StrList.Add('        img_gray = imread(MS_Name, 0)');

    //テンプレートマッチングの実行(比較方法cv2.TM_CCORR_NORMED)
    StrList.Add('        result = cv2.matchTemplate(img, template, cv2.TM_CCORR_NORMED)');

    //類似度が最小,最大となる画素の類似度、位置を調べ代入する
    StrList.Add('        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)');
    //最も似ている領域の左上の座標を取得
    StrList.Add('        top_left = max_loc');
    StrList.Add('        if i == 1:');

    //補正値を取得(高さ)
    StrList.Add('            h1 = ' + IntToStr(TLY1 - TopLY));
    StrList.Add('            h2 = ' + IntToStr(BRY1 - TopLY));
    //補正値を取得(幅)
    StrList.Add('            w1 = ' + IntToStr(TLX1 - TopLX));
    StrList.Add('            w2 = ' + IntToStr(BRX1 - TopLX));

    //矩形の左上の座標を計算 [0]-> X, [1]-> Y
    StrList.Add('        TL = (top_left[0] + w1, top_left[1] + h1)');
    //矩形の右下の座標を計算
    StrList.Add('        BR = (top_left[0] + w2, top_left[1] + h2)');
    //画像を切り出し img[top_Y : bottom_Y, left_X : right_X]
    StrList.Add('        img = img_gray[TL[1] : BR[1], TL[0] : BR[0]]');

    //選択肢数
    StrList.Add('        n_col = '+ strSelCnt);

    //解答欄1列あたりの行数
    StrList.Add('        n_row = '+ strRowCnt);
    StrList.Add('        margin_top = 0');
    StrList.Add('        margin_bottom = 0');
    StrList.Add('        n_row = n_row + margin_top + margin_bottom');

    //マークの列数・行数の整数倍のサイズになるようリサイズ
    StrList.Add('        img = cv2.resize(img, (n_col*100, n_row*100))');

    //保存して確認
    //StrList.Add('        cv2.imwrite("01_ReSize.png", img)');

    //平滑化の度合い
    StrList.Add('        img = cv2.GaussianBlur(img,(35,35),0)');

    //保存して確認
    //StrList.Add('        cv2.imwrite("02_GaussianBlur.png", img)');

    //二値化の閾値
    //50を閾値として2値化
    //imgはグレースケール画像でなければならない
    //第2引数はしきい値で,
    //画素値を識別するために使用(指定)
    //第3引数は最大値でしきい値以上
    //(指定するフラグ次第では以下)の値を持つ
    //画素に対して割り当てられる値
    //StrList.Add('        ret, img = cv2.threshold(img, 140, 255, cv2.THRESH_BINARY)');

    //大津の二値化で閾値の設定を自動化
    //第1引数には画像データを設定
    //(グレースケール画像でなければならない)
    //第2引数はしきいだが自動計算させるので0(ゼロ)を指定
    //第3引数は0-255の256段階でグレースケール化しているから
    //最大値の255を指定
    StrList.Add('        ret, img = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)');

    //保存して確認
    //StrList.Add('        cv2.imwrite("03_threshold.png", img)');

    //白黒を反転
    StrList.Add('        img = 255 - img');

    //保存して確認(追加)
    StrList.Add('        cv2.imwrite("04_threshold.png", img)');

    //全マークを判定
    StrList.Add('        result = []');
    StrList.Add('        for row in range(margin_top, n_row - margin_bottom):');
    StrList.Add('            tmp_img = img [row*100:(row+1)*100,]');
    StrList.Add('            area_sum = []');
    StrList.Add('            for col in range(n_col):');
    StrList.Add('                area_sum.append(np.sum(tmp_img[:,col*100:(col+1)*100]))');
    StrList.Add('            result.append(area_sum > np.median(area_sum) * 3)');

    //判定結果を出力
    StrList.Add('        for x in range(len(result)):');
    StrList.Add('            res = np.where(result[x]==True)[0]+1');
    StrList.Add('            if len(res)>1:');
    StrList.Add('                var1.Value = "99"');
    StrList.Add('            elif len(res)==1:');
    StrList.Add('                s = str(res)');
    StrList.Add('                var1.Value = s[1]');
    StrList.Add('            else:');
    StrList.Add('                var1.Value = "999"');

    //Execute
    PythonEngine1.ExecStrings(StrList);

    //結果を表示
    Memo1.Lines.Assign(strAnsList);

    //Userへ案内
    MessageDlg('読み取り完了!', mtInformation, [mbOk] , 0);

  finally
    //解放
    StrList.Free;
    strAnsList.Free;
  end;

end;

Pythonから返された計算結果を受け取るため、PythonDelphiVar1のOnSetDataイベントの手続きを作成する。Form上のPythonDelphiVar1をクリックして選択し、オブジェクトインスペクタのOnSetDataイベントの右側をダブルクリックして、コード入力画面で以下の内容を入力する。

procedure TForm1.PythonDelphiVar1SetData(Sender: TObject; Data: Variant);
begin
  //値がセットされたら動的配列に値を追加
  strAnsList.Add(Data);
  intCnt:=intCnt+1;
  Application.ProcessMessages;
end;
表示の「999」は空欄、「99」は複数マークであることを意味する。

上書き保存(Ctrl+S)して、実行(F9)。次の画像のように、マークシートが正しく読み取り処理されることを確認する。

複数マークを許可する場合には、判定結果を出力する部分のコードを次のように変更する。マークシートの読み取り結果をCSVファイルに出力したり、Excelに書き出したりして利用する場合には、複数回答は99、未回答は999のように処理した方が、後々の処理がラクになる(・・・と思う)。

    //判定結果を出力(複数回答は99、未回答は999で表示)
    {コメント化ここから
    StrList.Add('        for x in range(len(result)):');
    StrList.Add('            res = np.where(result[x]==True)[0]+1');
    StrList.Add('            if len(res)>1:');
    StrList.Add('                var1.Value = "99"');
    StrList.Add('            elif len(res)==1:');
    StrList.Add('                s = str(res)');
    StrList.Add('                var1.Value = s[1]');
    StrList.Add('            else:');
    StrList.Add('                var1.Value = "999"');
    ここまで}

    //判定結果を出力(複数回答の詳細を表示)
    StrList.Add('        for x in range(len(result)):');
    StrList.Add('            res = np.where(result[x]==True)[0]+1');
    StrList.Add('            if len(res)>1:');
    StrList.Add('                var1.Value = str(res)+ '+'"!複数回答!"');
    StrList.Add('            elif len(res)==1:');
    StrList.Add('                s = str(res)');
    StrList.Add('                var1.Value = s[1]');
    StrList.Add('            else:');
    StrList.Add('                var1.Value = " *未回答*"');

PythonEngineが正しく初期化され、Embeddable Pythonが利用できることが確認できたら、このメッセージは必要ないのでコメント化しておく。

procedure TForm1.FormCreate(Sender: TObject);
var
  //Python39-32へのPath
  AppDataDir:string;
begin
  ・・・
  if DirectoryExists(AppDataDir) then
  begin
    //フォルダが存在したときの処理(コメント化)
    //MessageDlg('Embeddable Pythonが利用可能です。',
    //  mtInformation, [mbOk] , 0);
    PythonEngine1.AutoLoad:=True;

5.さらに進化

さまざまな機能を追加したマークシートリーダー
(ファイルの名称を連番で変更/画像の回転/グリッド指示位置と画像の連動/グリッド指示位置を画像上で矩形選択/閾値等各種パラメータの調整と保存機能/音声読み上げ関連機能の搭載/回答チェック機能(空欄&複数回答対応)/CSV形式でのデータ出力/ExcelBookへのデータ出力/様式の異なるマークシートをテンプレートとして登録して利用可能/抱き合わせ採点の実施機能/共通テスト(数学の様式)に対応等、考えつく限りの機能を搭載/さらに進化します!)

このプログラムでは、「マークシート画像の表示」と、「読み取り処理」の間に何も関連がないが、このプログラムをさらに発展させて、複数枚数の処理を可能にし、読み取り結果を画面上で確認するような機能を追加する際には、マークシート画像の表示はどうしても必要な機能になる。

さらに、画面の左側などに読み込んだマークシートがリスト形式で表示されるようにして、ここから任意のマークシート画像を選んで表示できるような機能も追加するとよいと思う。

読み取り結果も、ここではMemoに表示しているが、CSVやExcelへ出力して利用することを考えると、ここはGridコントロールに変更したい。

Gridコントロール上で選択したデータの該当回答欄に相当する画像が自動的に画面上に表示され、かつ、表示されたマークシート画像上の該当回答欄が矩形で選択され、ユーザーがチェックしやすいGUIにするとなお良いだろう。

また、チェック時にはユーザーがマークシート画像を見ながら確認作業が行えるよう、Gridコントロールの数字をアナウンスしてくれる音声読み上げ機能があると大変便利だ。それから、回答の必要がない、全マークシートが空欄となっている部分は、予め指定することで、チェックから除外できる機能も欲しい。

さらに、スキャナーから読み込んだ画像データを回転させたり、連番で扱いやすい名前に変更したり、様式の異なるマークシートをテンプレートとして登録できるような機能も搭載したい。

より一層ユーザーに優しい、夢に見たようなマークシートリーダーを開発したい。この希望の実現に向けて、日々努力する私でありたい。

Web上に貴重な資料を公開してくださった多くの皆さまに心より深く御礼申し上げます。ほんとうにありがとうございました。

6.著作権表示の記載方法

参考:Python4DelphiのLicenseについて

GitHubのPython4Delphiのダウンロードページには「The project is licensed under the MIT License.」とある。これは「改変・再配布・商用利用・有料販売すべてが自由かつ無料」であること、及び使用するにあたっての必須条件はPython4Delphiの「著作権を表示すること」と「MITライセンスの全文」or 「 MITライセンス全文へのLink」をソフトウェアに記載する、もしくは、別ファイルとして同梱しなさい・・・ということを意味する。

したがってPython4Delphiを利用したプログラムの配布にあたっては、ソフトウェアの中で、次のような著作権表示を行うか、もしくは P4DフォルダのルートにあるLicenseフォルダをプログラムに同梱して配布すればよいことになる。

Python4Delphiを利用した場合の著作権表示の記載例:

Copyright (c) 2018 Dietmar Budelsky, Morgan Martinet, Kiriakos Vlahos
Released under the MIT license
https://opensource.org/licenses/mit-license.php

7.お願いとお断り

このサイトの内容を利用される場合は、自己責任でお願いします。記載した内容を利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。

【関連記事】