32 init_effect_details();
44 init_effect_details();
48 void Sharpen::init_effect_details()
53 info.
description =
"Boost edge contrast to make video details look crisper.";
65 std::shared_ptr<QImage> mask_image, int64_t frame_number)
const
68 if (!original_image || !effected_image || !mask_image)
70 if (original_image->size() != effected_image->size() || effected_image->size() != mask_image->size())
73 unsigned char* original_pixels =
reinterpret_cast<unsigned char*
>(original_image->bits());
74 unsigned char* effected_pixels =
reinterpret_cast<unsigned char*
>(effected_image->bits());
75 unsigned char* mask_pixels =
reinterpret_cast<unsigned char*
>(mask_image->bits());
76 const int pixel_count = effected_image->width() * effected_image->height();
78 #pragma omp parallel for schedule(static)
79 for (
int i = 0; i < pixel_count; ++i) {
80 const int idx = i * 4;
81 float factor =
static_cast<float>(qGray(mask_pixels[idx], mask_pixels[idx + 1], mask_pixels[idx + 2])) / 255.0f;
83 factor = 1.0f - factor;
84 factor = factor * factor;
85 const float inverse = 1.0f - factor;
87 effected_pixels[idx] =
static_cast<unsigned char>(
88 (original_pixels[idx] * inverse) + (effected_pixels[idx] * factor));
89 effected_pixels[idx + 1] =
static_cast<unsigned char>(
90 (original_pixels[idx + 1] * inverse) + (effected_pixels[idx + 1] * factor));
91 effected_pixels[idx + 2] =
static_cast<unsigned char>(
92 (original_pixels[idx + 2] * inverse) + (effected_pixels[idx + 2] * factor));
93 effected_pixels[idx + 3] = original_pixels[idx + 3];
98 static void boxes_for_gauss(
double sigma,
int b[3])
101 double wi = std::sqrt((12.0 * sigma * sigma / n) + 1.0);
102 int wl = int(std::floor(wi));
105 double mi = (12.0 * sigma * sigma - n*wl*wl - 4.0*n*wl - 3.0*n)
107 int m = int(std::round(mi));
108 for (
int i = 0; i < n; ++i)
109 b[i] = i < m ? wl : wu;
113 static void blur_axis(
const QImage& src, QImage& dst,
int r,
bool vertical)
121 int H = src.height();
122 int bpl = src.bytesPerLine();
123 const uchar* in = src.bits();
124 uchar* out = dst.bits();
125 int window = 2*r + 1;
128 #pragma omp parallel for
129 for (
int y = 0; y < H; ++y) {
130 const uchar* rowIn = in + y*bpl;
131 uchar* rowOut = out + y*bpl;
132 double sB = rowIn[0]*(r+1), sG = rowIn[1]*(r+1),
133 sR = rowIn[2]*(r+1), sA = rowIn[3]*(r+1);
134 for (
int x = 1; x <= r; ++x) {
135 const uchar* p = rowIn + std::min(x, W-1)*4;
136 sB += p[0]; sG += p[1]; sR += p[2]; sA += p[3];
138 for (
int x = 0; x < W; ++x) {
139 uchar* o = rowOut + x*4;
140 o[0] = uchar(sB / window + 0.5);
141 o[1] = uchar(sG / window + 0.5);
142 o[2] = uchar(sR / window + 0.5);
143 o[3] = uchar(sA / window + 0.5);
145 const uchar* addP = rowIn + std::min(x+r+1, W-1)*4;
146 const uchar* subP = rowIn + std::max(x-r, 0)*4;
147 sB += addP[0] - subP[0];
148 sG += addP[1] - subP[1];
149 sR += addP[2] - subP[2];
150 sA += addP[3] - subP[3];
155 #pragma omp parallel for
156 for (
int x = 0; x < W; ++x) {
157 double sB = 0, sG = 0, sR = 0, sA = 0;
158 const uchar* p0 = in + x*4;
159 sB = p0[0]*(r+1); sG = p0[1]*(r+1);
160 sR = p0[2]*(r+1); sA = p0[3]*(r+1);
161 for (
int y = 1; y <= r; ++y) {
162 const uchar* p = in + std::min(y, H-1)*bpl + x*4;
163 sB += p[0]; sG += p[1]; sR += p[2]; sA += p[3];
165 for (
int y = 0; y < H; ++y) {
166 uchar* o = out + y*bpl + x*4;
167 o[0] = uchar(sB / window + 0.5);
168 o[1] = uchar(sG / window + 0.5);
169 o[2] = uchar(sR / window + 0.5);
170 o[3] = uchar(sA / window + 0.5);
172 const uchar* addP = in + std::min(y+r+1, H-1)*bpl + x*4;
173 const uchar* subP = in + std::max(y-r, 0)*bpl + x*4;
174 sB += addP[0] - subP[0];
175 sG += addP[1] - subP[1];
176 sR += addP[2] - subP[2];
177 sA += addP[3] - subP[3];
184 static void box_blur(
const QImage& src, QImage& dst,
double rf,
bool vertical)
186 int r0 = int(std::floor(rf));
190 blur_axis(src, dst, r0, vertical);
193 QImage a(src.size(), QImage::Format_ARGB32);
194 QImage b(src.size(), QImage::Format_ARGB32);
195 blur_axis(src, a, r0, vertical);
196 blur_axis(src, b, r1, vertical);
198 int pixels = src.width() * src.height();
199 const uchar* pa = a.bits();
200 const uchar* pb = b.bits();
201 uchar* pd = dst.bits();
202 #pragma omp parallel for
203 for (
int i = 0; i < pixels; ++i) {
204 for (
int c = 0; c < 4; ++c) {
205 pd[i*4+c] = uchar((1.0 - f) * pa[i*4+c]
214 static void gauss_blur(
const QImage& src, QImage& dst,
double sigma)
217 boxes_for_gauss(sigma, b);
218 QImage t1(src.size(), QImage::Format_ARGB32);
219 QImage t2(src.size(), QImage::Format_ARGB32);
221 double r = 0.5 * (b[0] - 1);
222 box_blur(src , t1, r,
false);
223 box_blur(t1, t2, r,
true);
225 r = 0.5 * (b[1] - 1);
226 box_blur(t2, t1, r,
false);
227 box_blur(t1, t2, r,
true);
229 r = 0.5 * (b[2] - 1);
230 box_blur(t2, t1, r,
false);
231 box_blur(t1, dst, r,
true);
236 std::shared_ptr<Frame> frame, int64_t frame_number)
238 auto img = frame->GetImage();
239 if (!img || img->isNull())
241 if (img->format() != QImage::Format_ARGB32)
242 *img = img->convertToFormat(QImage::Format_ARGB32);
244 int W = img->width();
245 int H = img->height();
246 if (W <= 0 || H <= 0)
255 double sigma = std::max(0.1, rpx * H / 720.0);
258 QImage blur(W, H, QImage::Format_ARGB32);
259 gauss_blur(*img, blur, sigma);
262 int bplS = img->bytesPerLine();
263 int bplB = blur.bytesPerLine();
264 uchar* sBits = img->bits();
265 uchar* bBits = blur.bits();
268 #pragma omp parallel for reduction(max:maxDY)
269 for (
int y = 0; y < H; ++y) {
270 uchar* sRow = sBits + y * bplS;
271 uchar* bRow = bBits + y * bplB;
272 for (
int x = 0; x < W; ++x) {
273 double dB = double(sRow[x*4+0]) - double(bRow[x*4+0]);
274 double dG = double(sRow[x*4+1]) - double(bRow[x*4+1]);
275 double dR = double(sRow[x*4+2]) - double(bRow[x*4+2]);
276 double dY = std::abs(0.114*dB + 0.587*dG + 0.299*dR);
277 maxDY = std::max(maxDY, dY);
282 double thr = thrUI * maxDY;
285 #pragma omp parallel for
286 for (
int y = 0; y < H; ++y) {
287 uchar* sRow = sBits + y * bplS;
288 uchar* bRow = bBits + y * bplB;
289 for (
int x = 0; x < W; ++x) {
290 uchar* sp = sRow + x*4;
291 uchar* bp = bRow + x*4;
294 double dB = double(sp[0]) - double(bp[0]);
295 double dG = double(sp[1]) - double(bp[1]);
296 double dR = double(sp[2]) - double(bp[2]);
297 double dY = 0.114*dB + 0.587*dG + 0.299*dR;
300 if (std::abs(dY) < thr)
304 auto halo = [](
double d) {
305 return (255.0 - std::abs(d)) / 255.0;
316 const double wB = 0.114, wG = 0.587, wR = 0.299;
320 double lumaInc = amt * dY;
321 outC[0] = bp[0] + lumaInc * wB;
322 outC[1] = bp[1] + lumaInc * wG;
323 outC[2] = bp[2] + lumaInc * wR;
327 double lumaDetail = dY;
328 double chromaB = dB - lumaDetail * wB;
329 double chromaG = dG - lumaDetail * wG;
330 double chromaR = dR - lumaDetail * wR;
331 outC[0] = bp[0] + amt * chromaB;
332 outC[1] = bp[1] + amt * chromaG;
333 outC[2] = bp[2] + amt * chromaR;
337 outC[0] = bp[0] + amt * dB;
338 outC[1] = bp[1] + amt * dG;
339 outC[2] = bp[2] + amt * dR;
346 double inc = amt * dY * halo(dY);
347 for (
int c = 0; c < 3; ++c)
348 outC[c] = sp[c] + inc;
353 double chroma[3] = { dB - l, dG - l, dR - l };
354 for (
int c = 0; c < 3; ++c)
355 outC[c] = sp[c] + amt * chroma[c] * halo(chroma[c]);
359 outC[0] = sp[0] + amt * dB * halo(dB);
360 outC[1] = sp[1] + amt * dG * halo(dG);
361 outC[2] = sp[2] + amt * dR * halo(dR);
366 for (
int c = 0; c < 3; ++c) {
367 sp[c] = uchar(std::clamp(outC[c], 0.0, 255.0) + 0.5);
404 if (!root[
"amount"].isNull())
406 if (!root[
"radius"].isNull())
408 if (!root[
"threshold"].isNull())
410 if (!root[
"mode"].isNull())
411 mode = root[
"mode"].asInt();
412 if (!root[
"channel"].isNull())
413 channel = root[
"channel"].asInt();
414 if (!root[
"mask_mode"].isNull())
429 "Mode",
mode,
"int",
"",
nullptr, 0, 1,
false, t);
433 "Channel",
channel,
"int",
"",
nullptr, 0, 2,
false, t);
438 "Mask Mode",
mask_mode,
"int",
"",
nullptr, 0, 1,
false, t);
441 return root.toStyledString();