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();