This repository has been archived by the owner on Oct 3, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy paththumbnail.cc
139 lines (123 loc) · 3.6 KB
/
thumbnail.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
extern "C" {
#include "thumbnail.h"
}
#include "distort.hh"
#include <cstring>
#include <functional>
#include <iostream>
#include <opencv2/opencv.hpp>
#include <stdexcept>
#if CV_MAJOR_VERSION > 3
#define CV_INTER_LINEAR cv::INTER_LINEAR
#define CV_IMWRITE_JPEG_QUALITY cv::IMWRITE_JPEG_QUALITY
#endif
// Size of thumbnail dimension. thumbnail is always a square.
static const int thumb_dim = 150;
static const char* thumbnail(
cv::CascadeClassifier* c, const char* path, Buffer* thumb)
{
static const char no_faces[] = "no faces detected";
const cv::Mat colour = cv::imread(path, cv::IMREAD_COLOR);
if (colour.empty()) {
return no_faces;
}
// Always keep the resulting Mat in dst and swap before a new operation
cv::Mat src, dst;
auto swap = [&]() { cv::swap(src, dst); };
// Initial assignment does not need swap
cv::cvtColor(colour, dst, cv::COLOR_BGR2GRAY);
swap();
cv::equalizeHist(src, dst);
std::vector<cv::Rect> faces;
c->detectMultiScale(dst, faces, 1.1, 5, 0, cv::Size(50, 50));
if (!faces.size()) {
return no_faces;
}
cv::Rect face;
if (faces.size() == 1) {
face = faces.front();
} else {
// Find biggest match
uint64_t max_size = 0;
for (auto& f : faces) {
uint64_t s = (uint64_t)f.width + (uint64_t)f.height;
if (s > max_size) {
face = f;
max_size = s;
}
}
}
// Increase matched size, if image bellow 150x150.
// face should always be a square.
if (face.width < thumb_dim && face.height == face.width) {
// Perform bounds checks and find the largest equal increase size in all
// directions
int diff = (150 - face.width) / 2;
if (face.x - diff < 0) {
diff = face.x;
}
if (face.y - diff < 0) {
diff = face.y;
}
if (face.x + face.width + diff > colour.cols) {
diff = colour.cols - (face.x + face.width);
}
if (face.y + face.height + diff > colour.rows) {
diff = colour.rows - (face.y + face.height);
}
face.x -= diff;
face.y -= diff;
face.width += diff;
face.height += diff;
}
cv::resize(cv::Mat(colour, face), dst, cv::Size(thumb_dim, thumb_dim), 0, 0,
CV_INTER_LINEAR);
swap();
cpli_distort_mat(src, dst);
std::vector<unsigned char> out;
static const std::vector<int> params = { CV_IMWRITE_JPEG_QUALITY, 85 };
if (!cv::imencode(".jpg", dst, out, params)) {
return "could not encode result";
}
const auto s = out.size();
thumb->data = memcpy(malloc(s), out.data(), s);
thumb->size = s;
return 0;
}
static char* malloc_string(const char* s)
{
return strcpy((char*)malloc(strlen(s) + 1), s);
}
static char* catch_errors(std::function<const char*()> fn)
{
try {
auto err = fn();
if (err) {
return malloc_string(err);
}
return nullptr;
} catch (const std::exception& ex) {
return malloc_string(ex.what());
}
}
extern "C" void* cpli_load_classifier(const char* path)
{
auto c = new cv::CascadeClassifier();
if (!c->load(path)) {
delete c;
return nullptr;
}
return c;
}
extern "C" void cpli_unload_classifier(void* c)
{
delete static_cast<cv::CascadeClassifier*>(c);
}
extern "C" char* cpli_thumbnail(
void* classifier, const char* path, Buffer* thumb)
{
return catch_errors([=]() {
return thumbnail(
static_cast<cv::CascadeClassifier*>(classifier), path, thumb);
});
}