Чи відрізняється продуктивність між Python або C ++ кодуванням OpenCV?


77

Я прагну запустити opencv потроху, але спочатку мені потрібно вирішити, який API OpenCV є більш корисним. Я передбачаю, що реалізація Python коротша, але час роботи буде більш щільним і повільним порівняно з власними реалізаціями C ++. Чи є хтось, хто може прокоментувати різницю між продуктивністю та кодуванням між цими двома перспективами?


4
Більшість справжньої роботи в Cбудь-якому випадку виконується за кулісами кодом OpenCV , тому за умови, що ваш власний код не надто продуманий, різниця не повинна бути такою великою, як ви наївно очікуєте.
juanchopanza

Відповіді:


161

Як вже згадувалося в попередніх відповідях, Python працює повільніше порівняно з C ++ або C. Python створений завдяки своїй простоті, переносимості та, крім того, творчості, де користувачам потрібно турбуватися лише про свій алгоритм, а не про проблеми програмування.

Але тут, у OpenCV, є щось інше. Python-OpenCV - це просто обгортка навколо оригінального коду C / C ++. Зазвичай він використовується для поєднання найкращих характеристик обох мов, продуктивності C / C ++ та простоти Python .

Отже, коли ви викликаєте функцію в OpenCV з Python, те, що насправді працює, лежить в основі джерела C / C ++. Так що не буде великої різниці в продуктивності. (Я пам'ятаю , що я де - то читав , що продуктивність штраф <1%, не пам'ятаю , де. Груба оцінка з деякими основними функціями в OpenCV показує найгірший штраф в розмірі , <4%т. Еpenalty = [maximum time taken in Python - minimum time taken in C++]/minimum time taken in C++ ) .

Проблема виникає, коли у вашому коді багато власних кодів python. Наприклад, якщо ви створюєте власні функції, недоступні в OpenCV, ситуація погіршується. Такі коди запускаються в Python, що значно знижує продуктивність.

Але новий інтерфейс OpenCV-Python має повну підтримку Numpy. Numpy - це пакет для наукових обчислень на Python. Це також обгортка навколо власного коду C. Це високооптимізована бібліотека, яка підтримує широкий спектр матричних операцій, що дуже підходить для обробки зображень. Отже, якщо ви можете правильно поєднати функції OpenCV і функції Numpy, ви отримаєте дуже високий швидкісний код.

Варто пам’ятати, що завжди намагайтеся уникати циклів та ітерацій у Python. Натомість використовуйте засоби обробки масивів, доступні в Numpy (та OpenCV). Просто додавши два масиви numpy за допомогоюC = A+B допомогою набагато швидше, ніж використання подвійних циклів.

Наприклад, ви можете перевірити ці статті:

  1. Швидка маніпуляція масивом у Python
  2. Порівняння продуктивності інтерфейсів OpenCV-Python, cv та cv2

15

Усі результати Google для openCV стверджують однаково: цей python буде лише трохи повільнішим. Але жодного разу я не бачив жодного профілювання цього. Тому я вирішив зробити кілька і виявив:

Python значно повільніший за C ++ з opencv, навіть для тривіальних програм.

Найпростіший приклад, який я міг подумати, - це відображення результатів роботи веб-камери на екрані та відображення кількості кадрів в секунду. За допомогою python я досяг 50 кадрів в секунду (на атомі Intel). З C ++ я отримав 65 кадрів в секунду, збільшення на 25%. В обох випадках використання центрального процесора використовувало одне ядро, і, наскільки мені відомо, це було пов'язано з продуктивністю центрального процесора. Крім того, цей тестовий приклад про узгодження з тим, що я бачив у проектах, які я переносив з одного на інший у минулому.

Звідки ця різниця? У python всі функції openCV повертають нові копії матриць зображень. Кожного разу, коли ви захоплюєте зображення або змінюєте його розмір - у C ++ ви можете повторно використовувати наявну пам’ять. У python ви не можете. Я підозрюю, що цей час, витрачений на виділення пам’яті, є основною відмінністю, оскільки, як казали інші: основним кодом openCV є C ++.

Перш ніж викидати python у вікно: python набагато швидше розвивається, і якщо ви не стикаєтесь з апаратними обмеженнями або якщо швидкість розробки важливіша за продуктивність, використовуйте python. У багатьох додатках, які я робив з openCV, я починав з python, а пізніше перетворив лише компоненти комп'ютерного зору на C ++ (наприклад, використовуючи модуль ctype python та компілюючи CV-код у спільну бібліотеку).

Код Python:

import cv2
import time

FPS_SMOOTHING = 0.9

cap = cv2.VideoCapture(2)
fps = 0.0
prev = time.time()
while True:
    now = time.time()
    fps = (fps*FPS_SMOOTHING + (1/(now - prev))*(1.0 - FPS_SMOOTHING))
    prev = now

    print("fps: {:.1f}".format(fps))

    got, frame = cap.read()
    if got:
        cv2.imshow("asdf", frame)
    if (cv2.waitKey(2) == 27):
        break

Код С ++:

#include <opencv2/opencv.hpp>
#include <stdint.h>

using namespace std;
using namespace cv;

#define FPS_SMOOTHING 0.9

int main(int argc, char** argv){
    VideoCapture cap(2);
    Mat frame;

    float fps = 0.0;
    double prev = clock(); 
    while (true){
        double now = (clock()/(double)CLOCKS_PER_SEC);
        fps = (fps*FPS_SMOOTHING + (1/(now - prev))*(1.0 - FPS_SMOOTHING));
        prev = now;

        printf("fps: %.1f\n", fps);

        if (cap.isOpened()){
            cap.read(frame);
        }
        imshow("asdf", frame);
        if (waitKey(2) == 27){
            break;
        }
    }
}

Можливі обмеження тесту:

  • Частота кадрів камери
  • Точність вимірювання таймера
  • Час, витрачений на форматування друку

2
Випадково ваш тестовий випадок виявив найбільшу різницю між Python та C ++. Тож це може бути нереально. Кращим тестом було б розглянути відеокадр і, можливо, спробувати обчислити комп’ютер, націлившись на мотоцикл для самокерованого автомобіля. Це був би приблизно такий самий час роботи для C ++ або Python. Випадок без натискання показує, скільки часу потрібно для завантаження буферів кадрів, не відмовляючись від реальної роботи. SO завантаження кадру домінує в часі. Якщо ви виконуєте реальну роботу, то поліпшення кадру складає лише 2% від загальної кількості, а не 100% від загальної кількості.
user3150208,

1
Хоча в даний час я не маю жодних орієнтирів, я підозрюю, що вони є більш важливими, ніж ви передбачаєте. Наприклад, якщо ви працюєте в python, dst = cv2.filter2D(img, -1, kernel)тоді комп'ютер створює копію imgта повертає як dst. Якщо ви не використовуєте, imgтоді приходить GC і очищає старе зображення. Це неможливо обійти цим за допомогою python API openCV. У C / C ++ ви можете легко створити буфер статичного зображення правильного розміру, який не створюється / не знищується кожен кадр. Час виділення та звільнення пам'яті не дорівнює нулю.
sdfgeoff

Чи не буде збільшення частоти кадрів з 50 до 65 покращенням на 30% (а не на 25%)?
AmigoNico

6

У відповіді sdfgeoff відсутній той факт, що ви можете повторно використовувати масиви в Python. Попередньо виділіть їх і передайте, і вони звикнуть. Так:

    image = numpy.zeros(shape=(height, width, 3), dtype=numpy.uint8)
    #....
    retval, _ = cv.VideoCapture.read(image)

Наскільки я можу зрозуміти, багато функцій (наприклад, filter2D) не беруть масиви призначення як параметри. Однак, якщо ви можете вказати мені деякі документи, які говорять про інше, я із задоволенням зміню свою відповідь. Мені також було б дуже цікаво побачити порівняння продуктивності з цією технікою.
sdfgeoff

2
Не знаю, чому ти це кажеш. Ось doc для filter2d: docs.opencv.org/3.4/d4/d86/… Зверніть увагу, що четвертим параметром у Python є "dst", що є цільовим масивом. Я не скрізь перевіряв, але стандартним є те, що якщо в C ++ є аргумент призначення, то він є там у Python
Пол

Га, ти маєш рацію. Я раніше цього не помічав. Думаю, мені доведеться повторити порівняння продуктивності
sdfgeoff

Ви можете детальніше розповісти, як попередній розподіл прискорює процес? Я не зовсім зрозумів цю частину, тому що для мене здається, що я просто розподіляю простір в іншій точці, але розподіляю все одно (?)
fogx

Заощадження для попереднього розподілу отримали б від розподілу масиву один раз, але потім виклику VideoCapture.read () або filter2d () всередині циклу. Типовим використанням може бути ініціалізація, а потім назавжди цикл, зчитування зображення з камери та його обробка. Попередній розподіл дозволить заощадити близько мілісекунди кожної ітерації.
Пол

4

Ви маєте рацію, Python майже завжди значно повільніший за C ++, оскільки вимагає інтерпретатора, а C ++ - ні. Однак для цього потрібно, щоб C ++ був строго набраний, що залишає набагато менший запас помилок. Деякі люди воліють, щоб змушували їх строго кодувати, тоді як інші насолоджуються властивою Python поблажливістю.

Якщо вам потрібен повний дискурс про стилі кодування Python проти стилів кодування C ++, це не найкраще місце, спробуйте знайти статтю.

РЕДАГУВАТИ: Оскільки Python є інтерпретованою мовою, тоді як C ++ компілюється до машинного коду, загалом кажучи , ви можете отримати переваги продуктивності, використовуючи C ++. Однак, що стосується використання OpenCV, основні бібліотеки OpenCV вже скомпільовані до машинного коду, тому обгортка Python навколо бібліотеки OpenCV виконує скомпільований код. Іншими словами, коли йдеться про виконання обчислювально дорогих алгоритмів OpenCV з Python, ви не побачите великого хіту продуктивності, оскільки вони вже скомпільовані для конкретної архітектури, з якою ви працюєте.


1
Так, інтерпретується python. Але майже вся робота виконується всередині openCV. Скажімо, існує поділ 20/80. 80% роботи, виконаної всередині openCV, OpenCV написано в компільованому C. Ми говоримо про те, наскільки швидко виконуються решта 20% коду. Навіть якщо Python працює в 4 рази повільніше, це лише додає 30% часу виконання. Додатки ManyopenCV розділені на 5/95, тому Python майже не робить різниці
user3150208
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.