Python3 Tkinter カメラの映像からバーコードやQRコードを読み取りたい
にて作成したソースコードをラズパイで動かしたときに動作が重くなりました。
Windowsで確認した際はラグはなかったのですが、ラズパイで確認するとtkinterのcanvasの映像が体感的に3秒ほど遅れています。
after()の中にprint()にてタイムスタンプ表示を行うと、指定[ms]に対して少し誤差はあるものの、映像表示ほどの遅れはありません。
指定時間[ms]は10[ms]、100[ms]、1000[ms]で試験しましたが、どれも同じくらい遅いです。
canvasの描画内容をコメントアウトしてバーコードの読み取りのみに変更しても、読み取るのに要する時間(バーコードをカメラの前にもっていってから、tkinterのラベルに表示するまでの時間)が2~3秒と長いような気がします。(単にピントが合っておらず、時間がかかっているときもありますが)
画像一枚に対してのバーコード読み取りや描画処理がラズパイには負担が大きく、処理としてついていけないのでしょうか?
ソースコード
Windowsで試験した際はエラー回避のためcv2.VideoCaputure()の第2引数にcv2.CAP_DSHOWを入れてましたが、
ラズパイでやったらエラーになったので、ここでは指定しません。
Python
1from datetime import datetime 2from PIL import Image, ImageTk 3from pyzbar.pyzbar import decode 4import cv2 5import queue 6import threading 7import time 8import tkinter as tk 9 10decode_cnt = 0 11decode_span = 20 12 13class App: 14 def __init__(self, win): 15 video_source = 0 16 self.vcap = cv2.VideoCapture(video_source) 17 self.vcap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc("H", "2", "6", "4")) 18 self.vcap.set(cv2.CAP_PROP_FRAME_WIDTH, 720) 19 self.vcap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) 20 #self.vcap.set(cv2.CAP_PROP_FPS, 30) 21 self.fourcc = self.vcap.get(cv2.CAP_PROP_FOURCC) 22 self.width = self.vcap.get(cv2.CAP_PROP_FRAME_WIDTH) 23 self.height = self.vcap.get(cv2.CAP_PROP_FRAME_HEIGHT) 24 #self.fps = self.vcap.get(cv2.CAP_PROP_FPS) 25 print("width: {}".format(self.width)) 26 print("height: {}".format(self.height)) 27 self.font = cv2.FONT_HERSHEY_SIMPLEX 28 29 self.label_header = "バーコード:" 30 self.barcodeData = "" 31 32 self.queue_to_subthread = queue.Queue() 33 self.queue_to_mainthread = queue.Queue() 34 35 self.win = win 36 self.win.title("Camera") 37 self.thread = threading.Thread(target=self.get_queue, args=(self.queue_to_mainthread,)) 38 self.thread.start() 39 size_str = str(int(self.width + 100)) + "x" + str(int(self.height + 100)) 40 self.win.geometry(size_str) 41 #self.disp_center(self.win, int(self.width) + 100, int(self.height) + 100) 42 #self.disp_center(self.win, 720, 480) 43 self.win.protocol("WM_DELETE_WINDOW", self.close) 44 self.create_view() 45 self.bind_event() 46 #self.update_by_timer() 47 self.canvas.after(15, self.update_by_timer) 48 49 50 def disp_center(self, win, wx=400, wy=300): 51 #win.resizable(0, 0) 52 dx = win.winfo_screenwidth() 53 dy = win.winfo_screenheight() 54 win_size = str(wx) + "x" + str(wy) + \ 55 "+" + str(int(dx/2 - wx/2)) + "+" + str(int(dy/2 - wy/2)) 56 win.geometry(win_size) 57 58 59 def close(self, e=None): 60 self.queue_to_subthread.put(["close", None]) 61 self.thread.join() 62 self.win.destroy() 63 self.vcap.release() 64 #cv2.destroyAllWindows() 65 66 67 def create_view(self): 68 self.win.rowconfigure(0, weight=1) 69 self.win.columnconfigure(0, weight=1) 70 71 self.frame = tk.Frame(self.win, relief="sunken", bd=1) 72 self.frame.grid(row=0, column=0, sticky="nsew") 73 self.frame.rowconfigure(0, weight=1) 74 self.frame.columnconfigure(0, weight=1) 75 76 self.canvas = tk.Canvas(self.frame) 77 self.canvas.grid(row=0, column=0, sticky="nsew", padx=10, pady=10) 78 79 ret, frm = self.vcap.read() 80 #print("ret: {}".format(ret)) 81 #print("frm: {}".format(frm)) 82 self.frame = cv2.cvtColor(frm, cv2.COLOR_BGR2RGB) 83 self.photo = ImageTk.PhotoImage(image = Image.fromarray(self.frame)) 84 self.canvas.update() 85 cv_w = self.canvas.winfo_reqwidth() 86 cv_h = self.canvas.winfo_reqheight() 87 self.img_id = self.canvas.create_image(cv_w // 2, cv_h // 2, image=self.photo, anchor="center") 88 89 90 """ 91 self.canvas.update() 92 cv_w = self.canvas.winfo_width() 93 cv_h = self.canvas.winfo_height() 94 95 rec_id = self.canvas.create_rectangle(0, 0, cv_w, cv_h, fill="black") 96 97 98 # 長方形の座標取得 99 rec_pos = self.canvas.coords(rec_id) 100 # テキストを描画(位置は適当) 101 text_id = self.canvas.create_text(0, 0, text="NO SIGNAL", font=("", 20), fill="white") 102 # テキストのサイズ取得 103 text_size = self.canvas.bbox(text_id) 104 # テキストの座標移動 105 rc_x = rec_pos[2] / 2 106 rc_y = rec_pos[2] / 3 107 tc_y = text_size[3] / 2 108 self.canvas.move(text_id, rc_x - (rec_pos[0] / 2), rc_y - tc_y) 109 """ 110 111 self.label = tk.Label(self.win) 112 self.label["text"] = self.label_header 113 self.label.grid(row=1, column=0, sticky="", pady=10) 114 115 self.button = tk.Button(self.win, text="Snapshot", command=self.pushed_button) 116 self.button.grid(row=2, column=0, sticky="", pady=10) 117 118 119 def pushed_button(self): 120 self.queue_to_subthread.put(["snapshot", None]) 121 122 123 def update_by_timer(self): 124 global decode_cnt, decode_span 125 ret, self.frame = self.vcap.read() 126 #print("ret: {}".format(ret)) 127 #print("self.frame: {}".format(self.frame)) 128 # cv2はBGR、pillow(PIL)はRGB。色の構成順が異なる 129 self.frame = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB) # フルカラー(色の構成順をRGBへ) 130 #self.frame = cv2.cvtColor(self.frame, cv2.COLOR_BGR2GRAY) # グレースケール 131 132 # 2値化(単純な閾値処理) 133 # https://axa.biopapyrus.jp/ia/opencv/threshold.html 134 #_, self.frame = cv2.threshold(self.frame, 120, 255, cv2.THRESH_BINARY) # 2値化 135 136 # 2値化(適応的閾値処理) 137 # https://webcache.googleusercontent.com/search?q=cache:d8CkW7wbZ5AJ:https://algorithm.joho.info/programming/python/opencv-adaptive-thresholding-py/+&cd=2&hl=ja&ct=clnk&gl=jp 138 #self.frame = cv2.adaptiveThreshold(self.frame, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 3) 139 140 141 142 if ret and decode_cnt % decode_span == 0: 143 self.queue_to_subthread.put(["decode", self.frame]) 144 145 try: 146 msg, d = self.queue_to_mainthread.get_nowait() 147 #print("data: \n{}".format(d)) 148 print(d) 149 x,y,w,h = (0,0,0,0) 150 if d: 151 for barcode in d: 152 x,y,w,h = barcode.rect 153 self.barcodeData = barcode.data.decode("utf-8") 154 155 156 else: 157 self.barcodeData = "" 158 self.label["text"] = self.label_header + self.barcodeData 159 cv2.rectangle(self.frame,(x,y),(x+w,y+h),(0,0,255),2) 160 frame = cv2.putText(self.frame,self.barcodeData,(x,y-10),self.font,.5,(0,0,255),2,cv2.LINE_AA) 161 162 163 except queue.Empty: 164 pass 165 166 self.photo = ImageTk.PhotoImage(image = Image.fromarray(self.frame)) 167 self.canvas.itemconfig(self.img_id, image=self.photo) 168 decode_cnt += 1 169 self.canvas.after(15, self.update_by_timer) 170 171 172 def update_by_resize(self, e): 173 self.canvas.itemconfig(self.img_id, image=self.photo, anchor="center") 174 cv_w = self.canvas.winfo_width() 175 cv_h = self.canvas.winfo_height() 176 self.canvas.coords(self.img_id, cv_w // 2, cv_h // 2) 177 #pass 178 179 180 def bind_event(self): 181 self.win.bind("<Escape>", self.close) 182 self.canvas.bind("<Configure>", self.update_by_resize) 183 184 185 def get_queue(self, queue_to_mainthread): 186 while True: 187 msg, data = self.queue_to_subthread.get() 188 if msg == "decode": 189 d = decode(data) 190 if d: 191 queue_to_mainthread.put(["result", d]) 192 else: 193 queue_to_mainthread.put(["result", None]) 194 elif msg == "snapshot": 195 self.snapshot() 196 elif msg == "close": 197 break 198 else: 199 print("----- Undefined msg -----") 200 break 201 202 def snapshot(self): 203 filename = "/home/pi/デスクトップ/img/frame-" + datetime.now().strftime("%Y-%m-%d-%H-%M-%S_%f") + ".jpg" 204 cv2.imwrite(filename, 205 cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB)) 206 print(filename) 207 208 209def main(): 210 win = tk.Tk() 211 App(win) 212 win.mainloop() 213 214if __name__ == "__main__": 215 main()
ラズパイのバージョン
$ lsb_release -a No LSB modules are available. Distributor ID: Raspbian Description: Raspbian GNU/Linux 9.13 (stretch) Release: 9.13 Codename: stretch
パッケージインストール方法等
$ sudo pip3 install opencv-python $ sudo pip3 install pyzbar $ sudo pip3 list opencv-python==4.4.0.42 pyzbar==0.1.8 (他省略)
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/05/18 07:20
2021/05/18 07:50
2021/05/18 08:18
2021/05/18 08:43
2021/05/18 09:05
2021/05/19 00:54
2021/05/19 01:32 編集
2021/05/19 02:13
2021/05/19 02:26 編集
2021/05/19 02:29
2021/05/19 02:46 編集
2021/05/19 03:48 編集
2021/05/19 03:53
2021/05/19 04:05
2021/05/19 04:15 編集
2021/05/19 04:35
2021/05/19 05:01 編集
2021/05/19 05:11