#include "featureannotations.h"
-#include <QLoggingCategory>
#include <QImage>
+#include <QLoggingCategory>
Q_LOGGING_CATEGORY(annotationsCategory, "Annotations")
+Q_LOGGING_CATEGORY(annotationsVerboseCategory, "Annotations.verbose")
#include <dlib/dnn.h>
#include <dlib/image_saver/image_saver.h>
+#include <QMetaObject>
+#include <QtConcurrent>
+
// random windows.h defines
#undef interface
using namespace std;
using namespace dlib;
+template <long num_filters, typename SUBNET>
+using con5d = con<num_filters, 5, 5, 2, 2, SUBNET>;
+template <long num_filters, typename SUBNET>
+using con5 = con<num_filters, 5, 5, 1, 1, SUBNET>;
-template <long num_filters, typename SUBNET> using con5d = con<num_filters,5,5,2,2,SUBNET>;
-template <long num_filters, typename SUBNET> using con5 = con<num_filters,5,5,1,1,SUBNET>;
+template <typename SUBNET>
+using downsampler_r = relu<affine<
+ con5d<32, relu<affine<con5d<32, relu<affine<con5d<16, SUBNET>>>>>>>>>;
+template <typename SUBNET> using rcon5_r = relu<affine<con5<45, SUBNET>>>;
-template <typename SUBNET> using downsampler_r = relu<affine<con5d<32, relu<affine<con5d<32, relu<affine<con5d<16,SUBNET>>>>>>>>>;
-template <typename SUBNET> using rcon5_r = relu<affine<con5<45,SUBNET>>>;
-
-using detection_net_type = loss_mmod<con<1,9,9,1,1,rcon5_r<rcon5_r<rcon5_r<downsampler_r<input_rgb_image_pyramid<pyramid_down<6>>>>>>>>;
+using detection_net_type =
+ loss_mmod<con<1, 9, 9, 1, 1,
+ rcon5_r<rcon5_r<rcon5_r<downsampler_r<
+ input_rgb_image_pyramid<pyramid_down<6>>>>>>>>;
namespace {
detection_net_type net;
-}
+bool valid = false;
+} // namespace
FeatureAnnoations::FeatureAnnoations(QObject *parent) : QObject{parent} {
- deserialize("C:/_C/anime_face_recognition/mmod_network.dat") >> net;
+ try {
+ deserialize("C:/_C/anime_face_recognition/mmod_network.dat") >> net;
+ valid = true;
+ } catch (const std::exception &ex) {
+ qCWarning(annotationsCategory)
+ << "Failed to read neural network. Error:" << ex.what();
+ }
}
FeaturePluginInstance *
FeatureAnnoations::createInstance(QObject *instance,
- PlayerFeaturePlauginInterface *interface) {
+ PlayerFeaturePlauginInterface *interface) {
+ if (!valid)
+ throw std::exception{"Network failed to initialize"};
return new FeatureAnnoationsInstance(instance, interface);
}
: FeaturePluginInstance{instance, player} {
qCDebug(annotationsCategory) << "Registering with instance" << instance;
- connect(instance, SIGNAL(frameChanged(const QImage &)),
- this, SLOT(onFrameChanged(const QImage &)));
+ connect(instance, SIGNAL(frameChanged(const QImage &)), this,
+ SLOT(onFrameChanged(const QImage &)));
+ connect(&m_watcher, SIGNAL(finished()), this, SLOT(onResultReady()));
m_fpsTimer.setInterval(1000);
connect(&m_fpsTimer, &QTimer::timeout, [&]() {
- qCDebug(annotationsCategory) << "Did" << m_fpsCounter
- << "frames per seoncd";
+ qCDebug(annotationsCategory)
+ << "Did" << m_fpsCounter << "frames per seoncd";
m_fpsCounter = 0;
});
m_fpsTimer.start();
}
-void FeatureAnnoationsInstance::onFrameChanged(const QImage &image)
-{
- qCDebug(annotationsCategory) << "Frame changed! " << image.size() << image.format();
- if (image.size().isEmpty()) return;
- try {
- matrix<rgb_pixel> img{image.size().height(), image.size().width()};
-
- const auto *imageData = reinterpret_cast<const QRgb *>(image.bits());
- auto size = img.size();
-
- std::transform(imageData, imageData + size, img.begin(),
- [](const QRgb &d) {
- return rgb_pixel{static_cast<unsigned char>(qRed(d)),
- static_cast<unsigned char>(qGreen(d)),
- static_cast<unsigned char>(qBlue(d))};
- });
-
- //dlib::save_bmp(img, "testimage.bmp");
-
- auto dets = net(img);
- PlayerFeaturePlauginInterface::AnnotationList al;
- for (auto&& d : dets)
- al << PlayerFeaturePlauginInterface::Annotation{
- static_cast<double>(d.rect.left()) / image.size().width(),
- static_cast<double>(d.rect.top()) / image.size().height(),
- static_cast<double>(d.rect.right() - d.rect.left()) / image.size().width(),
- static_cast<double>(d.rect.bottom() - d.rect.top()) / image.size().height(),
- "red",
- ""
- };
- m_playerInterface->featureSetAnnotations(al);
- ++m_fpsCounter;
-
- } catch(const std::exception &ex) {
- qCDebug(annotationsCategory) << "Exception: " << ex.what();
- }
+void FeatureAnnoationsInstance::onFrameChanged(const QImage &image) {
+ qCDebug(annotationsVerboseCategory)
+ << "Frame changed! " << image.size() << image.format();
+ if (image.size().isEmpty())
+ return;
+
+ if (m_watcher.isRunning())
+ return;
+
+ qCDebug(annotationsVerboseCategory) << "Starting annotation detection...";
+
+ auto future = QtConcurrent::run(
+ [](QImage image) -> PlayerFeaturePlauginInterface::AnnotationList {
+ try {
+ const int maxHeight{600};
+ if (image.size().height() > maxHeight)
+ image = image.scaledToHeight(maxHeight, Qt::FastTransformation);
+
+ matrix<rgb_pixel> img{image.size().height(), image.size().width()};
+
+ const auto *imageData = reinterpret_cast<const QRgb *>(image.bits());
+ auto size = img.size();
+
+ std::transform(
+ imageData, imageData + size, img.begin(), [](const QRgb &d) {
+ return rgb_pixel{static_cast<unsigned char>(qRed(d)),
+ static_cast<unsigned char>(qGreen(d)),
+ static_cast<unsigned char>(qBlue(d))};
+ });
+
+ // dlib::save_bmp(img, "testimage.bmp");
+
+ auto dets = net(img);
+ PlayerFeaturePlauginInterface::AnnotationList al;
+ for (auto &&d : dets)
+ al << PlayerFeaturePlauginInterface::Annotation{
+ static_cast<double>(d.rect.left()) / image.size().width(),
+ static_cast<double>(d.rect.top()) / image.size().height(),
+ static_cast<double>(d.rect.right() - d.rect.left()) /
+ image.size().width(),
+ static_cast<double>(d.rect.bottom() - d.rect.top()) /
+ image.size().height(),
+ "red",
+ ""};
+
+ qCDebug(annotationsVerboseCategory)
+ << "Found" << al.size() << "annotations";
+ return al;
+
+ } catch (const std::exception &ex) {
+ qCWarning(annotationsCategory) << "Exception: " << ex.what();
+ }
+ return {};
+ },
+ std::move(image));
+
+ m_watcher.setFuture(future);
+}
+
+void FeatureAnnoationsInstance::onResultReady() {
+ ++m_fpsCounter;
+ m_playerInterface->featureSetAnnotations(m_watcher.result());
}