【问题标题】:Why is cv2.dnn work faster when I use CPU rather than GPU?为什么当我使用 CPU 而不是 GPU 时 cv2.dnn 工作得更快?
【发布时间】:2021-07-20 09:08:45
【问题描述】:

我是 openCV - CUDA 的新手,所以我一直在测试最简单的一种,它在 GPU 而不是 CPU 上加载模型,以查看 GPU 的速度有多快,我对得到的结果感到震惊。

----------------------------------------------------------------
---         GPU                vs             CPU            ---
---                                                          ---
--- 21.913758993148804 seconds ---3.0586464405059814 seconds ---
--- 22.379303455352783 seconds ---3.1384341716766357 seconds ---
--- 21.500431060791016 seconds ---2.9400241374969482 seconds ---
--- 21.292986392974854 seconds ---3.3738017082214355 seconds ---
--- 20.88358211517334 seconds  ---3.388749599456787 seconds  ---

我会给出我的代码 sn-p 以防我可能做错了什么导致 GPU 时间飙升。

def loadYolo():
    net = cv2.dnn.readNet("yolov4.weights", "yolov4.cfg")
    
    net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)
    net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA_FP16)

    classes = []
    with open("coco.names", "r") as f:
        classes = [line.strip() for line in f.readlines()]

    layer_names = net.getLayerNames()
    output_layers = [layer_names[i[0] - 1] for i in net.getUnconnectedOutLayers()]
    return net,classes,layer_names,output_layers


@socketio.on('image')
def image(data_image):

    sbuf = StringIO()
    sbuf.write(data_image)
    
    b = io.BytesIO(base64.b64decode(data_image))
    if(str(data_image) == 'data:,'):
        pass
    else:
        pimg = Image.open(b)
    
        frame = cv2.cvtColor(np.array(pimg), cv2.COLOR_RGB2BGR)
        frame = resize(frame, width=700)
        frame = cv2.flip(frame, 1)
    
        net,classes,layer_names,output_layers=loadYolo()
        height, width, channels = frame.shape

        
        blob = cv2.dnn.blobFromImage(frame, 1 / 255.0, (416, 416),
        swapRB=True, crop=False)

       
        net.setInput(blob)
        outs = net.forward(output_layers)
        print("--- %s seconds ---" % (time.time() - start_time))
        
        
        class_ids = []
        confidences = []
        boxes = []
        for out in outs:
            for detection in out:
                scores = detection[5:]
                class_id = np.argmax(scores)
                confidence = scores[class_id]
                if confidence > 0.5:
                    # Object detected
                    center_x = int(detection[0] * width)
                    center_y = int(detection[1] * height)
                    w = int(detection[2] * width)
                    h = int(detection[3] * height)

                    # Rectangle coordinates
                    x = int(center_x - w / 2)
                    y = int(center_y - h / 2)

                    boxes.append([x, y, w, h])
                    confidences.append(float(confidence))
                    class_ids.append(class_id)

        indexes = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4)
        font = cv2.FONT_HERSHEY_PLAIN
        colors = np.random.uniform(0, 255, size=(len(classes), 3))
        for i in range(len(boxes)):
            if i in indexes:
                x, y, w, h = boxes[i]
                label = str(classes[class_ids[i]])
                color = colors[class_ids[i]]
                cv2.rectangle(frame, (x, y), (x + w, y + h), color, 2)
                cv2.putText(frame, label, (x, y + 30), font, 1, color, 2)
    
        imgencode = cv2.imencode('.jpg', frame)[1]

        stringData = base64.b64encode(imgencode).decode('utf-8')
        b64_src = 'data:image/jpg;base64,'
        stringData = b64_src + stringData
        emit('response_back', stringData)

我的 Gpu 是 Nvidia 1050 Ti,我的 CPU 是 i5 gen 9,以防有人需要规格。有人可以启发我,因为我现在非常困惑吗?非常感谢

编辑 1: 我尝试使用 cv2.dnn.DNN_TARGET_CUDA 而不是 cv2.dnn.DNN_TARGET_CUDA_FP16,但与 CPU 相比,时间仍然很糟糕。下面是 GPU 结果:

--- 10.91195559501648 seconds ---
--- 11.344025135040283 seconds ---
--- 11.754926204681396 seconds ---
--- 12.779674530029297 seconds ---

以下是CPU结果:

--- 4.780993223190308 seconds ---
--- 4.910650253295898 seconds ---
--- 4.990436553955078 seconds ---
--- 5.246175050735474 seconds ---

它仍然比 CPU 慢

编辑 2: OpenCv 是 4.5.0,CUDA 11.1 和 CUDNN 8.0.1

【问题讨论】:

  • 你的opencv版本是多少?
  • OpenCV 4.5.0 @Amir Karami
  • 你找到问题了吗?
  • 你在执行GPU版本的过程中检查了GPU资源使用情况吗?如果是,负载是多少?

标签: opencv yolo opencv-python yolov4


【解决方案1】:

从前两个答案中,我设法改变了解决方案:

net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA_FP16)

进入:

net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA)

由于我的 GPU 类型与 FP16 不兼容,有助于将 GPU 速度提高一倍,这要归功于 Amir Karami,尽管 Ian Chu 的回答并没有解决我的问题,但它为我提供了强制使所有图像仅使用的基础一个网络实例实际上将处理时间从每个需要 10 秒显着降低到 0.03-0.04 秒,从而超过 CPU 速度很多倍。我不接受这两个答案的原因是因为两者都没有真正解决我的问题,但两者都成为我解决方案的坚实基础,所以我仍然支持它们。我只是在这里留下我的答案,以防有人遇到像我这样的问题。

【讨论】:

    【解决方案2】:

    您绝对应该只加载一次 YOLO。为通过套接字的每个图像重新创建它对于 CPU 和 GPU 来说都很慢,但 GPU 需要更长的时间来初始加载,这就是为什么您会看到它的运行速度比 CPU 慢。

    我不明白您为 YOLO 模型使用 LRU 缓存是什么意思。没有看到你的代码结构的其余部分,我无法提出任何真正的建议,但你可以尝试至少暂时将网络放入全局空间,看看它是否运行得更快? (完全删除函数并将其主体放在全局空间中)

    类似的东西

    net = cv2.dnn.readNet("yolov4.weights", "yolov4.cfg")
    
    net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)
    net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA_FP16)
    
    classes = []
    with open("coco.names", "r") as f:
        classes = [line.strip() for line in f.readlines()]
    
    layer_names = net.getLayerNames()
    output_layers = [layer_names[i[0] - 1] for i in net.getUnconnectedOutLayers()]
    
    
    @socketio.on('image')
    def image(data_image):
    
        sbuf = StringIO()
        sbuf.write(data_image)
        
        b = io.BytesIO(base64.b64decode(data_image))
        if(str(data_image) == 'data:,'):
            pass
        else:
            pimg = Image.open(b)
        
            frame = cv2.cvtColor(np.array(pimg), cv2.COLOR_RGB2BGR)
            frame = resize(frame, width=700)
            frame = cv2.flip(frame, 1)
        
            height, width, channels = frame.shape
    
            
            blob = cv2.dnn.blobFromImage(frame, 1 / 255.0, (416, 416),
            swapRB=True, crop=False)
    
           
            net.setInput(blob)
            outs = net.forward(output_layers)
            print("--- %s seconds ---" % (time.time() - start_time))
            
            
            class_ids = []
            confidences = []
            boxes = []
            for out in outs:
                for detection in out:
                    scores = detection[5:]
                    class_id = np.argmax(scores)
                    confidence = scores[class_id]
                    if confidence > 0.5:
                        # Object detected
                        center_x = int(detection[0] * width)
                        center_y = int(detection[1] * height)
                        w = int(detection[2] * width)
                        h = int(detection[3] * height)
    
                        # Rectangle coordinates
                        x = int(center_x - w / 2)
                        y = int(center_y - h / 2)
    
                        boxes.append([x, y, w, h])
                        confidences.append(float(confidence))
                        class_ids.append(class_id)
    
            indexes = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.4)
            font = cv2.FONT_HERSHEY_PLAIN
            colors = np.random.uniform(0, 255, size=(len(classes), 3))
            for i in range(len(boxes)):
                if i in indexes:
                    x, y, w, h = boxes[i]
                    label = str(classes[class_ids[i]])
                    color = colors[class_ids[i]]
                    cv2.rectangle(frame, (x, y), (x + w, y + h), color, 2)
                    cv2.putText(frame, label, (x, y + 30), font, 1, color, 2)
        
            imgencode = cv2.imencode('.jpg', frame)[1]
    
            stringData = base64.b64encode(imgencode).decode('utf-8')
            b64_src = 'data:image/jpg;base64,'
            stringData = b64_src + stringData
            emit('response_back', stringData)
    

    【讨论】:

    • 做得好我得到cv2.error: OpenCV(4.5.1) D:\OpencvBuild\opencv-4.5.1\modules\dnn\src\dnn.cpp:1070: error: (-215:Assertion failed) memHosts.find(lp) == memHosts.end() in function 'cv::dnn::dnn4_v20201117::BlobManager::addHost'哈哈。我怀疑是因为我正在按照您告诉我的操作流式传输大量图像,这意味着只有一个网络实例,并且需要处理大量图像,这使得它们请求相同的实例,从而导致该错误
    • 我分享的代码几乎都是我的flask代码XD。你也需要我分享我的html代码吗?如果您需要,我愿意分享。
    【解决方案3】:

    DNN_TARGET_CUDA_FP16 指的是 16 位浮点数。由于您的 gpu 是 1050 Ti,因此您的 gpu 似乎不适用于 FP16。您可以从 here 检查它,从 here 检查计算能力。 我认为你应该改变这一行:

    net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA_FP16)
    

    进入:

    net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA)
    

    【讨论】:

    • @Albert 有用吗?因为我只是写了我的猜测,因为我没有 6.1 计算能力 gpu。让我知道它是否运行良好..谢谢
    • @Amir Karami 感谢您的回答。与以前相比,它确实做得相当好,实际上快了 2 倍,但仍然比 CPU 慢得多,这是不合逻辑的,因为 GPU 应该更快哈哈
    • @Albert 很奇怪...抱歉我不知道了...
    • 通过cuda内存加载图像需要相当长的时间,因此它可能对单个图像无效,尝试对一批图像运行比较并检查是否有任何性能提升。
    • @flamelite 实际上它是一些图像,您可以看到我正在使用套接字 io,实际上我正在将大量图像从客户端流式传输到服务器以进行处理。由于某种原因,CPU肯定更快哈哈。唯一的副作用是它吃掉了大量的 CPU,这就是我将它迁移到 GPU 的原因,哈哈,但在目前的情况下,时间太长了,这令人失望,因为据我所知,它应该更快
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-06
    • 2016-10-28
    • 2021-03-23
    • 2022-06-29
    • 2012-06-24
    • 1970-01-01
    • 2018-10-22
    相关资源
    最近更新 更多