python - 如何通过 OpenCv 更有效地使用 SQL 查询,而不必每帧都插入数据
问题描述
我正在研究面部识别软件作为一种爱好,以了解有关如何使用 OpenCV 的更多信息。
我的软件包含一个检测和生成 20 个数据集图像的功能,并将其与我在数据库中分配的用户 ID 一起保存,然后对其进行训练并将其添加到我用来识别的“.trainedData.yml”文件中面孔。
我有一个 SQL 数据库,用于存储人们的个人资料并使用我的网络摄像头捕捉视频。
当我运行当前软件时,在每一帧中,我都会检查置信度,如果它足够好,我会在数据库中查找以返回找到的配置文件的信息。之后我调用一个函数来插入数据库数据,这样我就可以跟踪检测到面部的时间和地点。
问题:每帧都会处理这个插入查询,创建大量插入,有时甚至在同时完成多个插入时失败。
有没有一种方法可以提高效率,而不必每帧插入数据并且可能每 5 或 10 秒插入一次?
请在下面找到我的代码
先感谢您,
import cv2
from dbconnect import mySQL
import geocoder
faceDetect=cv2.CascadeClassifier('Classifiers\haarcascade_frontalface_alt.xml')
cam = cv2.VideoCapture(0)
rec = cv2.face.LBPHFaceRecognizer_create()
rec.read(r'trainner\trainningData.yml')
def getProfile(Id):
mySQL.execute("SELECT * FROM people where id ="+ str(Id))
cursor = mySQL.fetchall()
print(cursor)
profile = None
for row in cursor:
profile = row
#mySQL.close()
return profile
def insertProfile(Id):
try:
query = "insert into memberLocation values (null,"+ str(Id) +", now(), 'LOCATION DETECTED')"
print(query)
mySQL.execute(query)
print("Entry inserted successfuly")
mySQL.commit()
except:
print("Failed to insert user " + str(Id))
def draw_border(frame, pt1, pt2, color, thickness, r, d):
x1,y1 = pt1
x2,y2 = pt2
# Top left
cv2.line(frame, (x1 + r, y1), (x1 + r + d, y1), color, thickness)
cv2.line(frame, (x1, y1 + r), (x1, y1 + r + d), color, thickness)
cv2.ellipse(frame, (x1 + r, y1 + r), (r, r), 180, 0, 90, color, thickness)
# Top right
cv2.line(frame, (x2 - r, y1), (x2 - r - d, y1), color, thickness)
cv2.line(frame, (x2, y1 + r), (x2, y1 + r + d), color, thickness)
cv2.ellipse(frame, (x2 - r, y1 + r), (r, r), 270, 0, 90, color, thickness)
# Bottom left
cv2.line(frame, (x1 + r, y2), (x1 + r + d, y2), color, thickness)
cv2.line(frame, (x1, y2 - r), (x1, y2 - r - d), color, thickness)
cv2.ellipse(frame, (x1 + r, y2 - r), (r, r), 90, 0, 90, color, thickness)
# Bottom right
cv2.line(frame, (x2 - r, y2), (x2 - r - d, y2), color, thickness)
cv2.line(frame, (x2, y2 - r), (x2, y2 - r - d), color, thickness)
cv2.ellipse(frame, (x2 - r, y2 - r), (r, r), 0, 0, 90, color, thickness)
video_capture = cv2.VideoCapture(0)
font = cv2.FONT_HERSHEY_SIMPLEX
while True:
# try to get coordinates
# g = geocoder.ip('me') # coordinates are way off correct but pulling internet service location not current location
# print(g.latlng)
# Capture frame-by-frame
ret, frame = video_capture.read()
if ret==False:
continue
frame = cv2.flip(frame, 1) # Flip image
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = faceDetect.detectMultiScale(
gray,
scaleFactor=1.1,
minNeighbors=5,
minSize=(30, 30),
flags=cv2.CASCADE_SCALE_IMAGE
)
for (x, y, w, h) in faces:
draw_border(frame, (x, y), (x + w, y + h), (255, 0, 105),4, 15, 10)
#cv2.rectangle(frame, (x,y), (x+w, y+h), (0,0,255), 2)
roi_gray = gray[y:y+h, x:x+w]
roi_color = frame[y:y+h, x:x+w]
nbr_predicted, conf = rec.predict(gray[y:y+h, x:x+w])
if conf < 70:
profile=getProfile(nbr_predicted)
if profile != None:
cv2.putText(frame, "Confidence Level: "+str(round(conf,2))+ "%", (x, y+h+30), font, 0.4, (255, 0, 105), 1)
cv2.putText(frame, "Name: "+str(profile[1]), (x, y+h+50), font, 0.4, (255, 0, 105), 1)
cv2.putText(frame, "Age: " + str(profile[2]), (x, y + h + 70), font, 0.4, (255, 0, 105), 1)
cv2.putText(frame, "Gender: " + str(profile[3]), (x, y + h + 90), font, 0.4, (255, 0, 105), 1)
#call function to insert into database
insertProfile(nbr_predicted)
else:
cv2.putText(frame, "Confidence Level: "+str(round(conf,2))+ "%", (x, y+h+30), font, 0.4, (0, 0, 255), 1)
cv2.putText(frame, "Name: Unknown", (x, y + h + 50), font, 0.4, (0, 0, 255), 1)
cv2.putText(frame, "Age: Unknown", (x, y + h + 70), font, 0.4, (0, 0, 255), 1)
cv2.putText(frame, "Gender: Unknown", (x, y + h + 90), font, 0.4, (0, 0, 255), 1)
# Display the resulting frame
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
video_capture.release()
cv2.destroyAllWindows()
解决方案
您可以列出收集nbr_predicted
实例的位置,为每个成功识别添加一个计数器和一个参数(each_N_passes);然后在每个 N 识别处进行批量插入:
nbr_predicted_list = []
passes = 0
batchStep = 10
while True:
...
if conf < 70:
#Instead of this:
#call function to insert into database
#insertProfile(nbr_predicted)
#DO:
passes++
nbr_predicted_list.append(nbr_predicted)
if passes % batchPasses == 0:
for pr in nbr_predicted_list:
insertProfile(pr)
nbr_predicted_list.clear()
另外,尝试增加延迟时间: if cv2.waitKey(1) & 0xFF == ord('q'):
,除非最大帧率很关键。
改成这样说:
cv2.waitKey(delay)
在哪里:
delay = 1000/frameRate # ms
(由于识别时间等原因,实际帧率会更低。)
...
如果您可以检查重复的配置文件并且不要在批次中插入两次(如果需要),也可以减少事务的数量,如果您在 getProfile 中使用 Id 读取配置文件后缓存(如果它们在跑)。
如果它们被缓存,则在下一次调用中从字典中获取 Id 并返回配置文件而不使用 SQL 事务。
如果需要速度,请尝试删除过多的打印调用(或使它们具有尽可能短的输出和更少的新行/终端滚动),日志记录可能会更快。
更复杂的解决方案可能使用线程或多处理。防止插入竞争条件的一种方法可能是在每次调用数据库之间添加短 cv.waitKey 或休眠 1 毫秒或其他任何时间;不过最好在一个线程中。
请注意,如果您将 SQL 访问延迟太多并收集太多候选插入(如果说使用最大 30 fps、延迟 33 和 10 秒,如果每帧有识别:300 个项目),并且如果您将它们推送到主线程一次,这可能会阻塞,并且可能会在这些传输期间“限制”视频帧速率。
...
另一个一般的想法可能是插入数据库的函数不仅接收一个,而且接收许多收集的数据项,然后调用该函数一次插入许多项:可能 SQL 命令本身和数据库方案可以是这样的一个 INSERT 添加许多行,如果这可以解决太多简单单个事务的瓶颈。
例如,可以在中间表中一次添加许多数据项作为 varchar 行,其中以 CSV 格式等编码的项目作为字符串。然后一些更复杂的 SQL 命令或另一个进程或线程中的另一个脚本可以监视该表,对新行进行采样(SELECT * where time ... 在一些最后采样的当前时间或某个 id 等之后),解析它们然后插入将解析的项目放入另一个具有默认“未压缩”结构的“最终”表中,同时在需要时添加睡眠等。
推荐阅读
- globals - Is there a way to access a variable from a calling function in python?
- java - 为什么我的邻接列表给了我一个 ClassCast 异常?
- swiftui - lineLimit in View does not work in SwiftUI
- c++ - CUDA kernel printf() produces no output in terminal, works in profiler
- javascript - How to capture choice from a list nested with a radio button
- postgresql - Is jsonb conversion to text deterministic?
- java - Invalid value entry in Sharedpreferences
- python - 无法让 Django 的 get_absolute_url 正确设置链接
- xml - Trying to understand XPATH Filtering for Windows Event Logs (XML)
- javascript - 问题设置和注入 chrome 扩展内容脚本