使用OpenCV实时读取QR码时触发cv2.error错误的排查求助
解决QR码打卡应用的相机报错及持续运行问题
问题场景
开发QR码上下班打卡应用时,系统基础功能正常,但相机启动后不久触发错误:
invalid qr code source points (expected: 'contourarea(src_points) > 0.0'), where 'contourarea(src_points)' is 0 must be greater than '0.0' is 0.
报错关联代码片段:
while (capture.isOpened()): _,frame = capture.read() if (cv2.waitKey(1)== ord('s')): break qrDetector = cv2.QRCodeDetector() data,bbox, rectifiedImage = qrDetector.detectAndDecode(frame) fecha = date.today() now = datetime.now() h=now.time() if len(data) > 0: # 报错触发行 arr = data.replace("\'", "\"") arreglo = json.loads(arr) cedula = arreglo["Cedula"] nombre = arreglo["Nombre"] sql ='SELECT asi_id,asi_estado FROM asistencia WHERE asi_id = (select MAX(asi_id)from asistencia WHERE asi_cedula = %s)' cursor.execute(sql,(cedula,)) datos = cursor.fetchone() id = datos[0] estado = datos[1] if(cedula == cedula and estado == None or estado != "Entrada" ): asistencia.lbestado.setText("Entrada") estado1 = "Entrada" sqli = 'INSERT INTO asistencia(asi_cedula,asi_nombre,asi_hora_e,asi_fecha,asi_estado)VALUES(%s,%s,%s,%s,%s)' datos = (cedula,nombre,h,fecha,estado1) cursor.execute(sqli,datos) db.connection.commit() else: asistencia.lbestado.setText("Salida") estado2 = "Salida" sql2 = 'UPDATE asistencia SET asi_hora_s = %s, asi_estado = %s WHERE asi_cedula = %s AND asi_id = %s' d2 = (h,estado2,cedula,id) cursor.execute(sql2,d2) db.connection.commit() winsound.Beep(f,d) time.sleep(2) else: image = qimage2ndarray.array2qimage(frame) asistencia.lbvista.setPixmap(QPixmap.fromImage(image))
需求:相机持续保持开启状态,直到用户主动关闭应用。
问题原因
- QR检测逻辑不严谨:仅通过
len(data) > 0判断是否检测到二维码,但OpenCV的detectAndDecode可能在识别到无效轮廓(面积为0)时返回空data,同时触发内部错误。 - 重复创建检测器:每次循环都初始化
QRCodeDetector,既浪费资源也可能导致检测不稳定。 - 无空帧处理:未判断
capture.read()是否成功读取帧,空帧会导致后续检测逻辑出错。 - 数据库查询未做空值判断:新用户首次打卡时,查询不到历史记录会触发索引错误。
解决方案及优化代码
# 初始化QR检测器(放到循环外,避免重复创建) qrDetector = cv2.QRCodeDetector() while capture.isOpened(): ret, frame = capture.read() # 检查是否成功读取相机帧 if not ret: print("相机帧读取失败") break # 监听退出按键(可根据UI需求替换为窗口关闭事件) if cv2.waitKey(1) == ord('s'): break data, bbox, rectifiedImage = qrDetector.detectAndDecode(frame) fecha = date.today() now = datetime.now() h = now.time() # 先验证二维码轮廓有效性,再处理数据 if bbox is not None and len(data) > 0: arr = data.replace("\'", "\"") arreglo = json.loads(arr) cedula = arreglo["Cedula"] nombre = arreglo["Nombre"] sql = 'SELECT asi_id, asi_estado FROM asistencia WHERE asi_id = (SELECT MAX(asi_id) FROM asistencia WHERE asi_cedula = %s)' cursor.execute(sql, (cedula,)) datos = cursor.fetchone() # 处理无历史记录的情况(新用户首次打卡) if datos is not None: id = datos[0] estado = datos[1] # 简化条件判断,去掉冗余逻辑 if estado is None or estado != "Entrada": asistencia.lbestado.setText("Entrada") estado1 = "Entrada" sqli = 'INSERT INTO asistencia(asi_cedula, asi_nombre, asi_hora_e, asi_fecha, asi_estado) VALUES(%s, %s, %s, %s, %s)' datos_insert = (cedula, nombre, h, fecha, estado1) cursor.execute(sqli, datos_insert) db.connection.commit() else: asistencia.lbestado.setText("Salida") estado2 = "Salida" sql2 = 'UPDATE asistencia SET asi_hora_s = %s, asi_estado = %s WHERE asi_cedula = %s AND asi_id = %s' d2 = (h, estado2, cedula, id) cursor.execute(sql2, d2) db.connection.commit() winsound.Beep(f, d) time.sleep(2) else: # 新用户首次打卡直接记录Entrada asistencia.lbestado.setText("Entrada(新用户)") estado1 = "Entrada" sqli = 'INSERT INTO asistencia(asi_cedula, asi_nombre, asi_hora_e, asi_fecha, asi_estado) VALUES(%s, %s, %s, %s, %s)' datos_insert = (cedula, nombre, h, fecha, estado1) cursor.execute(sqli, datos_insert) db.connection.commit() winsound.Beep(f, d) time.sleep(2) else: # 实时显示相机画面 image = qimage2ndarray.array2qimage(frame) asistencia.lbvista.setPixmap(QPixmap.fromImage(image)) # 程序结束时释放相机资源 capture.release() cv2.destroyAllWindows()
关键修改说明
- 提前初始化检测器:将
QRCodeDetector放到循环外,减少对象创建开销,提升检测稳定性。 - 增加有效性判断:通过
bbox is not None确保检测到的二维码轮廓有效,避免触发contour面积为0的错误。 - 空帧处理:添加
if not ret判断,防止空帧导致后续逻辑崩溃。 - 数据库空值处理:新增
datos is not None判断,支持新用户首次打卡场景。 - 优化退出逻辑:若要实现“直到用户关闭应用”,可将按键退出替换为UI窗口的关闭事件(比如Qt的
closeEvent),在事件处理中执行capture.release()并终止循环。
内容的提问来源于stack exchange,提问作者vinicio campoverde
相关产品推荐
相关产品推荐

