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;
126 const float inv_w = 1.0f / window;
129 #pragma omp parallel for
130 for (
int y = 0; y < H; ++y) {
131 const uchar* rowIn = in + y*bpl;
132 uchar* rowOut = out + y*bpl;
133 int sB = rowIn[0]*(r+1), sG = rowIn[1]*(r+1), sR = rowIn[2]*(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];
138 for (
int x = 0; x < W; ++x) {
139 uchar* o = rowOut + x*4;
140 o[0] = uchar(sB * inv_w + 0.5f);
141 o[1] = uchar(sG * inv_w + 0.5f);
142 o[2] = uchar(sR * inv_w + 0.5f);
144 const uchar* addP = rowIn + std::min(x+r+1, W-1)*4;
145 const uchar* subP = rowIn + std::max(x-r, 0)*4;
146 sB += addP[0] - subP[0];
147 sG += addP[1] - subP[1];
148 sR += addP[2] - subP[2];
153 #pragma omp parallel for
154 for (
int x = 0; x < W; ++x) {
155 const uchar* p0 = in + x*4;
156 int sB = p0[0]*(r+1), sG = p0[1]*(r+1), sR = p0[2]*(r+1);
157 for (
int y = 1; y <= r; ++y) {
158 const uchar* p = in + std::min(y, H-1)*bpl + x*4;
159 sB += p[0]; sG += p[1]; sR += p[2];
161 for (
int y = 0; y < H; ++y) {
162 uchar* o = out + y*bpl + x*4;
163 o[0] = uchar(sB * inv_w + 0.5f);
164 o[1] = uchar(sG * inv_w + 0.5f);
165 o[2] = uchar(sR * inv_w + 0.5f);
167 const uchar* addP = in + std::min(y+r+1, H-1)*bpl + x*4;
168 const uchar* subP = in + std::max(y-r, 0)*bpl + x*4;
169 sB += addP[0] - subP[0];
170 sG += addP[1] - subP[1];
171 sR += addP[2] - subP[2];
178 static void box_blur(
const QImage& src, QImage& dst,
double rf,
bool vertical)
180 int r0 = int(std::floor(rf));
184 blur_axis(src, dst, r0, vertical);
187 QImage a(src.size(), QImage::Format_ARGB32);
188 QImage b(src.size(), QImage::Format_ARGB32);
189 blur_axis(src, a, r0, vertical);
190 blur_axis(src, b, r1, vertical);
192 int pixels = src.width() * src.height();
193 const uchar* pa = a.bits();
194 const uchar* pb = b.bits();
195 uchar* pd = dst.bits();
196 const float ff = float(f);
197 const float ff1 = 1.0f - ff;
198 #pragma omp parallel for
199 for (
int i = 0; i < pixels; ++i) {
200 for (
int c = 0; c < 3; ++c) {
201 pd[i*4+c] = uchar(ff1 * pa[i*4+c] + ff * pb[i*4+c] + 0.5f);
208 static void gauss_blur(
const QImage& src, QImage& dst,
double sigma)
211 boxes_for_gauss(sigma, b);
212 QImage t1(src.size(), QImage::Format_ARGB32);
213 QImage t2(src.size(), QImage::Format_ARGB32);
215 double r = 0.5 * (b[0] - 1);
216 box_blur(src , t1, r,
false);
217 box_blur(t1, t2, r,
true);
219 r = 0.5 * (b[1] - 1);
220 box_blur(t2, t1, r,
false);
221 box_blur(t1, t2, r,
true);
223 r = 0.5 * (b[2] - 1);
224 box_blur(t2, t1, r,
false);
225 box_blur(t1, dst, r,
true);
230 std::shared_ptr<Frame> frame, int64_t frame_number)
232 auto img = frame->GetImage();
233 if (!img || img->isNull())
235 if (img->format() != QImage::Format_ARGB32)
236 *img = img->convertToFormat(QImage::Format_ARGB32);
238 int W = img->width();
239 int H = img->height();
240 if (W <= 0 || H <= 0)
248 if (amt == 0.0 || rpx <= 0.0)
252 double sigma = std::max(0.1, rpx * H / 720.0);
255 QImage blur(W, H, QImage::Format_ARGB32);
256 gauss_blur(*img, blur, sigma);
259 int bplS = img->bytesPerLine();
260 int bplB = blur.bytesPerLine();
261 uchar* sBits = img->bits();
262 uchar* bBits = blur.bits();
266 #pragma omp parallel for reduction(max:maxDY)
267 for (
int y = 0; y < H; ++y) {
268 uchar* sRow = sBits + y * bplS;
269 uchar* bRow = bBits + y * bplB;
270 for (
int x = 0; x < W; ++x) {
271 float dB = float(sRow[x*4+0]) - float(bRow[x*4+0]);
272 float dG = float(sRow[x*4+1]) - float(bRow[x*4+1]);
273 float dR = float(sRow[x*4+2]) - float(bRow[x*4+2]);
274 float dY = std::abs(0.114f*dB + 0.587f*dG + 0.299f*dR);
275 maxDY = std::max(maxDY, dY);
281 const float thr = float(thrUI) * maxDY;
282 const float famt = float(amt);
284 #pragma omp parallel for
285 for (
int y = 0; y < H; ++y) {
286 uchar* sRow = sBits + y * bplS;
287 uchar* bRow = bBits + y * bplB;
288 for (
int x = 0; x < W; ++x) {
289 uchar* sp = sRow + x*4;
290 uchar* bp = bRow + x*4;
293 float dB = float(sp[0]) - float(bp[0]);
294 float dG = float(sp[1]) - float(bp[1]);
295 float dR = float(sp[2]) - float(bp[2]);
296 float dY = 0.114f*dB + 0.587f*dG + 0.299f*dR;
299 if (std::abs(dY) < thr)
303 auto halo = [](
float d) {
304 return (255.0f - std::abs(d)) * (1.0f / 255.0f);
311 const float wB = 0.114f, wG = 0.587f, wR = 0.299f;
315 float lumaInc = famt * dY;
316 outC[0] = bp[0] + lumaInc * wB;
317 outC[1] = bp[1] + lumaInc * wG;
318 outC[2] = bp[2] + lumaInc * wR;
322 float chromaB = dB - dY * wB;
323 float chromaG = dG - dY * wG;
324 float chromaR = dR - dY * wR;
325 outC[0] = bp[0] + famt * chromaB;
326 outC[1] = bp[1] + famt * chromaG;
327 outC[2] = bp[2] + famt * chromaR;
331 outC[0] = bp[0] + famt * dB;
332 outC[1] = bp[1] + famt * dG;
333 outC[2] = bp[2] + famt * dR;
340 float inc = famt * dY * halo(dY);
341 for (
int c = 0; c < 3; ++c)
342 outC[c] = sp[c] + inc;
346 float chroma[3] = { dB - dY, dG - dY, dR - dY };
347 for (
int c = 0; c < 3; ++c)
348 outC[c] = sp[c] + famt * chroma[c] * halo(chroma[c]);
352 outC[0] = sp[0] + famt * dB * halo(dB);
353 outC[1] = sp[1] + famt * dG * halo(dG);
354 outC[2] = sp[2] + famt * dR * halo(dR);
359 for (
int c = 0; c < 3; ++c) {
360 sp[c] = uchar(std::clamp(outC[c], 0.0f, 255.0f) + 0.5f);
397 if (!root[
"amount"].isNull())
399 if (!root[
"radius"].isNull())
401 if (!root[
"threshold"].isNull())
403 if (!root[
"mode"].isNull())
404 mode = root[
"mode"].asInt();
405 if (!root[
"channel"].isNull())
406 channel = root[
"channel"].asInt();
407 if (!root[
"mask_mode"].isNull())
422 "Mode",
mode,
"int",
"",
nullptr, 0, 1,
false, t);
426 "Channel",
channel,
"int",
"",
nullptr, 0, 2,
false, t);
431 "Mask Mode",
mask_mode,
"int",
"",
nullptr, 0, 1,
false, t);
434 return root.toStyledString();