如何使用 OpenCv 更有效地使用 SQL 查询,而不必在每一帧中插入数据

How to use an SQL query more efficiently with OpenCv and not having to insert data every frame

我把研究面部识别软件作为一种爱好,以了解更多关于如何使用 OpenCV 的信息。

我的软件包含检测和生成 20 个数据集图像的功能,并将其与我在数据库中分配的用户 ID 一起保存,然后对其进行训练并将其添加到我的“.trainedData.yml”我用来识别人脸的文件。

我有一个 SQL 数据库,我在其中存储人员的个人资料并使用我的网络摄像头捕捉视频。

当我 运行 当前软件时,在每一帧中,我都会检查置信度,如果足够好,我会在数据库中查找 return 找到的配置文件信息。之后,我调用了一个函数来将数据插入到数据库中,这样我就可以跟踪检测到面部的时间和位置。

问题: 每帧都在处理此插入查询,创建大量插入,有时甚至在同时执行多个插入时失败。

有没有一种方法可以提高效率,而不必每帧插入数据,也许每 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 和 return 配置文件而不需要 SQL 事务。

如果需要速度,还请尝试删除过多的打印调用(或使用尽可能短的输出和较少的新 lines/terminal 滚动),记录可能会更快。

更复杂的解决方案可能使用线程或多处理。防止插入竞争条件的一种方法可能是在每次调用数据库之间添加 short cv.waitKey 或休眠 1 毫秒或其他任何时间;不过在线程中更好。

请注意,如果你延迟 SQL 访问太多并收集太多候选插入(如果说使用最大 30 fps,延迟 33 和 10 秒,如果每个帧都有一个识别:300 个项目) ,并且如果您立即将它们推送到主线程中,这可能会阻塞并且可能会在这些传输过程中“限制”视频帧速率。

...

另一个一般的想法可能是插入到数据库中的函数不仅接收一个,而且接收许多收集的数据项,然后调用该函数一次插入许多项:可能是 SQL 命令本身和数据库方案可以是一个 INSERT 来添加多行,如果这可以解决太多简单单个事务的瓶颈。

例如,可以在中间 table 中一次添加许多数据项作为 varchar 行,其中项目以 CSV 格式等编码为字符串。然后一些更复杂的 SQL 命令或另一个进程或线程中的另一个脚本可以观察 table,对新行进行采样(SELECT * where time ... 在一些最后采样的当前时间之后或一些 id 等),解析它们,然后将解析后的项目插入另一个具有默认“未压缩”结构的“最终”table,同时根据需要添加睡眠等。