Перейти к содержимому
Compvision.ru

Nuzhny

Пользователи
  • Количество публикаций

    1 427
  • Зарегистрирован

  • Посещение

  • Days Won

    176

Все публикации пользователя Nuzhny

  1. Intel OpenVINO

    GPU у меня Intel UHD 630, что в составе i7-8750H, Windows 10. Есть ноут с Убунтой, но там Sandy Bridge, даже нет смысла проверять. Всё равно здорово работает.
  2. Intel OpenVINO

    Пусть сам разработчик (привет, BeS) не пишет новость, но нельзя не сказать. OpenVINO - очень быстрый inference нейросетей на CPU. Его же недавно выложили на Гитхаб (это я не проверял, качал установщик с сайта). Можно легко подключить в качестве backend к OpenCV и нейросети, запускаемые через opencv_dnn ускорятся в разы. Это очень здорово. Мне понадобилось это буквально вчера и оказалось, что через OpenVINO resnet для лиц из OpenCV работает в 3-4 раза быстрее, чем дефолный opencv_dnn! Потом я взял их собственную сеть для детекции лиц и оказалось, что она работает в 10 раз быстрее при той же визуальной точности. Порядок цифр на моём ноуте был примерно такой: - один кадр на CPU c default opencv_dnn, resnet: >50 млсек; - один кадр на iGPU c default opencv_dnn, resnet: ~25 млсек; - один кадр на CPU c OpenVINO backend, resnet: ~15 млсек; - один кадр на CPU c OpenVINO backend, их сеть: ~5 млсек. Круто! Ещё в пакете есть openvx, который тоже можно подключить в CMake к OpenCV, но с 4-м OpenCV у меня не завёлся, а на 3-м проверять было лень. Очень радуют новости, когда производители железа раздают оптимизированные библиотеки разработчикам.
  3. У них алгоритм будет реагировать неправильно. Чтобы было правильно, надо делать сегментацию и искать движение не в целом по кадру, а по сегментам. В твоём случае всё намного проще: детектор лица, можно добавить детекцию кожи внутри найденного лица и всё. В OpenCV он всё больше не для реального времени, а для постобработки.
  4. Intel OpenVINO

    Приветствую, Дмитрий! Я вас заочно знаю: моя коллега создавала issue по поводу ltsm сети и вы ей в итоге помогли с этим. Хорошее дело делаете! OpenVINO для меня случился очень кстати, потому что есть необходимость быстрого поиска на компьютерах без GPU. Рано или поздно Haar должен был уйти. Да, я использовал res10_300x300_ssd_iter_140000.caffemodel (кажется, она раньше была в составе OpenCV, но сейчас найти не могу источник). На iGPU с OpenVINO у меня не запустилось с ошибкой: "[WARNING]. Device ID (0x3E9B) not supported. Pretending to behave like SKL GT2." Родная модель face-detection-retail-0004 также не запустилась с формулировкой: "OpenCV(4.0.0-pre) Error: Assertion failed (Failed to initialize Inference Engine backend: Unknown Layer Type: PriorBoxClustered) in cv::dnn::InfEngineBackendNet::initPlugin, file c:\work\libraries\opencv\opencv\modules\dnn\src\op_inf_engine.cpp, line 502" Я пока это списываю на тот факт, что использую не релиз, а из мастера исходники беру.
  5. Использует ГАИ, но продают им местные конторы, как правило. OpenAlpr ориентирована больше на учёт занятых мест на стоянке, кажется, в этой обласи распознавание актуально даже больше, чем у ГАИ. Постобработка траекторий - это да, начинал заниматься этим ещё 10 лет назад, даже публикация была. Очень полезная и актуальная тема.
  6. Ммм, кажется, что на этом рынке будет сложно конкурировать. У Интела есть очень быстрое бесплатное решение для китайских номеров, которое работает на CPU и будет превосходить те же каскады Хаара практически во всём. И распознавать номер. Плюс к этому они умеют находить не только номер, но и автомобиль, а также его характеристики типа цвета. Дообучить сеть для русских номеров будет плёвым делом, благо номеров в сети достаточно. Надо сделать прямо таки что-то очень сильное. Ну и уже лет 10 назад на рынке массово появились библиотеки по распознаванию номеров почти у каждого поставщика систем безопасности. Я работал в Ставрополе, поначалу покупали украинское решение по распознаванию (набор dll + аппаратный ключ защиты для каждой копии). Они доучивали свою систему для российских, для казахстанских номеров. Потом сделали свою и отказались от сторонней. То есть рынок, кажется, занят и выйти на него с современными нейросетями довольно просто.
  7. GOTURN

    Я пробовал на CPU через opencv_dnn, было медленно. А у тебя какой опыт?
  8. BRISK - это же лишь один из видов дескрипторов. Просто отслеживать их вряд ли получится, потому что надо следить за общей формой лица. То есть надо и правильное положение точек найти, и следить за лицом. Например, как это делается. Но для видео, возможно, это делается проще и быстрее.
  9. Если перейти на OpenCV поновее и использовать C++ API, то будет ещё быстрее. Особенно на современном железе. Ещё можно и transparent API использовать, чтобы на видеокарту часть вычислений перенести.
  10. О, я сам тоже подхожу к этой проблеме. Как решать ещё не смотрел, но где есть решение, чтобы посмотреть, знаю - OpenFace. Демка двухгодичной давности выглядит отлично:
  11. Ну как, получилось? У меня появилось немного времени, я реализовал свою идею (код корявый, конечно - сорри): /// /// \brief PosRefinement /// \param bin /// \param cutRect /// \param kThreshold /// \param minSize /// \param vw /// \return /// bool PosRefinement( cv::Mat bin, cv::Rect& cutRect, double kThreshold, cv::Size minSize, cv::VideoWriter& vw ) { cv::namedWindow("mc", cv::WINDOW_NORMAL | cv::WINDOW_KEEPRATIO); cv::Mat clImg; cv::cvtColor(bin, clImg, cv::COLOR_GRAY2BGR); cv::waitKey(1); const double areaThreshold = 100; const int radius = 5; const int maxIters = 100; std::vector<std::vector<cv::Point>> contours; std::vector<cv::Vec4i> hierarchy; cv::findContours(bin, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE, cv::Point()); size_t bestCont = contours.size(); double maxArea = 0; for (size_t i = 0; i < contours.size(); i++) { double area = cv::contourArea(contours[i]); if (area > maxArea) { maxArea = area; bestCont = i; } } std::cout << "maxArea = " << maxArea << std::endl; if (maxArea < areaThreshold) { return false; } cv::Moments m = cv::moments(contours[bestCont]); cv::Point mc(cvRound(m.m10 / m.m00), cvRound(m.m01 / m.m00)); cv::Rect currRect(mc.x - radius / 2, mc.y - radius / 2, radius, radius); cv::rectangle(clImg, currRect, cv::Scalar(0, 0, 255), 1); cv::imshow("mc", clImg); cv::waitKey(1); auto Clamp = [](int v, int hi) -> bool { if (v < 0) { v = 0; return true; } else if (hi && v > hi - 1) { v = hi - 1; return true; } return false; }; auto RectClamp = [&](cv::Rect& r, int w, int h) -> bool { return Clamp(r.x, w) || Clamp(r.x + r.width, w) || Clamp(r.y, h) || Clamp(r.y + r.height, h); }; int stepL = radius / 2; int stepR = radius / 2; int stepT = radius / 2; int stepB = radius / 2; double k = 0; struct State { double k = 0; int stepL = 0; int stepR = 0; int stepT = 0; int stepB = 0; cv::Rect currRect; State() = default; State(double k_, int stepL_, int stepR_, int stepT_, int stepB_, cv::Rect currRect_) : k(k_), stepL(stepL_), stepR(stepR_), stepT(stepT_), stepB(stepB_), currRect(currRect_) { } bool operator==(const State& st) const { return (st.k == k) && (st.stepL == stepL) && (st.stepR == stepR) && (st.stepT == stepT) && (st.stepB == stepB) && (st.currRect == currRect); } }; const size_t statesCount = 2; State prevStates[statesCount]; size_t stateInd = 0; for (int it = 0; it < maxIters; ++it) { std::cout << "it " << it << ": " << currRect << std::endl; cv::Rect rleft(currRect.x - stepL, currRect.y, currRect.width + stepL, currRect.height); cv::Rect rright(currRect.x, currRect.y, currRect.width + stepR, currRect.height); cv::Rect rtop(currRect.x, currRect.y - stepT, currRect.width, currRect.height + stepT); cv::Rect rbottom(currRect.x, currRect.y, currRect.width, currRect.height + stepB); double kleft = 0; double kright = 0; double ktop = 0; double kbottom = 0; if (!RectClamp(rleft, bin.cols, bin.rows)) { cv::Rect rstep(currRect.x - stepL, currRect.y, stepL, currRect.height); if (cv::sum(bin(rstep))[0] / (255.0 * rstep.area()) > kThreshold / 2) { kleft = cv::sum(bin(rleft))[0] / (255.0 * rleft.area()); } } if (!RectClamp(rright, bin.cols, bin.rows)) { cv::Rect rstep(currRect.x + currRect.width, currRect.y, stepR, currRect.height); if (cv::sum(bin(rstep))[0] / (255.0 * rstep.area()) > kThreshold / 2) { kright = cv::sum(bin(rright))[0] / (255.0 * rright.area()); } } if (!RectClamp(rtop, bin.cols, bin.rows)) { cv::Rect rstep(currRect.x, currRect.y - stepT, currRect.width, stepT); if (cv::sum(bin(rstep))[0] / (255.0 * rstep.area()) > kThreshold / 2) { ktop = cv::sum(bin(rtop))[0] / (255.0 * rtop.area()); } } if (!RectClamp(rbottom, bin.cols, bin.rows)) { cv::Rect rstep(currRect.x, currRect.y + currRect.height, currRect.width, stepB); if (cv::sum(bin(rstep))[0] / (255.0 * rstep.area()) > kThreshold / 2) { kbottom = cv::sum(bin(rbottom))[0] / (255.0 * rbottom.area()); } } std::cout << "kleft = " << kleft << ", kright = " << kright << ", ktop = " << ktop << ", kbottom = " << kbottom << std::endl; bool wasEnlargeX = false; if (kleft > kThreshold) { currRect.x -= stepL; currRect.width += stepL; wasEnlargeX = true; if (kleft > k) { ++stepL; } } else { if (stepL > 1) { --stepL; } currRect.x += 1; currRect.width -= 1; } if (kright > kThreshold) { currRect.width += stepR; wasEnlargeX = true; if (kright > k) { ++stepR; } } else { if (stepR > 1) { --stepR; } currRect.width -= 1; } bool wasEnlargeY = false; if (ktop > kThreshold) { currRect.y -= stepT; currRect.height += stepT; wasEnlargeY = true; if (ktop > k) { ++stepT; } } else { if (stepT > 1) { --stepT; } currRect.y += 1; currRect.height -= 1; } if (kbottom > kThreshold) { currRect.height += stepB; wasEnlargeY = true; if (kbottom > k) { ++stepB; } } else { if (stepB > 1) { --stepB; } currRect.height -= 1; } k = cv::sum(bin(currRect))[0] / (255.0 * currRect.area()); std::cout << "k = " << k << ", stepL = " << stepL << ", stepR = " << stepR << ", stepT = " << stepT << ", stepB = " << stepB << std::endl; State currState(k, stepL, stepR, stepT, stepB, currRect); bool repState = false; for (size_t i = 0; i < statesCount; ++i) { if (prevStates[i] == currState) { repState = true; break; } } if (repState) { break; } else { prevStates[stateInd] = currState; stateInd = (stateInd + 1 < statesCount) ? (stateInd + 1) : 0; } if (k < kThreshold && (stepL + stepR + stepT + stepB == 4) && !wasEnlargeX && !wasEnlargeY) { break; } cv::cvtColor(bin, clImg, cv::COLOR_GRAY2BGR); cv::rectangle(clImg, currRect, cv::Scalar(0, 0, 255), 1); cv::imshow("mc", clImg); cv::waitKey(40); if (vw.isOpened()) { vw << clImg; } } cutRect = currRect; std::cout << cutRect << ", " << minSize << std::endl; return (cutRect.width >= minSize.width) && (cutRect.height >= minSize.height); } /// /// \brief main /// \param argc /// \param argv /// \return /// int main(int argc, char** argv) { cv::Mat img = cv::imread("/home/snuzhny/Downloads/ce4119cc6e4b396f4a4a379e18fbc268-full.png", cv::IMREAD_GRAYSCALE); cv::Rect objRect(0, 0, 630, 300); cv::Size minSize(objRect.width / 10, objRect.height / 10); cv::Mat clImg; cv::cvtColor(cv::Mat(img, objRect), clImg, cv::COLOR_GRAY2BGR); cv::Mat bin; cv::threshold(cv::Mat(img, objRect), bin, 128, 255, cv::THRESH_BINARY); cv::namedWindow("clImg", cv::WINDOW_NORMAL | cv::WINDOW_KEEPRATIO); cv::imshow("clImg", clImg); cv::VideoWriter vw("res_rects.avi", cv::VideoWriter::fourcc('P', 'I', 'M', '1'), 25, clImg.size(), true); for (;;) { cv::Rect cutRect; if (!PosRefinement(bin, cutRect, 0.9f, minSize, vw)) { break; } cv::rectangle(bin, cutRect, cv::Scalar(0, 0, 0), cv::FILLED); objRect.x += cutRect.x; objRect.y += cutRect.y; objRect.width = cutRect.width; objRect.height = cutRect.height; cv::rectangle(clImg, cutRect, cv::Scalar(0, 0, 255), 1); cv::imshow("clImg", clImg); cv::waitKey(1); } cv::imshow("clImg", clImg); cv::waitKey(0); cv::imwrite("res_rects.png", clImg); return 0; } res_rects.avi
  12. Поиск мостов

    Да, именно так. h5 у нас не водится, tensorflow::SavedModel сохраняет в *.pb, то есть бинарный protobuf. Модели в Керасе тоже обучали и использовали, но уже не вспомню как. Сейчас же Керас уже стал часть TensorFlow (его исходники в репозитории TensorFlow), возможен взаимный вызов из одного методов другого. Точно помню, что проблем с этим не было.
  13. Поиск мостов

    Тут правильно было бы спросить: использовал ли я их в проекте, написанном на С++? Ответ: да. Но тут 2 этапа: Выбор архитектуры, обучение, всякие графики - это всё делалось на Питоне. Сама работа обученной сети или inference. Да, это делал на С++ и сейчас делаю. 2.1. В первом приближении практически всё можно запускать с помощью средств OpenCV - opencv_dnn. В нашем совместном проекте Multitarget-tracker детекторы так и работают: и resnet, и MobileNet, и YOLO... Не всё идеально, но запускается и результат выдаёт, но только на CPU или Intel GPU. 2.2. TensorFlow имеет хорошее C++ API, в проектах мы его используем вместе с OpenCV, которым предварительно обрабатываем видео. Обучение и тренировка сети на Питоне. 2.3. Ранее использовал Caffe, там тоже всё отлично. 2.4. Использовал и видел многие проекты, которые включают в себя код darknet и обученный под себя YOLO. Там всё достаточно просто, примеры в нём самом есть. 2.5. Ещё знаю, что нормальный С++ API есть у Caffe2 и MxNet, но с ними опыта не было.
  14. Поиск мостов

    Ну, пробовать можно. В любом случае этот процесс должен начаться со сбора датасета, разметки и т.д. Раньше работать над алгоритмом смысла нет.
  15. Поиск мостов

    Все современные подходы к таким вещам сводятся к использованию нейросетей. Если интересует сегментация мостов, то архитектура типа Unet. Если детектирование прямоугольника, то SSD, RetinaNet, Faster RCNN, YOLO.
  16. Этот пункт можно выполнять по-разному. Первый вариант, который я описывал: Например, у нас есть первоначальный прямоугольник Rect(15, 20, 3, 3). Делаем из него 4: граница влево Rect(14, 20, 4, 3), граница вправо Rect(15, 20, 4, 3), граница вверх Rect(15, 19, 3, 4), граница вниз Rect(15, 20, 3, 4). Если у одного из них соотношение выше всех, то его и оставляем. Если одинаковое у всех, то можно оставить и все расширения: Rect(14, 19, 5, 5). Или оставлять только самые лучшие расширения.
  17. На правах идеи, подглядел где-то у Смородова: 1. Находим точку С0 внутри самого большого скопления белых объектов (контурами или вертикальными и горизонтальными гистограммами). Это будет где-то внутри белого большого прямоугольника. 2. Считаем, что в точке С0 у нас расположен центр прямоугольника с размерами 3х3 пикселя. 3. В цикле увеличиваем размер прямоугольника следующими 4-мя преобразованиями: сдвиг границы прямоугольника влево, вправо, вверх и вниз на 1 пиксель (можно и больше сначала). Получаем 4 новых прямоугольника, у каждого из которых смотрим отношение белых пикселей к общей площади k1. Если отношение больше, скажем, k = 0.95, то получаем новый прямоугольник с центром C1 и размерами на 1 пиксель больше. Из 4-х прямоугольников выбираем тот, у которого отношение наибольшее. 4. Повторяем пункт 3 пока не достигнем границ картинки, либо пока отношение белой площади к обще не станет ниже, чем k. 5. Если исходный прямоугольник удовлетворяет нашим условиям (размер его больше и меньше неким эвристикам), то он вычислен верно. Закрашиваем его полностью чёрным цветом. 6. Повторяем пункты 1-5, пока у нас всё изображение не станет чёрным, либо пока не закончатся прямоугольники достаточно большого размера. Эта процедура может быть достаточно медленной, но она поддаётся ускорению: - в пункте 3 можно брать шаг побольше, чем 1 и снижать его, если значение ki будет меньше k; - для посчёта сумм внутри прямоугольников можно предварительно вычислить интегральное изображение и брать суммы по нему.
  18. Дешёвые типа Hik'а - да. Но есть и те, которые совсем не сжимают, но дорогие они.
  19. Если недорогие, то можно у Hikvision найти ip-камеры, которые умеют смотреть на 90 градусов. Только я слабо представляю полезность такого подхода.
  20. Датасет тут не поможет. Из-за Jpeg артефактов определить наличие пассажира как-то автоматичеки или доулучшать контраст не получится. Всё, что может тут машина сделать сама, сможет сделать и человек с Фотошопом. Чудо могло бы быть, если бы был не кадр а видео. Тогда с superresolution или аналогом можно было бы попробовать что-то вытащить.
  21. Думаю, что для произвольного изображения определить размытость невозможно. А как же Боке? Или сцена без деталей? Ещё как-то вопрос можно решить для видео. Поэтому поддерживаю предыдущий вариант - натренировать классификатор.
  22. Да, можно. Это же называется SFM.
  23. Пусть программа и исполтзует OpenCV, не вижу никаких проблем. Ты же не будешь использовать OpenCV для работы с базами данных.
  24. Я бы не заморачивался и сделал всё на ffmpeg (на С разумеется). OpenCV вообще никак не приспособлен для этого.
×