ขั้นตอนการทำงาน
เริ่มต้นจากการใช้ Raspberry Pi camera จับภาพ QR code เปลี่ยนข้อมูลภาพให้กลายเป็น Numpy ndarray [1] ส่งต่อไปยัง pyzbar module เพื่อทำการ decode ต่อไป
เหตุผลที่ใช้ Numpy ndarray แทนที่จะใช้เป็น image file (กล้องจับภาพแล้วบันทึกเป็นแฟ้มภาพก่อน) ก็เพราะผมเห็นว่าข้อมูลภาพที่ได้ไม่ได้มีขนาดใหญ่โต อีกทั้งเป็นภาพขาว-ดำ ซึ่งใช้พื้นที่ในหน่วยความจำไม่มาก และเป็นลดภาระงานการเขียน-อ่านไฟล์อีกด้วย
รู้จัก pyzbar Data Fields
ข้อมูลที่ได้จากการ decode โดย pyzbar ที่สำคัญมี 3 fields คือ (ใน source code จะมี 4 fields)
1. data : เป็นข้อมูลแบบข้อความที่บรรจุอยู่ใน QR code
2. type : ชนิดของ code ที่อ่านได้ รุ่นปัจจุบันอ่านได้เฉพาะ QR code
3. polygon : คือ location หรือ จุดที่ระบุมุมทั้งสี่ของ QR Code ในระพิกัด (x,y coordinate)
4. rect : ให้ความหมายเช่นเดียวกับ polygon แต่อยู่ในรูปแบบของ tuple (left, top, width, height) ซึ่งง่ายต่อการนำไปใช้วาดกรอบด้วย PyQt
1. ติดตั้ง Modules ใช้งาน
1.1 ติดตั้ง Pillow (PIL) เป็น Python Image Library
$ sudo python3 -m pip intalll -U Pillow
1.2 ติดตั้ง pyzbar
1.2.1 ติดตั้ง dependencies
$ sudo apt-get update
$ sudo apt-get install libzbar0 libzbar-dev
1.2.2 ติดตั้ง pyzbar [3]
$ sudo python3 -m pip intalll pyzbar
2. ติดตั้ง picamera
การจะใช้งาน Camera Module ต้องได้รับการติดตั้งและทำการ enable camera module เสียก่อน [2]
3. ติดตั้ง PyQt5
$ sudo apt-get python3-pyqt5
Full Source Code [4]
import sys
import numpy as np
from PyQt5 import QtCore
from PyQt5 import QtWidgets
from PyQt5 import QtGui
from picamera import PiCamera
from pyzbar.pyzbar import decode
class VideoRecorder(QtCore.QObject):
# To take a snapshot one at a time then emit out in term on numpy ndarray
image_data = QtCore.pyqtSignal(np.ndarray)
def __init__(self, camera_port=0, parent=None):
super().__init__(parent)
self.resolution = (320,240)
self.camera = PiCamera()
self.camera.resolution=self.resolution
self.timer = QtCore.QBasicTimer()
def start_recording(self):
self.timer.start(0, self)
def timerEvent(self, event):
if (event.timerId() != self.timer.timerId()):
return
frame = np.empty((self.resolution[1],self.resolution[0],3),dtype=np.uint8)
#capture image
self.camera.capture(frame,'rgb')
#emit image out
self.image_data.emit(frame)
class DecoderWidget(QtWidgets.QWidget):
#To display image array and do decoding
def __init__(self, parent=None):
super().__init__(parent)
self.qr_data = []
self.image = QtGui.QImage()
self._red = (0, 0, 255)
self._width = 2
self._min_size = (30, 30)
def _decode(self):
if self.qr_data != [] :
msg = QtWidgets.QMessageBox()
msg.setIcon(QtWidgets.QMessageBox.Information)
msg.setWindowTitle("QR Code Data ")
msg.setDetailedText(self.qr_data[0].data.decode('utf-8'))
retval = msg.exec_()
def image_data_slot(self, image_data: np.ndarray):
self.qr_data = decode(image_data)
self.image = self.get_qimage(image_data)
if self.image.size() != self.size():
self.setFixedSize(self.image.size())
self.update()
def get_qimage(self, image: np.ndarray):
height, width, colors = image.shape
bytesPerLine = 3 * width
QImage = QtGui.QImage
image = QImage(image.data, width, height, bytesPerLine, QImage.Format_RGB888)
image = image.rgbSwapped()
return image
def paintEvent(self, event):
painter = QtGui.QPainter(self)
painter.drawImage(0, 0, self.image)
self.image = QtGui.QImage()
if self.qr_data != [] :
col = QtGui.QColor(200, 0, 0)
col.setNamedColor('#d4d4d4')
painter.setPen(col)
#painter.setBrush(QtGui.QColor(200, 0, 0))
rect = self.qr_data[0].rect
painter.drawRect(rect.left, rect.top, rect.width, rect.height)
class MainWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.decoder_widget = DecoderWidget()
self.recorder = VideoRecorder()
image_data_slot = self.decoder_widget.image_data_slot
self.recorder.image_data.connect(image_data_slot)
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.decoder_widget)
self.run_button = QtWidgets.QPushButton('Decode')
layout.addWidget(self.run_button)
self.run_button.clicked.connect(self.decoder_widget._decode)
self.setLayout(layout)
self.recorder.start_recording()
def main():
app = QtWidgets.QApplication(sys.argv)
main_window = QtWidgets.QMainWindow()
main_widget = MainWidget()
main_window.setCentralWidget(main_widget)
main_window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
ผลการทำงาน
โปรแกรมตรวจหาตำแหน่งของ QR Code ในภาพที่ได้จากกล้อง กรอบสีขาวแสดงว่าโปรแกรมได้ตรวจพบ QRCode แล้ว |
คลิ๊กปุ่ม "Decode" เพื่อทำการดึงข้อมูลออกจาก QR Code ในภาพแสดงข้อความที่อยู่ใน QR Code |
เอกสารอ้างอิง
[1] https://docs.scipy.org/doc/numpy-1.14.0/reference/generated/numpy.ndarray.html
[2] https://www.raspberrypi.org/documentation/usage/camera/
[3] https://pypi.org/project/pyzbar/
[4] https://github.com/somchaisomph/blogs/blob/master/qrcode/qt5_decoder.py
Sign up here with your email
ConversionConversion EmoticonEmoticon