31 init_effect_details();
42 init_effect_details();
46 void Sharpen::init_effect_details()
51 info.
description =
"Boost edge contrast to make video details look crisper.";
57 static void boxes_for_gauss(
double sigma,
int b[3])
60 double wi = std::sqrt((12.0 * sigma * sigma / n) + 1.0);
61 int wl = int(std::floor(wi));
64 double mi = (12.0 * sigma * sigma - n*wl*wl - 4.0*n*wl - 3.0*n)
66 int m = int(std::round(mi));
67 for (
int i = 0; i < n; ++i)
68 b[i] = i < m ? wl : wu;
72 static void blur_axis(
const QImage& src, QImage& dst,
int r,
bool vertical)
81 int bpl = src.bytesPerLine();
82 const uchar* in = src.bits();
83 uchar* out = dst.bits();
87 #pragma omp parallel for
88 for (
int y = 0; y < H; ++y) {
89 const uchar* rowIn = in + y*bpl;
90 uchar* rowOut = out + y*bpl;
91 double sB = rowIn[0]*(r+1), sG = rowIn[1]*(r+1),
92 sR = rowIn[2]*(r+1), sA = rowIn[3]*(r+1);
93 for (
int x = 1; x <= r; ++x) {
94 const uchar* p = rowIn + std::min(x, W-1)*4;
95 sB += p[0]; sG += p[1]; sR += p[2]; sA += p[3];
97 for (
int x = 0; x < W; ++x) {
98 uchar* o = rowOut + x*4;
99 o[0] = uchar(sB / window + 0.5);
100 o[1] = uchar(sG / window + 0.5);
101 o[2] = uchar(sR / window + 0.5);
102 o[3] = uchar(sA / window + 0.5);
104 const uchar* addP = rowIn + std::min(x+r+1, W-1)*4;
105 const uchar* subP = rowIn + std::max(x-r, 0)*4;
106 sB += addP[0] - subP[0];
107 sG += addP[1] - subP[1];
108 sR += addP[2] - subP[2];
109 sA += addP[3] - subP[3];
114 #pragma omp parallel for
115 for (
int x = 0; x < W; ++x) {
116 double sB = 0, sG = 0, sR = 0, sA = 0;
117 const uchar* p0 = in + x*4;
118 sB = p0[0]*(r+1); sG = p0[1]*(r+1);
119 sR = p0[2]*(r+1); sA = p0[3]*(r+1);
120 for (
int y = 1; y <= r; ++y) {
121 const uchar* p = in + std::min(y, H-1)*bpl + x*4;
122 sB += p[0]; sG += p[1]; sR += p[2]; sA += p[3];
124 for (
int y = 0; y < H; ++y) {
125 uchar* o = out + y*bpl + x*4;
126 o[0] = uchar(sB / window + 0.5);
127 o[1] = uchar(sG / window + 0.5);
128 o[2] = uchar(sR / window + 0.5);
129 o[3] = uchar(sA / window + 0.5);
131 const uchar* addP = in + std::min(y+r+1, H-1)*bpl + x*4;
132 const uchar* subP = in + std::max(y-r, 0)*bpl + x*4;
133 sB += addP[0] - subP[0];
134 sG += addP[1] - subP[1];
135 sR += addP[2] - subP[2];
136 sA += addP[3] - subP[3];
143 static void box_blur(
const QImage& src, QImage& dst,
double rf,
bool vertical)
145 int r0 = int(std::floor(rf));
149 blur_axis(src, dst, r0, vertical);
152 QImage a(src.size(), QImage::Format_ARGB32);
153 QImage b(src.size(), QImage::Format_ARGB32);
154 blur_axis(src, a, r0, vertical);
155 blur_axis(src, b, r1, vertical);
157 int pixels = src.width() * src.height();
158 const uchar* pa = a.bits();
159 const uchar* pb = b.bits();
160 uchar* pd = dst.bits();
161 #pragma omp parallel for
162 for (
int i = 0; i < pixels; ++i) {
163 for (
int c = 0; c < 4; ++c) {
164 pd[i*4+c] = uchar((1.0 - f) * pa[i*4+c]
173 static void gauss_blur(
const QImage& src, QImage& dst,
double sigma)
176 boxes_for_gauss(sigma, b);
177 QImage t1(src.size(), QImage::Format_ARGB32);
178 QImage t2(src.size(), QImage::Format_ARGB32);
180 double r = 0.5 * (b[0] - 1);
181 box_blur(src , t1, r,
false);
182 box_blur(t1, t2, r,
true);
184 r = 0.5 * (b[1] - 1);
185 box_blur(t2, t1, r,
false);
186 box_blur(t1, t2, r,
true);
188 r = 0.5 * (b[2] - 1);
189 box_blur(t2, t1, r,
false);
190 box_blur(t1, dst, r,
true);
195 std::shared_ptr<Frame> frame, int64_t frame_number)
197 auto img = frame->GetImage();
198 if (!img || img->isNull())
200 if (img->format() != QImage::Format_ARGB32)
201 *img = img->convertToFormat(QImage::Format_ARGB32);
203 int W = img->width();
204 int H = img->height();
205 if (W <= 0 || H <= 0)
214 double sigma = std::max(0.1, rpx * H / 720.0);
217 QImage blur(W, H, QImage::Format_ARGB32);
218 gauss_blur(*img, blur, sigma);
221 int bplS = img->bytesPerLine();
222 int bplB = blur.bytesPerLine();
223 uchar* sBits = img->bits();
224 uchar* bBits = blur.bits();
227 #pragma omp parallel for reduction(max:maxDY)
228 for (
int y = 0; y < H; ++y) {
229 uchar* sRow = sBits + y * bplS;
230 uchar* bRow = bBits + y * bplB;
231 for (
int x = 0; x < W; ++x) {
232 double dB = double(sRow[x*4+0]) - double(bRow[x*4+0]);
233 double dG = double(sRow[x*4+1]) - double(bRow[x*4+1]);
234 double dR = double(sRow[x*4+2]) - double(bRow[x*4+2]);
235 double dY = std::abs(0.114*dB + 0.587*dG + 0.299*dR);
236 maxDY = std::max(maxDY, dY);
241 double thr = thrUI * maxDY;
244 #pragma omp parallel for
245 for (
int y = 0; y < H; ++y) {
246 uchar* sRow = sBits + y * bplS;
247 uchar* bRow = bBits + y * bplB;
248 for (
int x = 0; x < W; ++x) {
249 uchar* sp = sRow + x*4;
250 uchar* bp = bRow + x*4;
253 double dB = double(sp[0]) - double(bp[0]);
254 double dG = double(sp[1]) - double(bp[1]);
255 double dR = double(sp[2]) - double(bp[2]);
256 double dY = 0.114*dB + 0.587*dG + 0.299*dR;
259 if (std::abs(dY) < thr)
263 auto halo = [](
double d) {
264 return (255.0 - std::abs(d)) / 255.0;
275 const double wB = 0.114, wG = 0.587, wR = 0.299;
279 double lumaInc = amt * dY;
280 outC[0] = bp[0] + lumaInc * wB;
281 outC[1] = bp[1] + lumaInc * wG;
282 outC[2] = bp[2] + lumaInc * wR;
286 double lumaDetail = dY;
287 double chromaB = dB - lumaDetail * wB;
288 double chromaG = dG - lumaDetail * wG;
289 double chromaR = dR - lumaDetail * wR;
290 outC[0] = bp[0] + amt * chromaB;
291 outC[1] = bp[1] + amt * chromaG;
292 outC[2] = bp[2] + amt * chromaR;
296 outC[0] = bp[0] + amt * dB;
297 outC[1] = bp[1] + amt * dG;
298 outC[2] = bp[2] + amt * dR;
305 double inc = amt * dY * halo(dY);
306 for (
int c = 0; c < 3; ++c)
307 outC[c] = sp[c] + inc;
312 double chroma[3] = { dB - l, dG - l, dR - l };
313 for (
int c = 0; c < 3; ++c)
314 outC[c] = sp[c] + amt * chroma[c] * halo(chroma[c]);
318 outC[0] = sp[0] + amt * dB * halo(dB);
319 outC[1] = sp[1] + amt * dG * halo(dG);
320 outC[2] = sp[2] + amt * dR * halo(dR);
325 for (
int c = 0; c < 3; ++c) {
326 sp[c] = uchar(std::clamp(outC[c], 0.0, 255.0) + 0.5);
362 if (!root[
"amount"].isNull())
364 if (!root[
"radius"].isNull())
366 if (!root[
"threshold"].isNull())
368 if (!root[
"mode"].isNull())
369 mode = root[
"mode"].asInt();
370 if (!root[
"channel"].isNull())
371 channel = root[
"channel"].asInt();
385 "Mode",
mode,
"int",
"",
nullptr, 0, 1,
false, t);
389 "Channel",
channel,
"int",
"",
nullptr, 0, 2,
false, t);
393 return root.toStyledString();