首页 > 解决方案 > 如何通过 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()

标签: pythonsqldatabaseopencvface-detection

解决方案


您可以列出收集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 等之后),解析它们然后插入将解析的项目放入另一个具有默认“未压缩”结构的“最终”表中,同时在需要时添加睡眠等。


推荐阅读