Python は、ファイル入出力やHTTP通信、クラウドサービスの利用だけでなく、PC付属デバイスからの入出力手段も用意されている。今回は、PC内蔵カメラを使って書籍のバーコードを読み取り、Amazon商品サイトのURLを求めるプログラムを作ってみる。
目次
サンプル・プログラム
PC内蔵カメラの映像を画面に表示する
PC内蔵カメラを利用するには、画像処理を行う外部ライブラリ OpenCV をインストールする。pipコマンドを使ってインストールしてほしい。
もし表示できなかったりエラーが出た場合は、このあとのソース・プログラムの解説を参考に、パラメータを変更してみてほしい。
pip install opencv-pythonインストールできたら、プログラム "webcamCapture.py" を実行してみてほしい。PC内蔵カメラ(WebCam)で撮影しているライブ映像を画面に表示できたろうか。何かキーを押すと、このプログラムは終了する。
もし表示できなかったりエラーが出た場合は、このあとのソース・プログラムの解説を参考に、パラメータを変更してみてほしい。
try:
import cv2
except ImportError as errmsg:
print(f"外部ライブラリをインストールしてください ... {errmsg}")
exit()
# メイン・プログラム =======================================================
# 初期値
cameraID = 0 # WebCamのID(各自の環境に合わせて変更のこと)
# WebCamのキャプチャを開始
cap = cv2.VideoCapture(cameraID)
if not cap.isOpened():
print("カメラを開くことができませんでした")
exit(1)
while True:
# フレームを読み込む
ret, frame = cap.read()
if ret == True:
# 表示するテキストを用意する
text = "-- Hit any key to exit --" # 表示テキスト(日本語NG)
position = (20, 40) # 文字の表示位置 (x, y)
font = cv2.FONT_HERSHEY_SIMPLEX # フォントの種類
fontScale = 1 # 文字の大きさ
fontColor = (0, 255, 0) # 文字の色 (B, G, R)
fontWeight = 2 # 文字の太さ
lineType = cv2.LINE_AA # アンチエイリアス
# フレームにテキストを描画する
cv2.putText(frame, text, position, font, fontScale, fontColor, fontWeight, lineType)
# フレームを表示する
cv2.imshow("WebCam", frame)
# 何かキーが押されたらループから抜ける
if cv2.waitKey(1) != -1:
break
else:
break
# キャプチャをリリースし、ウィンドウを閉じる
cap.release()
cv2.destroyAllWindows()
メイン・プログラムでは、まず、カメラのIDを変数 cameraID に代入する。内蔵カメラの場合、たいていは 0 だが、もしうまく画面に表示しない場合は値を変えてみてほしい。なお、複数のカメラが接続されている場合、各々に異なるIDが割り当てられるので、cameraID を変更することで目的のカメラからの映像を取得することができる。
カメラから映像を取得(キャプチャ)するには、cv::VideoCaptureクラスを利用する。インスタンスを変数 cap に代入する。
カメラが利用できる状態かどうかは、isOpendメソッドを使って取得できる。戻り値が False なら準備できていないので、メッセージを表示してプログラムを終了する。ここで使った exit()は、Pythonの組み込み関数ではなく、定数という位置づけた。指定された値を終了コードとして、SystemExitを実行する。
次に、キーボードから何かキーが押下されるまで、カメラからのキャプチャと画面表示を繰り返して行うループに入る。このプログラムは動画を画面に表示しているわけではなく、カメラから1フレームの静止画像を受け取り、それを画面に表示している。それが高速にループ処理しているため、見かけ上、動画になって見える。
readメソッドを使って、1フレームをキャプチャする。戻り値は、(成否, フレームオブジェクト)のタプルである。成否の方は、Trueならキャプチャ成功、Falseなら失敗。
キャプチャに成功したら、putTextメソッドを使ってキャプチャ画面に「-- Hit any key to exit --」というテキストを緑色でフレームオブジェクトに書き込む。前述の通り、1フレーム毎に画像が切り替わるので、テキスト描画もフレーム毎に実行しないと消えてしまう。
ここまでできたら、imshowメソッドを使ってフレームをウィンドウに表示する。第1引数にはウィンドウ名を、第2引数にフレームオブジェクトを渡す。ウィンドウ明が同じであれば、以降のフレームも同じウィンドウに表示するので、動画を表示しているように見せることができる。ここでは省略しているが、第3引数として画面サイズをタプルで渡すことができる。
waitKeyメソッドを使って、何かキーが押下されたらループを脱出(break)する。waitKeyメソッド は第1引数に待機時間(ミリ秒)を渡す。ここでは最小値の1を渡している。戻り値は、押下されたキーのコード、もしくは何も押されていなければ -1 が戻る。
最後に、releaseメソッドを使い、カメラへの接続をクローズし、フレームオブジェクトなどのリソースをリリースし、destroyAllWindowsメソッドを使ってフレームを表示していたウィンドウを閉じる。
カメラから映像を取得(キャプチャ)するには、cv::VideoCaptureクラスを利用する。インスタンスを変数 cap に代入する。
カメラが利用できる状態かどうかは、isOpendメソッドを使って取得できる。戻り値が False なら準備できていないので、メッセージを表示してプログラムを終了する。ここで使った exit()は、Pythonの組み込み関数ではなく、定数という位置づけた。指定された値を終了コードとして、SystemExitを実行する。
次に、キーボードから何かキーが押下されるまで、カメラからのキャプチャと画面表示を繰り返して行うループに入る。このプログラムは動画を画面に表示しているわけではなく、カメラから1フレームの静止画像を受け取り、それを画面に表示している。それが高速にループ処理しているため、見かけ上、動画になって見える。
readメソッドを使って、1フレームをキャプチャする。戻り値は、(成否, フレームオブジェクト)のタプルである。成否の方は、Trueならキャプチャ成功、Falseなら失敗。
キャプチャに成功したら、putTextメソッドを使ってキャプチャ画面に「-- Hit any key to exit --」というテキストを緑色でフレームオブジェクトに書き込む。前述の通り、1フレーム毎に画像が切り替わるので、テキスト描画もフレーム毎に実行しないと消えてしまう。
ここまでできたら、imshowメソッドを使ってフレームをウィンドウに表示する。第1引数にはウィンドウ名を、第2引数にフレームオブジェクトを渡す。ウィンドウ明が同じであれば、以降のフレームも同じウィンドウに表示するので、動画を表示しているように見せることができる。ここでは省略しているが、第3引数として画面サイズをタプルで渡すことができる。
waitKeyメソッドを使って、何かキーが押下されたらループを脱出(break)する。waitKeyメソッド は第1引数に待機時間(ミリ秒)を渡す。ここでは最小値の1を渡している。戻り値は、押下されたキーのコード、もしくは何も押されていなければ -1 が戻る。
最後に、releaseメソッドを使い、カメラへの接続をクローズし、フレームオブジェクトなどのリソースをリリースし、destroyAllWindowsメソッドを使ってフレームを表示していたウィンドウを閉じる。
ISBNコード
ISBN(International Standard Book Number)は国際標準図書番号とも呼ばれ、書籍を特定するための世界共通の番号体系である。
2006年(平成18年)12月31日までは10進数10桁の番号が用いられていたが、2007年(平成19年)以降には10進数13桁の番号へ移行した。ここでは現行規格を中心に説明する。
ISBNコードは10進数13桁からなり、その内容は下記の通りである。
2006年(平成18年)12月31日までは10進数10桁の番号が用いられていたが、2007年(平成19年)以降には10進数13桁の番号へ移行した。ここでは現行規格を中心に説明する。
ISBNコードは10進数13桁からなり、その内容は下記の通りである。
桁 | 内容 |
---|---|
13-11 | 識別番号(978または979) |
10 | グループ記号(出版国などを表す数字で、日本は4) |
9-6 | 出版者記号 |
5-2 | 書名記号 |
1 | チェックデジット |
たとえば、「楽しいPHP入門」(クジラ飛行机/インプレスジャパン/2009年(平成21年)11月)という書籍のISBNコードは 9784844327882 である。これを分解すると下記のようになる。
内容 | 値 |
---|---|
識別番号 | 978 |
グループ記号 | 4 |
出版者記号 | 8443 |
書名記号 | 2788 |
チェックデジット | 2 |
出版者番号 8443 はインプレスコミュニケーションズに与えられた番号である。
チェックデジットはモジュラス10 ウェイト3 という方式である。以下のように計算する。
\begin{align}
&9 \times 1 + 7 \times 3 + 8 \times 1 + 4 \times 3 + 8 \times 1 + 4 \times 3 + 4 \times 1\\
&\ \ \ \ + 3 \times 3 + 2 \times 1 + 7 \times 3 + 8 \times 1 + 8 \times 3 = 138 \\
&138 \ \% \ 10 = 8 \\
&10 - 8 = 2
\end{align}
\]
となり、チェックデジットの 2 を得ることができる。
ちなみに、旧規格(2007年12月31日まで)は10進数10桁であるが、グループ記号、出版者記号、書名記号は現行規格と同じである。チェックデジットの計算方法は異なる(モジュラス11 ウェイト10-2)。
チェックデジットはモジュラス10 ウェイト3 という方式である。以下のように計算する。
- チェックデジットをのぞく一番左側の桁から順に1,3,1,3を掛けて、それらの和を取る。
- 和を10で割った余りを10から引く。ただし10で割って出た余りの下1桁が0の場合は0とする。
\begin{align}
&9 \times 1 + 7 \times 3 + 8 \times 1 + 4 \times 3 + 8 \times 1 + 4 \times 3 + 4 \times 1\\
&\ \ \ \ + 3 \times 3 + 2 \times 1 + 7 \times 3 + 8 \times 1 + 8 \times 3 = 138 \\
&138 \ \% \ 10 = 8 \\
&10 - 8 = 2
\end{align}
\]
となり、チェックデジットの 2 を得ることができる。
ちなみに、旧規格(2007年12月31日まで)は10進数10桁であるが、グループ記号、出版者記号、書名記号は現行規格と同じである。チェックデジットの計算方法は異なる(モジュラス11 ウェイト10-2)。
ASINコード
ASIN(Amazon Standard Identification Number)は、Amazon.comが独自に決めた商品番号のことで、書籍についてはISBNの旧規格と同じ番号であった。
ISBNの現行規格についても、識別番号が978であるものについては、桁数10-2は同一である。ただしチェックデジットの計算方法がモジュラス11 ウェイト10-2であるため、最後の1桁の数字が異なる。
モジュラス11 ウェイト10-2方式は、以下のように計算する。
ISBNの現行規格についても、識別番号が978であるものについては、桁数10-2は同一である。ただしチェックデジットの計算方法がモジュラス11 ウェイト10-2であるため、最後の1桁の数字が異なる。
モジュラス11 ウェイト10-2方式は、以下のように計算する。
- チェックデジットを除いた左側の桁から10,9,8‥‥2を掛けてそれらの和を取る。
- 和を11で割って出た余りを11から引く。
4×10+8×9+4×8+4×7+3×6+2×5+7×4+8×3+8×2=268となり、チェックデジットの 7 を得ることができる。
268%11=4
11-4=7
ISBNとASINの関係
書籍のバーコードをAmazon商品URLに変換する
Python は、外部ライブラリを使って画像中にあるバーコード(一次元、二次元)を自動認識することができる。ここでは pyzbarという外部ライブラリを使い、先ほどのPC内蔵カメラでキャプチャした画像から書籍バーコードを自動認識し、Amazonの商品URLに変換してコンソールに出力するプログラムを作ってみる。
まず、pyzbar をインストールしよう。
ただし、バーコードを認識するにはカメラに一定の解像度が要求され、フルHD撮影可能なカメラなら認識できるだろう。
まず、pyzbar をインストールしよう。
pip install pyzbarインストールできたら、プログラム "isbn2asin.py" を実行してみてほしい。PC内蔵カメラで書籍のバーコードを撮影すると、リアルタイムでAmazon商品URLをコンソールに表示する。何かキーを押すと、このプログラムは終了する。
ただし、バーコードを認識するにはカメラに一定の解像度が要求され、フルHD撮影可能なカメラなら認識できるだろう。
import re
try:
import cv2
from pyzbar.pyzbar import decode
except ImportError as errmsg:
print(f"外部ライブラリをインストールしてください ... {errmsg}")
exit()
# フレームにテキストを描画する
cv2.putText(frame, text, position, font, fontScale, fontColor, fontWeight, lineType)
# フレーム内のバーコードをデコードする
barcodes = decode(frame)
for barcode in barcodes:
# バーコードの種類と値を取得
barcodeType = barcode.type
barcodeData = barcode.data.decode("utf-8")
# ISBN→ASINに変換
try:
asin = isbn2asin(barcodeData)
text = f"ISBN {barcodeData} => https://www.amazon.co.jp/dp/{asin}"
# 失敗したらバーコードタイプと値を表示する
except ValueError as e:
text = f"{barcodeType}: {barcodeData}"
# コンソールに表示する
print(text)
# フレームを表示する
cv2.imshow("Barcode", frame)
# 何かキーが押されたらループから抜ける
if cv2.waitKey(1) != -1:
break
else:
break
# キャプチャをリリースし、ウィンドウを閉じる
メイン・プログラムのうち、上述のプログラム "webcamCapture.py" に付け加えた部分を説明する。
まず外部ライブラリ pyzbar を使ってフレームオブジェクトの中からバーコードを自動的にデコード decode する。引数として、OpenCVでキャプチャしたカメラ画像をどのまま渡すことができる。戻り値として、バーコード・オブジェクトがリストの形で戻る。つまり、1フレームに複数のバーコードがある場合でも認識ができる。
for文を使い、1つ1つのバーコードに対して処理していく。もしバーコードがなければ barcodes は空なので、次のフレームをとりにいく。
バーコードには様々な規格がある。たとえば1次元バーコードであれば、EAN、ITF、CODE39、NW-7など。さらにEANには桁数があり、書籍のバーコードであるISBNはEAN13規格で表される。この規格が barcode.type に代入される。バーコードの値(数字)はバイナリ値で barcode.data に格納されるので、テキスト処理できるよう barcode.data.decode("utf-8") としてUTF-8にデコードする。
バーコードの値を後述するユーザー関数 isbn2asin に渡し、Amazonの商品コード ASIN に変換する。これに "https://www.amazon.co.jp/dp/" を付加すれば、商品URLとなる。
ユーザー関数 isbn2asin で例外が発生したときは、それはISBNコードではないということなので、バーコードタイプとバーコードの値を表示するようにした。
まず外部ライブラリ pyzbar を使ってフレームオブジェクトの中からバーコードを自動的にデコード decode する。引数として、OpenCVでキャプチャしたカメラ画像をどのまま渡すことができる。戻り値として、バーコード・オブジェクトがリストの形で戻る。つまり、1フレームに複数のバーコードがある場合でも認識ができる。
for文を使い、1つ1つのバーコードに対して処理していく。もしバーコードがなければ barcodes は空なので、次のフレームをとりにいく。
バーコードには様々な規格がある。たとえば1次元バーコードであれば、EAN、ITF、CODE39、NW-7など。さらにEANには桁数があり、書籍のバーコードであるISBNはEAN13規格で表される。この規格が barcode.type に代入される。バーコードの値(数字)はバイナリ値で barcode.data に格納されるので、テキスト処理できるよう barcode.data.decode("utf-8") としてUTF-8にデコードする。
バーコードの値を後述するユーザー関数 isbn2asin に渡し、Amazonの商品コード ASIN に変換する。これに "https://www.amazon.co.jp/dp/" を付加すれば、商品URLとなる。
ユーザー関数 isbn2asin で例外が発生したときは、それはISBNコードではないということなので、バーコードタイプとバーコードの値を表示するようにした。
def cd11(code):
"""チェックデジットの計算(モジュラス11 ウェイト10-2)ASIN用
Args:
code(str): 計算するコード(最下位桁がチェックデジット)
Returns:
str: チェックデジット
"""
cd = 0
for pos in range(10, 1, -1):
n = int(code[10 - pos])
cd += n * pos
cd = cd % 11
cd = 11 - cd
if cd == 10:
return "X"
if cd == 11:
return "0"
return str(cd)
def cd10(code):
"""チェックデジットの計算(モジュラス10 ウェイト3)
Args:
code(str): 計算するコード(最下位桁がチェックデジット)
Returns:
str: チェックデジット
"""
cd = 0
for pos in range(13, 1, -1):
n = int(code[13 - pos])
cd += n * (3 if pos % 2 == 0 else 1)
cd = cd % 10
return str(0 if cd == 0 else 10 - cd)
def isbn2asin(isbn):
"""ISBNコードをASINコードに変換する
Args:
isbn(str): ISBNコード
Returns:
str: ASINコード
Raises:
ValueError: ISBNコードではない
"""
# 旧ISBNコードの場合はそのまま返す
if re.match(r"^[0-9]{9}[0-9X]$", isbn):
if cd11(isbn) != isbn[9]:
return False
return isbn
# 入力値チェック
if (not re.match(r"^[0-9]{13}$", isbn)
or cd10(isbn) != isbn[12]
or not re.match(r"^978", isbn)):
raise ValueError(f"{isbn} はISBNコードではありません")
# 10-1桁目を取り出す
code = isbn[3:12]
cd = cd11(code)
return isbn[3:12] + cd
ユーザー関数 isbn2asin は、ISBN を ASIN に変換する関数で、上述の定義に従って変換を行う。
練習問題
次回予告
Python を使うことで、ちょっとした画像処理を自動化できる。
今回利用した外部ライブラリ OpenCV は、カメラ映像をキャプチャするだけでなく、さまざまな画像処理機能を備えている。そこで次回からは、Python を使って簡単な画像処理を行うプログラムを作ってみることにする。
今回利用した外部ライブラリ OpenCV は、カメラ映像をキャプチャするだけでなく、さまざまな画像処理機能を備えている。そこで次回からは、Python を使って簡単な画像処理を行うプログラムを作ってみることにする。
コラム:キャプチャ画像のセキュリティ対策
本編で紹介したプログラムは、PC本体とPC内蔵カメラで完結しており、信頼境界をまたぐデータの受け渡しは発生しない。また、USB接続の外部カメラを使う場合も、そのカメラがWi-Fiなどのネットワーク機能を内蔵していなければ、まあ、信頼境界の内側にあるとみなしていいだろう。
しかし、キャプチャ画像をクラウドサービスに渡して処理させるようなプログラムをつくるときは、信頼境界の外側にデータが出ていくので、セキュリティ対策に留意してほしい。
なぜなら、自宅でカメラ撮影しているときは背景にプライバシー情報が映り込む可能性があるし、職場で撮影しているときには会社の機密情報が映り込む可能性があるからだ。実際、2020年(令和2年)4月に、医学部の学生が患者のカルテ情報が映り込んだ画像をSNSに投稿してしまい、病院が患者に謝罪するという事故があった。
とくにAIサービスにキャプチャ画像を送る場合クラウドサービス側が受け取った画像を学習データとして保存し、利用する場合がある。こうした学習データはネット上で共有されることが多いから、場合によっては、送ったキャプチャ画像の一部を、まったく異なるサービスを利用している第三者が見てしまう恐れもある。
数字やテキストと異なり、画像の出力バリデーションを自動的に行うのは難しい。次章で、画像の中から顔の部分だけを自動的にボカすプログラムを紹介するが、これも完全というわけではないし、顔以外の、たとえば機密文書が映り込んでいた場合には対処できない。また、スマホなどで撮影したJPEG画像には Exif情報というタグ情報の中に位置情報が書き込まれるので、自宅や勤務先が特定される可能性がある。
手間はかかるが、今回のようなライブ・キャプチャ映像を使うのではなく、キャプチャした画像から必要なパーツだけ切り抜き加工して、Exif情報などのタグ情報を消去してからクラウドサービスに渡すようすることをおすすめする。
なぜなら、自宅でカメラ撮影しているときは背景にプライバシー情報が映り込む可能性があるし、職場で撮影しているときには会社の機密情報が映り込む可能性があるからだ。実際、2020年(令和2年)4月に、医学部の学生が患者のカルテ情報が映り込んだ画像をSNSに投稿してしまい、病院が患者に謝罪するという事故があった。
とくにAIサービスにキャプチャ画像を送る場合クラウドサービス側が受け取った画像を学習データとして保存し、利用する場合がある。こうした学習データはネット上で共有されることが多いから、場合によっては、送ったキャプチャ画像の一部を、まったく異なるサービスを利用している第三者が見てしまう恐れもある。
数字やテキストと異なり、画像の出力バリデーションを自動的に行うのは難しい。次章で、画像の中から顔の部分だけを自動的にボカすプログラムを紹介するが、これも完全というわけではないし、顔以外の、たとえば機密文書が映り込んでいた場合には対処できない。また、スマホなどで撮影したJPEG画像には Exif情報というタグ情報の中に位置情報が書き込まれるので、自宅や勤務先が特定される可能性がある。
手間はかかるが、今回のようなライブ・キャプチャ映像を使うのではなく、キャプチャした画像から必要なパーツだけ切り抜き加工して、Exif情報などのタグ情報を消去してからクラウドサービスに渡すようすることをおすすめする。
参考サイト
- SNSにアップした写真から住所が漏れる:ぱふぅ家のホームページ
- 配布ファイルに個人情報を残さない:ぱふぅ家のホームページ
- プライバシー保護のために必要な写真編集:Adobe
- AIモデルから情報流出?学習データを復元する「Model Inversion Attack」を検証:NRIセキュアブログ
(この項おわり)