|
| 1 | +from __future__ import print_function |
| 2 | +import re |
| 3 | + |
| 4 | +import numpy as np |
| 5 | +import cv2 as cv |
| 6 | + |
| 7 | +from multiprocessing.pool import ThreadPool |
| 8 | +from collections import deque |
| 9 | + |
| 10 | +import dbr |
| 11 | +from dbr import * |
| 12 | + |
| 13 | +import time |
| 14 | +from util import * |
| 15 | + |
| 16 | +BarcodeReader.init_license("DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ==") |
| 17 | + |
| 18 | +class ScanManager: |
| 19 | + MODE_AUTO_STITCH = 0 |
| 20 | + MODE_MANUAL_STITCH = 1 |
| 21 | + MODE_CAMERA_ONLY = 2 |
| 22 | + |
| 23 | + def __init__(self): |
| 24 | + modes = (cv.Stitcher_PANORAMA, cv.Stitcher_SCANS) |
| 25 | + self.stitcher = cv.Stitcher.create(modes[1]) |
| 26 | + self.stitcher.setPanoConfidenceThresh(0.1) |
| 27 | + self.panorama = [] |
| 28 | + self.isPanoramaDone = False |
| 29 | + self.reader = BarcodeReader() |
| 30 | + |
| 31 | + def count_barcodes(self, frame): |
| 32 | + try: |
| 33 | + results = self.reader.decode_buffer(frame) |
| 34 | + return len(results) |
| 35 | + except BarcodeReaderError as e: |
| 36 | + print(e) |
| 37 | + |
| 38 | + return 0 |
| 39 | + |
| 40 | + def save_frame(self, frame): |
| 41 | + # frame = self.frame_overlay(frame) |
| 42 | + filename = str(time.time()) + "_panorama.jpg" |
| 43 | + cv.imwrite(filename, frame) |
| 44 | + print("Saved to " + filename) |
| 45 | + |
| 46 | + def frame_overlay(self, frame): |
| 47 | + frame_cp = frame.copy() |
| 48 | + try: |
| 49 | + results = self.reader.decode_buffer(frame_cp) |
| 50 | + if results != None: |
| 51 | + for result in results: |
| 52 | + points = result.localization_result.localization_points |
| 53 | + cv.line(frame_cp, points[0], points[1], (0,255,0), 2) |
| 54 | + cv.line(frame_cp, points[1], points[2], (0,255,0), 2) |
| 55 | + cv.line(frame_cp, points[2], points[3], (0,255,0), 2) |
| 56 | + cv.line(frame_cp, points[3], points[0], (0,255,0), 2) |
| 57 | + cv.putText(frame_cp, result.barcode_text, points[0], cv.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,255)) |
| 58 | + |
| 59 | + return frame_cp |
| 60 | + except BarcodeReaderError as e: |
| 61 | + print(e) |
| 62 | + return None |
| 63 | + |
| 64 | + def stitch_frame(self, frame): |
| 65 | + try: |
| 66 | + results = self.reader.decode_buffer(frame) |
| 67 | + if results != None: |
| 68 | + # Draw results on the copy of the frame. Keep original frame clean. |
| 69 | + frame_cp = frame.copy() |
| 70 | + for result in results: |
| 71 | + points = result.localization_result.localization_points |
| 72 | + cv.line(frame_cp, points[0], points[1], (0,255,0), 2) |
| 73 | + cv.line(frame_cp, points[1], points[2], (0,255,0), 2) |
| 74 | + cv.line(frame_cp, points[2], points[3], (0,255,0), 2) |
| 75 | + cv.line(frame_cp, points[3], points[0], (0,255,0), 2) |
| 76 | + cv.putText(frame_cp, result.barcode_text, points[0], cv.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,255)) |
| 77 | + |
| 78 | + # Save frame and barcode info if panorama is empty |
| 79 | + if len(self.panorama) == 0: |
| 80 | + self.panorama.append((frame, results, frame_cp)) |
| 81 | + else: |
| 82 | + # Compare results. If there is an intersection, transform and stitch. Otherwise, discard. |
| 83 | + preFrame = self.panorama[0][0] |
| 84 | + preResults = self.panorama[0][1] |
| 85 | + preFrameCp = self.panorama[0][2] |
| 86 | + |
| 87 | + while len(results) > 0: |
| 88 | + result = results.pop() |
| 89 | + for preResult in preResults: |
| 90 | + if preResult.barcode_text == result.barcode_text and preResult.barcode_format == result.barcode_format: |
| 91 | + prePoints = preResult.localization_result.localization_points |
| 92 | + # preContour = np.array([prePoints[0], prePoints[1], prePoints[2], prePoints[3]]) |
| 93 | + # preArea = cv.minAreaRect(preContour) |
| 94 | + # preAreaSize = preArea[1][0] * preArea[1][1] |
| 95 | + # preBounding = cv.boxPoints(preArea) |
| 96 | + |
| 97 | + points = result.localization_result.localization_points |
| 98 | + |
| 99 | + # # Crop image based on min area rect |
| 100 | + preFrame = preFrame[0: preFrame.shape[0], 0: max(prePoints[0][0], prePoints[1][0], prePoints[2][0], prePoints[3][0]) + 10] |
| 101 | + frame = frame[0: frame.shape[0], max(points[0][0], points[1][0], points[2][0], points[3][0]): frame.shape[1] + 10] |
| 102 | + |
| 103 | + preFrameCp = preFrameCp[0: preFrameCp.shape[0], 0: max(prePoints[0][0], prePoints[1][0], prePoints[2][0], prePoints[3][0]) + 10] |
| 104 | + frame_cp = frame_cp[0: frame_cp.shape[0], max(points[0][0], points[1][0], points[2][0], points[3][0]): frame_cp.shape[1] + 10] |
| 105 | + |
| 106 | + # # Stitch images |
| 107 | + frame = concat_images([preFrame, frame]) |
| 108 | + frame_cp = concat_images([preFrameCp, frame_cp]) |
| 109 | + |
| 110 | + # Re-detect barcodes from the new image |
| 111 | + results = self.reader.decode_buffer(frame) |
| 112 | + |
| 113 | + # Save results |
| 114 | + self.panorama = [(frame, results, frame_cp)] |
| 115 | + return frame, frame_cp |
| 116 | + |
| 117 | + return self.panorama[0][0], self.panorama[0][2] |
| 118 | + |
| 119 | + except BarcodeReaderError as e: |
| 120 | + print(e) |
| 121 | + return None, None |
| 122 | + |
| 123 | + return None, None |
| 124 | + |
| 125 | + |
| 126 | + def process_frame(self, frame): |
| 127 | + results = None |
| 128 | + try: |
| 129 | + results = self.reader.decode_buffer(frame) |
| 130 | + except BarcodeReaderError as bre: |
| 131 | + print(bre) |
| 132 | + |
| 133 | + return results |
| 134 | + |
| 135 | + def clean_deque(self, tasks): |
| 136 | + while len(tasks) > 0: |
| 137 | + tasks.popleft() |
| 138 | + |
| 139 | + def close_window(self, window_name): |
| 140 | + try: |
| 141 | + cv.destroyWindow(window_name) |
| 142 | + except: |
| 143 | + pass |
| 144 | + |
| 145 | + def run(self): |
| 146 | + import sys |
| 147 | + try: |
| 148 | + fn = sys.argv[1] |
| 149 | + except: |
| 150 | + fn = 0 |
| 151 | + cap = cv.VideoCapture(fn) |
| 152 | + |
| 153 | + threadn = 1 # cv.getNumberOfCPUs() |
| 154 | + barcodePool = ThreadPool(processes = threadn) |
| 155 | + panoramaPool = ThreadPool(processes = threadn) |
| 156 | + cameraTasks = deque() |
| 157 | + panoramaTask = deque() |
| 158 | + mode = self.MODE_CAMERA_ONLY |
| 159 | + image = None |
| 160 | + imageCp = None |
| 161 | + panoramaImage = None |
| 162 | + panoramaImageCp = None |
| 163 | + |
| 164 | + while True: |
| 165 | + ret, frame = cap.read() |
| 166 | + frame_cp = frame.copy() |
| 167 | + cv.putText(frame, 'A: auto pano, M: manual pano, C: capture, O: camera, S: stop', (10, 20), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,255)) |
| 168 | + cv.putText(frame, 'Barcode & QR Code Scanning ...', (10, 50), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0)) |
| 169 | + |
| 170 | + # Scan and show barcode & QR code results |
| 171 | + while len(cameraTasks) > 0 and cameraTasks[0].ready(): |
| 172 | + results = cameraTasks.popleft().get() |
| 173 | + if results != None: |
| 174 | + for result in results: |
| 175 | + points = result.localization_result.localization_points |
| 176 | + cv.line(frame, points[0], points[1], (0,255,0), 2) |
| 177 | + cv.line(frame, points[1], points[2], (0,255,0), 2) |
| 178 | + cv.line(frame, points[2], points[3], (0,255,0), 2) |
| 179 | + cv.line(frame, points[3], points[0], (0,255,0), 2) |
| 180 | + cv.putText(frame, result.barcode_text, points[0], cv.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,255)) |
| 181 | + |
| 182 | + if len(cameraTasks) < threadn: |
| 183 | + task = barcodePool.apply_async(self.process_frame, (frame_cp, )) |
| 184 | + cameraTasks.append(task) |
| 185 | + |
| 186 | + # Stitch images for panorama |
| 187 | + if mode == self.MODE_MANUAL_STITCH: |
| 188 | + cv.putText(frame, 'Manual Panorama ...', (10, 70), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0)) |
| 189 | + elif mode == self.MODE_AUTO_STITCH: |
| 190 | + cv.putText(frame, 'Auto Panorama ...', (10, 70), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0)) |
| 191 | + if not self.isPanoramaDone and len(panoramaTask) < threadn: |
| 192 | + task = panoramaPool.apply_async(self.stitch_frame, (frame_cp, )) |
| 193 | + panoramaTask.append(task) |
| 194 | + |
| 195 | + if mode == self.MODE_MANUAL_STITCH or mode == self.MODE_AUTO_STITCH: |
| 196 | + while len(panoramaTask) > 0 and panoramaTask[0].ready(): |
| 197 | + image, imageCp = panoramaTask.popleft().get() |
| 198 | + if image is not None: |
| 199 | + panoramaImage = image.copy() |
| 200 | + panoramaImageCp = imageCp.copy() |
| 201 | + cv.imshow('panorama', panoramaImageCp) |
| 202 | + |
| 203 | + # Key events |
| 204 | + ch = cv.waitKey(1) |
| 205 | + if ch == 27: |
| 206 | + break |
| 207 | + if ord('o') == ch: |
| 208 | + self.close_window('panorama') |
| 209 | + self.isPanoramaDone = True |
| 210 | + mode = self.MODE_CAMERA_ONLY |
| 211 | + self.clean_deque(panoramaTask) |
| 212 | + elif ord('a') == ch: |
| 213 | + self.close_window('panorama') |
| 214 | + self.isPanoramaDone = False |
| 215 | + mode = self.MODE_AUTO_STITCH |
| 216 | + self.clean_deque(panoramaTask) |
| 217 | + self.panorama = [] |
| 218 | + elif ord('m') == ch: |
| 219 | + self.close_window('panorama') |
| 220 | + self.isPanoramaDone = False |
| 221 | + mode = self.MODE_MANUAL_STITCH |
| 222 | + self.clean_deque(panoramaTask) |
| 223 | + self.panorama = [] |
| 224 | + elif ord('c') == ch and mode == self.MODE_MANUAL_STITCH and not self.isPanoramaDone: |
| 225 | + if len(panoramaTask) < threadn: |
| 226 | + task = panoramaPool.apply_async(self.stitch_frame, (frame_cp, )) |
| 227 | + panoramaTask.append(task) |
| 228 | + ################################################### Test image operations |
| 229 | + elif ord('x') == ch: |
| 230 | + if panoramaImageCp is not None: |
| 231 | + panoramaImageCp = shiftX(panoramaImageCp, 5) |
| 232 | + cv.imshow('panorama', panoramaImageCp) |
| 233 | + elif ord('t') == ch: |
| 234 | + if panoramaImageCp is not None: |
| 235 | + panoramaImageCp = concat_images([panoramaImageCp, frame]) |
| 236 | + cv.imshow('panorama', panoramaImageCp) |
| 237 | + elif ord('y') == ch: |
| 238 | + if panoramaImageCp is not None: |
| 239 | + panoramaImageCp = shiftY(panoramaImageCp, 5) |
| 240 | + cv.imshow('panorama', panoramaImageCp) |
| 241 | + elif ord('z') == ch: |
| 242 | + if panoramaImageCp is not None: |
| 243 | + panoramaImageCp = zoom_image(panoramaImageCp, 2) |
| 244 | + cv.imshow('panorama', panoramaImageCp) |
| 245 | + elif ord('r') == ch: |
| 246 | + if panoramaImageCp is not None: |
| 247 | + panoramaImageCp = rotate_image(panoramaImageCp, 1) |
| 248 | + cv.imshow('panorama', panoramaImageCp) |
| 249 | + ################################################### |
| 250 | + |
| 251 | + # Quit panorama mode |
| 252 | + if self.isPanoramaDone: |
| 253 | + self.close_window('panorama') |
| 254 | + mode = self.MODE_CAMERA_ONLY |
| 255 | + self.clean_deque(panoramaTask) |
| 256 | + self.isPanoramaDone = False |
| 257 | + if panoramaImage is not None: |
| 258 | + self.save_frame(panoramaImage) |
| 259 | + panoramaImage = None |
| 260 | + |
| 261 | + cv.imshow('Barcode & QR Code Scanner', frame) |
| 262 | + |
| 263 | + cv.destroyAllWindows() |
| 264 | + print('Done') |
| 265 | + |
| 266 | + |
| 267 | +if __name__ == '__main__': |
| 268 | + ScanManager().run() |
| 269 | + |
0 commit comments