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

Nuzhny

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

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

  • Посещение

  • Days Won

    176

Сообщения, опубликованные пользователем Nuzhny


  1. Приветствую, Дмитрий!

    Я вас заочно знаю: моя коллега создавала issue по поводу ltsm сети и вы ей в итоге помогли с этим. Хорошее дело делаете!

    OpenVINO для меня случился очень кстати, потому что есть необходимость быстрого поиска на компьютерах без GPU. Рано или поздно Haar должен был уйти.

    16 hours ago, dkurt said:

    Я правильно понимаю, что Вы попробовали использовать dnn с DNN_BACKEND_INFERENCE_ENGINE, используя модель в родном формате (не IR из Model Optimizer)? Интересно посмотреть также на числа с iGPU и OpenVINO backend (как для FP32, так и FP16 точности).

    Да, я использовал 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"

    Я пока это списываю на тот факт,  что использую не релиз, а из  мастера исходники беру.


  2. 42 minutes ago, iskees said:

    Но использует такие системы в основном ГАИ. И кроме самого распознавания номера им нужно еще и выявлять нарушения (замер скорости по кадру, проезд на красный и т.д.) которые еще и постоянно добавляются. На мой взгляд более остро сейчас стоит вопрос именно  с последующей обработкой уже распознанных номеров, построение траекторий, привязка к местности и т.д.

    Использует ГАИ, но продают им местные конторы, как правило. OpenAlpr ориентирована больше на учёт занятых мест на стоянке, кажется, в этой обласи распознавание актуально даже больше, чем у ГАИ.

    Постобработка траекторий - это да, начинал заниматься этим ещё 10 лет назад, даже публикация была. Очень полезная и актуальная тема.


  3. Ммм, кажется, что на этом рынке будет сложно конкурировать. У Интела есть очень быстрое бесплатное решение для китайских номеров, которое работает на CPU и будет превосходить те же каскады Хаара практически во всём. И распознавать номер. Плюс к этому они умеют находить не только номер, но и автомобиль, а также его характеристики типа цвета. Дообучить сеть для русских номеров будет плёвым делом, благо номеров в сети достаточно. Надо сделать прямо таки что-то очень сильное.

    Ну и уже лет 10 назад на рынке массово появились библиотеки по распознаванию номеров почти у каждого поставщика систем безопасности. Я работал в Ставрополе, поначалу покупали украинское решение по распознаванию (набор dll + аппаратный ключ защиты для каждой копии). Они доучивали свою систему для российских, для казахстанских номеров. Потом сделали свою и отказались от сторонней. То есть рынок, кажется, занят и выйти на него с современными нейросетями довольно просто.


  4. Пусть сам разработчик (привет, 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-м проверять было лень. Очень радуют новости, когда производители железа раздают оптимизированные библиотеки разработчикам.

    • Like 1

  5. BRISK - это же лишь один из видов дескрипторов. Просто отслеживать их вряд ли получится, потому что надо следить за общей формой лица. То есть надо и правильное положение точек найти, и следить за лицом. Например, как это делается. Но для видео, возможно, это делается проще и быстрее.


  6. Ну как, получилось? У меня появилось немного времени, я реализовал свою идею (код корявый, конечно - сорри):

    ///
    /// \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.png

    res_rects.avi

    • Like 1

  7. 10 minutes ago, idrua said:

    Т.е. обучаешь ты на Python, а inference  на плюсах. Я правильно понял?

    Да, именно так.

    10 minutes ago, idrua said:

    Ок. Даже если так, то как добыть из файла h5 весовые коэффициенты. Где-то описан формат файла?

    А были модели на Keras в обучении? Выходной файл обученной модели отличается чем-то для TF и Keras?

    h5 у нас не водится, tensorflow::SavedModel сохраняет в *.pb, то есть бинарный protobuf.

    Модели в Керасе тоже обучали и использовали, но уже не вспомню как. Сейчас же Керас уже стал часть TensorFlow (его исходники в репозитории TensorFlow), возможен взаимный вызов из одного методов другого. Точно помню, что проблем с этим не было.


  8. 22 minutes ago, idrua said:

    Серега , все хочу спросить, а ты не переписывал на плюсы подобные архитектуры? Насколько этот процесс трудоемок?

    Тут правильно было бы спросить: использовал ли я их в проекте, написанном на С++? Ответ: да.

    Но тут 2 этапа:

    1. Выбор архитектуры, обучение, всякие графики - это всё делалось на Питоне.
    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, но с ними опыта не было.

     


  9. Все современные подходы к таким вещам сводятся к использованию нейросетей. Если интересует сегментация мостов, то архитектура типа Unet. Если детектирование прямоугольника, то SSD, RetinaNet, Faster RCNN, YOLO.


  10. Этот пункт можно выполнять по-разному.

    Первый вариант, который я описывал: Например, у нас есть первоначальный прямоугольник 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). Или оставлять только самые лучшие расширения.


  11. На правах идеи, подглядел где-то у Смородова:

    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;

    - для посчёта сумм внутри прямоугольников можно предварительно вычислить интегральное изображение и брать суммы по нему.


  12. Датасет тут не поможет. Из-за Jpeg артефактов определить наличие пассажира как-то автоматичеки или доулучшать контраст не получится. Всё, что может тут машина сделать сама, сможет сделать и человек с Фотошопом. Чудо могло бы быть, если бы был не кадр а видео. Тогда с superresolution или аналогом можно было бы попробовать что-то вытащить.


  13. Думаю, что для произвольного изображения определить размытость невозможно. А как же Боке? Или сцена без деталей? Ещё как-то вопрос можно решить для видео.

    Поэтому поддерживаю предыдущий вариант - натренировать классификатор.


  14. Я уже несколько лет не занимался всем этим видеозахватом. Но сегодня увидел предложение отключить в OpenCV поддержку vfw и DirectShow как устаревшие технологии. Поэтому я бы не стал доверять OpenCV  в таком вопросе. Я не о том, что они что-то неправильно делают, технологии и правда очень старые, им десятилетия. Но если захват видео с требуемой скоростью - это критическая задача, то лучше делать всё самому на требуемом API, а не полагаться на стороннего разработчика, который в угоду стройности идей своей библиотеки может либо замедлить, либо отключить что-то важное. Всё таки в OpenCV видеозахват - это не главное. Важно только, чтобы он был.

×