From f968ccdf77def8a9973dace17805be51838ebcde Mon Sep 17 00:00:00 2001 From: cxl Date: Mon, 15 Apr 2013 17:59:38 +0000 Subject: [PATCH] .uppdev (image filter) git-svn-id: svn://ultimatepp.org/upp/trunk@5982 f0d560ea-af0d-0410-9eb7-867de7ffcac7 --- uppdev/BiCubic/BiCubic.upp | 4 +- uppdev/BiCubic/bicubic.h | 2 + uppdev/BiCubic/fast.cpp | 2 +- uppdev/BiCubic/fast2.cpp | 108 ++++++++++++++++++++++++++++ uppdev/BiCubic/help.txt | 118 ++++++++++++++++++++++++++++++ uppdev/BiCubic/main.cpp | 142 +++++++++++++++++++++++++++++++++---- 6 files changed, 359 insertions(+), 17 deletions(-) create mode 100644 uppdev/BiCubic/fast2.cpp create mode 100644 uppdev/BiCubic/help.txt diff --git a/uppdev/BiCubic/BiCubic.upp b/uppdev/BiCubic/BiCubic.upp index 0dc7caebf..6fb3ec735 100644 --- a/uppdev/BiCubic/BiCubic.upp +++ b/uppdev/BiCubic/BiCubic.upp @@ -10,9 +10,11 @@ file bicubic.h, exact.cpp, fast.cpp, + fast2.cpp, DownScale.cpp, image.iml, - main.cpp; + main.cpp, + help.txt; mainconfig "" = "GUI SSE2"; diff --git a/uppdev/BiCubic/bicubic.h b/uppdev/BiCubic/bicubic.h index 61c7df34a..3ce15173c 100644 --- a/uppdev/BiCubic/bicubic.h +++ b/uppdev/BiCubic/bicubic.h @@ -12,4 +12,6 @@ enum { Image RescaleWithKernelE(const Image& _img, int cx, int cy, double (*kernel)(double x), int a, int method = DOWNSCALE_WIDE); Image RescaleWithKernel(const Image& _img, int cx, int cy, double (*kernel)(double x), int a, int method = DOWNSCALE_WIDE); +Image RescaleWithKernel2(const Image& _img, int cx, int cy, double (*kernel)(double x), int a, int method = DOWNSCALE_WIDE); + Image DownScale(const Image& img, int nx, int ny); diff --git a/uppdev/BiCubic/fast.cpp b/uppdev/BiCubic/fast.cpp index 4db6a30c5..f4c48ad73 100644 --- a/uppdev/BiCubic/fast.cpp +++ b/uppdev/BiCubic/fast.cpp @@ -12,7 +12,7 @@ static int sGeta(int a, int src, int tgt, int method) { if(method != DOWNSCALE_WIDE) return a; - return max(src / tgt, 1) * a; + return min(max(src / tgt, 1) * a, 7); } force_inline diff --git a/uppdev/BiCubic/fast2.cpp b/uppdev/BiCubic/fast2.cpp new file mode 100644 index 000000000..b54ccda54 --- /dev/null +++ b/uppdev/BiCubic/fast2.cpp @@ -0,0 +1,108 @@ +#include "bicubic.h" + +#if 1 + +#define LDUMP(x) // DUMP(x) + +static const int *sGetKernel(double (*kfn)(double x), int a, int shift) +{ + INTERLOCKED { + static VectorMap, Buffer > kache; + Tuple3 key = MakeTuple((uintptr_t)kfn, a, shift); + Buffer *k = kache.FindPtr(key); + if(k) + return *k; + RTIMING("GenKernel"); + Buffer& ktab = kache.GetAdd(key); + ktab.Alloc(((2 * a) << shift) + 1); + for(int i = 0; i < ((2 * a) << shift) + 1; i++) + ktab[i] = int((1 << shift) * (*kfn)((double)i / (1 << shift) - a)); + return ktab; + } +} + +force_inline +int sGetk(const int *kernel, int x, int a, int shift) +{ + x += a << shift; + ASSERT(x >= 0 && x < ((2 * a) << shift) + 1); + return kernel[x]; +} + +static int sGeta(int a, int src, int tgt, int method, int& shift) +{ + if(method != DOWNSCALE_WIDE) + return a; + int n = min(max(src / tgt, 1) * a, 31); + shift = 8 - n / 8; + return n; +} + +Image RescaleWithKernel2(const Image& img, int cx, int cy, double (*kfn)(double x), int a, int method) +{ + Size isz = img.GetSize(); + + int shiftx, shifty; + int ax = sGeta(a, isz.cx, cx, method, shiftx); + int ay = sGeta(a, isz.cy, cy, method, shifty); + + int shift = min(shiftx, shifty); + + const int *kernel = sGetKernel(kfn, a, shift); + + Buffer px(cx * 2 * ax * 2 * ay * 2); + int *xd = px; + for(int x = 0; x < cx; x++) { + int sx = x * isz.cx / cx; + int dx = ((x * isz.cx) << shift) / cx - (sx << shift); + for(int yy = -ay + 1; yy <= ay; yy++) { + for(int xx = -ax + 1; xx <= ax; xx++) { + *xd++ = minmax(sx + xx, 0, isz.cx - 1); + *xd++ = sGetk(kernel, ((xx << shift) - dx) * a / ax, a, shift); + } + } + } + + ImageBuffer ib(cx, cy); + RGBA *t = ~ib; + for(int y = 0; y < cy; y++) { + int sy = y * isz.cy / cy; + int dy = ((y * isz.cy) << shift) / cy - (sy << shift); + xd = ~px; + Buffer py(2 * ay * 2); + int *yd = py; + for(int yy = -ay + 1; yy <= ay; yy++) { + *yd++ = sGetk(kernel, ((yy << shift) - dy) * a / ay, a, shift); + *yd++ = minmax(sy + yy, 0, isz.cy - 1); + } + for(int x = 0; x < cx; x++) { + int red = 0; + int green = 0; + int blue = 0; + int alpha = 0; + int w = 0; + yd = py; + for(int yy = 2 * ay; yy-- > 0;) { + int ky = *yd++; + const RGBA *l = img[*yd++]; + for(int xx = 2 * ax; xx-- > 0;) { + const RGBA& s = l[*xd++]; + int weight = ky * *xd++; + red += weight * s.r; + green += weight * s.g; + blue += weight * s.b; + alpha += weight * s.a; + w += weight; + } + } + t->r = Saturate255(red / w); + t->g = Saturate255(green / w); + t->b = Saturate255(blue / w); + t->a = Saturate255(alpha / w); + t++; + } + } + return ib; +} + +#endif diff --git a/uppdev/BiCubic/help.txt b/uppdev/BiCubic/help.txt new file mode 100644 index 000000000..504d6d615 --- /dev/null +++ b/uppdev/BiCubic/help.txt @@ -0,0 +1,118 @@ +#include "bicubic.h" + +#if 1 + +#define LDUMP(x) // DUMP(x) + +#define up(x) ((x) << 8) +#define dn(x) ((x + (1 << 7)) >> 8) +#define dn2(x) ((x + (1 << 15)) >> 16) + +static int sGeta(int a, int src, int tgt, int method) +{ + if(method != DOWNSCALE_WIDE) + return a; + return min(max(src / tgt, 1) * a, 7); +} + +force_inline +int sGetk(const int *kernel, int x, int a) +{ + x += up(a); + ASSERT(x >= 0 && x < up(2 * a) + 1); + return kernel[x]; +} + +static const int *sGetKernel(double (*kfn)(double x), int a) +{ + INTERLOCKED { + static VectorMap, Buffer > kache; + Tuple2 key = MakeTuple((uintptr_t)kfn, a); + Buffer *k = kache.FindPtr(key); + if(k) + return *k; + RTIMING("GenKernel"); + Buffer& ktab = kache.GetAdd(key); + ktab.Alloc(up(2 * a) + 1); + for(int i = 0; i < up(2 * a) + 1; i++) + ktab[i] = int(up(1) * (*kfn)((double)i / up(1) - a)); + return ktab; + } +} + +Image RescaleWithKernel2(const Image& img, int cx, int cy, double (*kfn)(double x), int a, int method) +{ + // ToDo: Cache this! + const int *kernel = sGetKernel(kfn, a); + + Size isz = img.GetSize(); + + int ax = sGeta(a, isz.cx, cx, method); + int ay = sGeta(a, isz.cy, cy, method); + + Buffer px(cx * 2 * ax * 2 * ay * 2); + int *xd = px; + for(int x = 0; x < cx; x++) { + int sx = x * isz.cx / cx; + int dx = up(x * isz.cx) / cx - up(sx); + for(int yy = -ay + 1; yy <= ay; yy++) { + for(int xx = -ax + 1; xx <= ax; xx++) { + *xd++ = minmax(sx + xx, 0, isz.cx - 1); + *xd++ = sGetk(kernel, (up(xx) - dx) * a / ax, a); + } + } + } + + ImageBuffer ib(cx, cy); + RGBA *t = ~ib; + for(int y = 0; y < cy; y++) { + int sy = y * isz.cy / cy; + int dy = up(y * isz.cy) / cy - up(sy); + const int *xd = ~px; + for(int x = 0; x < cx; x++) { + int red = 0; + int green = 0; + int blue = 0; + int alpha = 0; + int w = 0; + for(int yy = -ay + 1; yy <= ay; yy++) { + int ky = sGetk(kernel, (up(yy) - dy) * a / ay, a); + const RGBA *l = img[minmax(sy + yy, 0, isz.cy - 1)]; + for(int xx = -ax + 1; xx <= ax; xx++) { + const RGBA& s = l[*xd++]; + int weight = ky * *xd++; + LDUMP(weight); + red += weight * s.r; + green += weight * s.g; + blue += weight * s.b; + LDUMP((int)s.a); + alpha += weight * s.a; + LDUMP(alpha); + w += weight; + } + } + t->r = Saturate255(red / w); + t->g = Saturate255(green / w); + t->b = Saturate255(blue / w); + t->a = Saturate255(alpha / w); + LDUMP((int)t->a); + LDUMP(w); + t++; + } + } + return ib; +} + +#endif +---------------- +TIMING Rescale Linear integer optimized: 79.00 ms - 789.97 us (79.00 ms / 100 ), min: 0.00 ns, max: 1.00 ms, nesting: 1 - 100 +TIMING GenKernel : 0.00 ns - 0.00 ns ( 0.00 ns / 1 ), min: 0.00 ns, max: 0.00 ns, nesting: 1 - 1 +TIMING Rescale Linear integer: 139.00 ms - 1.39 ms (139.00 ms / 100 ), min: 1.00 ms, max: 2.00 ms, nesting: 1 - 100 +TIMING Rescale : 20.00 ms - 199.97 us (20.00 ms / 100 ), min: 0.00 ns, max: 1.00 ms, nesting: 1 - 100 +TIMING GenKernel : 0.00 ns - 0.00 ns ( 0.00 ns / 2 ), min: 0.00 ns, max: 0.00 ns, nesting: 1 - 2 +---------------- +TIMING Rescale Linear integer optimized: 79.00 ms - 789.97 us (79.00 ms / 100 ), min: 0.00 ns, max: 1.00 ms, nesting: 1 - 100 +TIMING GenKernel : 0.00 ns - 0.00 ns ( 0.00 ns / 1 ), min: 0.00 ns, max: 0.00 ns, nesting: 1 - 1 +TIMING Rescale Linear integer: 140.00 ms - 1.40 ms (140.00 ms / 100 ), min: 1.00 ms, max: 2.00 ms, nesting: 1 - 100 +TIMING Rescale : 19.00 ms - 189.97 us (19.00 ms / 100 ), min: 0.00 ns, max: 1.00 ms, nesting: 1 - 100 +TIMING GenKernel : 0.00 ns - 0.00 ns ( 0.00 ns / 2 ), min: 0.00 ns, max: 0.00 ns, nesting: 1 - 2 diff --git a/uppdev/BiCubic/main.cpp b/uppdev/BiCubic/main.cpp index 218865c56..8469cd4c9 100644 --- a/uppdev/BiCubic/main.cpp +++ b/uppdev/BiCubic/main.cpp @@ -28,6 +28,65 @@ double BiCubicKernel2(double x) return (1 / 6.0) * r; } +double BiCubic(double x, double a) +{ + x = fabs(x); + return x <= 1 ? (a + 2) * x * x * x - (a + 3) * x * x + 1 : + x <= 2 ? a * x * x * x - 5 * a * x * x + 8 * a * x - 4 * a : + 0; +} + +double BiCubic0(double x) +{ + return BiCubic(x, 0); +} + +double BiCubic1(double x) +{ + return BiCubic(x, -0.25); +} + +double BiCubic2(double x) +{ + return BiCubic(x, -0.5); +} + +double BiCubic3(double x) +{ + return BiCubic(x, -1.0); +} + +double BiCubic4(double x) +{ + return BiCubic(x, -1.5); +} + +double BiCubic_(double x, double B, double C) +{ + x = fabs(x); + double x2 = x * x; + double x3 = x * x * x; + return + 1 / 6.0 * (x < 1 ? (12 - 9*B - 6*C) * x3 + (-18 + 12*B + 6*C) * x2 + (6 - 2*B) : + x < 2 ? (-B - 6*C) * x3 + (6*B + 30*C) * x2 + (-12*B - 48*C) *x + (8*B + 24*C) : + 0); +} + +double BiCubic_Bspline(double x) +{ + return BiCubic_(x, 1, 0); +} + +double BiCubic_Mitchell(double x) +{ + return BiCubic_(x, 1 / 3.0, 1 / 3.0); +} + +double BiCubic_CatmullRom(double x) +{ + return BiCubic_(x, 0, 1 / 2); +} + double Nearest(double x) { return x >= -0.5 && x <= 0.5; @@ -43,8 +102,7 @@ double LinearKernel(double x) double SimpleKernel(double x) { - x = fabs(x); - return x < 1.5 ? 1 : 0; + return 1; } double MagicKernel(double x) @@ -55,6 +113,14 @@ double MagicKernel(double x) 0; } +double BSpline(double x) +{ + x = fabs(x); + return x <= 1 ? (x * x * x) / 2 - x * x + 2.0 / 3 : + x <= 2 ? -(x * x * x) / 6 + x * x - 2 * x + 4.0 / 3 : + 0; +} + double Lanczos(double x, int a) { x = fabs(x); @@ -90,7 +156,7 @@ struct MyApp : TopWindow { DropList kernel; DropList method; - Option fast; + DropList fn; void Paint(Draw& w) { w.DrawRect(GetSize(), LtGray()); @@ -99,19 +165,36 @@ struct MyApp : TopWindow { Image (*rescale)(const Image& _img, int cx, int cy, double (*kernel)(double x), int a, int method); - if(fast) + if(~fn == 0) + rescale = RescaleWithKernelE; + else + if(~fn == 1) rescale = RescaleWithKernel; else - rescale = RescaleWithKernelE; + rescale = RescaleWithKernel2; double (*k)(double) = (double (*)(double))(int64)~kernel; int ka = k == Lanczos3 ? 3 : k == Lanczos4 ? 4 : k == Lanczos5 ? 5 : k == LinearKernel ? 1 : 2; + TimeStop tm; + for(int i = 0; i < TestImg().GetCount(); i++) { w.DrawImage(250 * i, 0, TestImg().Get(i)); w.DrawImage(250 * i, 200, rescale(TestImg().Get(i), 84, 84, k, ka, ~method)); + w.DrawImage(250 * i + 84, 200, rescale(TestImg().Get(i), 42, 42, k, ka, ~method)); + w.DrawImage(250 * i + 84 + 52, 200, rescale(TestImg().Get(i), 21, 21, k, ka, ~method)); + w.DrawImage(250 * i + 84 + 52 + 31, 200, rescale(TestImg().Get(i), 10, 10, k, ka, ~method)); + w.DrawImage(250 * i + 84 + 52 + 31 + 20, 200, rescale(TestImg().Get(i), 5, 5, k, ka, ~method)); + w.DrawImage(250 * i + 84 + 52 + 31 + 20 + 20, 200, rescale(TestImg().Get(i), 1, 1, k, ka, ~method)); w.DrawImage(250 * i, 300, rescale(TestImg().Get(i), 250, 250, k, ka, ~method)); + w.DrawImage(250 * i, GetSize().cy - 250 - 84 - 20, Rescale(TestImg().Get(i), 84, 84)); + w.DrawImage(250 * i, GetSize().cy - 250 - 20, Rescale(TestImg().Get(i), 250, 250)); } + + w.DrawImage(GetSize().cx - 400, 100, rescale(TestImg::img2(), 400, 400, k, ka, ~method)); + w.DrawImage(GetSize().cx - 400, 500, rescale(TestImg::img3(), 400, 400, k, ka, ~method)); + + w.DrawText(0, GetSize().cy - 20, String().Cat() << "Elapsed " << tm); /* w.DrawImage(0, 0, RescaleBicubic2(rings, 180, 180, k, ka, expand)); @@ -167,8 +250,19 @@ struct MyApp : TopWindow { kernel.Add((int64)Nearest, "Nearest"); kernel.Add((int64)SimpleKernel, "Simple"); kernel.Add((int64)LinearKernel, "Linear"); - kernel.Add((int64)BiCubicKernel2, "BiCubic"); + kernel.Add((int64)BiCubicKernel2, "BiCubic old"); + kernel.Add((int64)BiCubic0, "BiCubic0"); + kernel.Add((int64)BiCubic1, "BiCubic1"); + kernel.Add((int64)BiCubic2, "BiCubic2"); + kernel.Add((int64)BiCubic3, "BiCubic3"); + kernel.Add((int64)BiCubic4, "BiCubic4"); kernel.Add((int64)MagicKernel, "Magic"); + kernel.Add((int64)BSpline, "BSpline"); + + kernel.Add((int64)BiCubic_Bspline, "BiCubic_Bspline"); + kernel.Add((int64)BiCubic_Mitchell, "BiCubic_Mitchell"); + kernel.Add((int64)BiCubic_CatmullRom, "BiCubic_CatmullRom"); + kernel.Add((int64)Lanczos2, "Lanczos2"); kernel.Add((int64)Lanczos3, "Lanczos3"); kernel.Add((int64)Lanczos4, "Lanczos4"); @@ -182,12 +276,14 @@ struct MyApp : TopWindow { method.Add(DOWNSCALE_WIDE, "Wide"); method <<= THISBACK(Sync); Add(method.TopPos(0, STDSIZE).RightPos(200, 200)); - method <<= DOWNSCALE_SIMPLE; + method <<= DOWNSCALE_WIDE; - Add(fast.TopPos(0, STDSIZE).RightPos(400, 200)); - fast <<= THISBACK(Sync); - fast.SetLabel("Integer"); - fast <<= true; + fn.Add(0, "FP"); + fn.Add(1, "Integer"); + fn.Add(2, "Optimized"); + Add(fn.TopPos(0, STDSIZE).RightPos(400, 200)); + fn <<= THISBACK(Sync); + fn <<= 2; Maximize(); } @@ -195,18 +291,21 @@ struct MyApp : TopWindow { GUI_APP_MAIN { + Image img = TestImg::img1(); + RescaleWithKernel2(img, 5, 5, LinearKernel, 1); + DUMP((int)Saturate255(5000)); DUMP((int)Saturate255(500)); DUMP((int)Saturate255(-1)); DUMP((int)Saturate255(-10000)); for(double x = -2; x <= 2; x += 0.1) - LOG(x << ' ' << Lanczos2(x) << ' ' << MagicKernel(x) << ' ' << LinearKernel(x)); + RLOG(x << ' ' << Lanczos2(x) << ' ' << MagicKernel(x) << ' ' << BiCubic2(x)); -// MyApp().Run(); + MyApp().Run(); #ifndef _DEBUG - Image img = TestImg::img1(); +#if 1 for(int i = 0; i < 100; i++) { - { +/* { RTIMING("Rescale"); Rescale(img, 84, 84); } @@ -214,6 +313,18 @@ GUI_APP_MAIN RTIMING("Rescale Linear integer"); RescaleWithKernel(img, 84, 84, LinearKernel, 1); } + { + RTIMING("Rescale Linear integer optimized"); + RescaleWithKernel2(img, 84, 84, LinearKernel, 1); + } +*/ { + RTIMING("Rescale UP"); + Rescale(img, 300, 300); + } + { + RTIMING("Rescale Linear integer optimized UP"); + RescaleWithKernel2(img, 300, 300, LinearKernel, 1); + } #if 0 { RTIMING("Rescale Linear double"); @@ -246,4 +357,5 @@ GUI_APP_MAIN #endif } #endif +#endif }