| @@ -29,15 +29,15 @@ Image Image::Convolution(Array2D<float> const &kernel) | |||
| { | |||
| /* Find the cell with the largest value */ | |||
| ivec2 ksize = kernel.GetSize(); | |||
| int besti = -1, bestj = -1; | |||
| int bestx = -1, besty = -1; | |||
| float tmp = 0.f; | |||
| for (int j = 0; j < ksize.y; ++j) | |||
| for (int i = 0; i < ksize.x; ++i) | |||
| if (lol::sq(kernel[i][j] > tmp)) | |||
| for (int dy = 0; dy < ksize.y; ++dy) | |||
| for (int dx = 0; dx < ksize.x; ++dx) | |||
| if (lol::sq(kernel[dx][dy] > tmp)) | |||
| { | |||
| tmp = sq(kernel[i][j]); | |||
| besti = i; | |||
| bestj = j; | |||
| tmp = sq(kernel[dx][dy]); | |||
| bestx = dx; | |||
| besty = dy; | |||
| } | |||
| /* If the kernel is empty, return a copy of the picture */ | |||
| @@ -46,18 +46,18 @@ Image Image::Convolution(Array2D<float> const &kernel) | |||
| /* Check whether the matrix rank is 1 */ | |||
| bool separable = true; | |||
| for (int j = 0; j < ksize.y && separable; ++j) | |||
| for (int dy = 0; dy < ksize.y && separable; ++dy) | |||
| { | |||
| if (j == bestj) | |||
| if (dy == besty) | |||
| continue; | |||
| for (int i = 0; i < ksize.x && separable; ++i) | |||
| for (int dx = 0; dx < ksize.x && separable; ++dx) | |||
| { | |||
| if (i == besti) | |||
| if (dx == bestx) | |||
| continue; | |||
| float p = kernel[i][j] * kernel[besti][bestj]; | |||
| float q = kernel[i][bestj] * kernel[besti][j]; | |||
| float p = kernel[dx][dy] * kernel[bestx][besty]; | |||
| float q = kernel[dx][besty] * kernel[bestx][dy]; | |||
| if (lol::abs(p - q) > 1.0e-8f) | |||
| separable = false; | |||
| @@ -69,11 +69,11 @@ Image Image::Convolution(Array2D<float> const &kernel) | |||
| /* Matrix rank is 1! Separate the filter. */ | |||
| Array<float> hvec, vvec; | |||
| float norm = 1.0f / lol::sqrt(lol::abs(kernel[besti][bestj])); | |||
| for (int i = 0; i < ksize.x; i++) | |||
| hvec << norm * kernel[i][bestj]; | |||
| for (int j = 0; j < ksize.y; j++) | |||
| vvec << norm * kernel[besti][j]; | |||
| float norm = 1.0f / lol::sqrt(lol::abs(kernel[bestx][besty])); | |||
| for (int dx = 0; dx < ksize.x; dx++) | |||
| hvec << norm * kernel[dx][besty]; | |||
| for (int dy = 0; dy < ksize.y; dy++) | |||
| vvec << norm * kernel[bestx][dy]; | |||
| return SepConv(*this, hvec, vvec); | |||
| } | |||
| @@ -83,220 +83,202 @@ Image Image::Convolution(Array2D<float> const &kernel) | |||
| } | |||
| } | |||
| template<PixelFormat FORMAT, int WRAP_X, int WRAP_Y> | |||
| static Image NonSepConv(Image &src, Array2D<float> const &kernel) | |||
| { | |||
| typedef typename PixelType<FORMAT>::type pixel_t; | |||
| ivec2 const size = src.GetSize(); | |||
| ivec2 const ksize = kernel.GetSize(); | |||
| Image dst(size); | |||
| bool const wrap_x = src.GetWrapX() == WrapMode::Repeat; | |||
| bool const wrap_y = src.GetWrapY() == WrapMode::Repeat; | |||
| pixel_t const *srcp = src.Lock<FORMAT>(); | |||
| pixel_t *dstp = dst.Lock<FORMAT>(); | |||
| if (src.GetFormat() == PixelFormat::Y_8 | |||
| || src.GetFormat() == PixelFormat::Y_F32) | |||
| for (int y = 0; y < size.y; y++) | |||
| { | |||
| float const *srcp = src.Lock<PixelFormat::Y_F32>(); | |||
| float *dstp = dst.Lock<PixelFormat::Y_F32>(); | |||
| for (int y = 0; y < size.y; y++) | |||
| for (int x = 0; x < size.x; x++) | |||
| { | |||
| for (int x = 0; x < size.x; x++) | |||
| pixel_t pixel(0.f); | |||
| for (int dy = 0; dy < ksize.y; dy++) | |||
| { | |||
| float pixel = 0.f; | |||
| int y2 = y + dy - ksize.y / 2; | |||
| if (y2 < 0) | |||
| y2 = WRAP_Y ? size.y - 1 - ((-y2 - 1) % size.y) : 0; | |||
| else if (y2 >= size.y) | |||
| y2 = WRAP_Y ? y2 % size.y : size.y - 1; | |||
| for (int j = 0; j < ksize.y; j++) | |||
| for (int dx = 0; dx < ksize.x; dx++) | |||
| { | |||
| int y2 = y + j - ksize.y / 2; | |||
| if (y2 < 0) | |||
| y2 = wrap_y ? size.y - 1 - ((-y2 - 1) % size.y) : 0; | |||
| else if (y2 >= size.y) | |||
| y2 = wrap_y ? y2 % size.y : size.y - 1; | |||
| for (int i = 0; i < ksize.x; i++) | |||
| { | |||
| float f = kernel[i][j]; | |||
| int x2 = x + i - ksize.x / 2; | |||
| if (x2 < 0) | |||
| x2 = wrap_x ? size.x - 1 - ((-x2 - 1) % size.x) : 0; | |||
| else if (x2 >= size.x) | |||
| x2 = wrap_x ? x2 % size.x : size.x - 1; | |||
| pixel += f * srcp[y2 * size.x + x2]; | |||
| } | |||
| } | |||
| dstp[y * size.x + x] = lol::clamp(pixel, 0.0f, 1.0f); | |||
| } | |||
| } | |||
| src.Unlock(srcp); | |||
| dst.Unlock(dstp); | |||
| } | |||
| else | |||
| { | |||
| vec4 const *srcp = src.Lock<PixelFormat::RGBA_F32>(); | |||
| vec4 *dstp = dst.Lock<PixelFormat::RGBA_F32>(); | |||
| float f = kernel[dx][dy]; | |||
| for (int y = 0; y < size.y; y++) | |||
| { | |||
| for (int x = 0; x < size.x; x++) | |||
| { | |||
| vec4 pixel(0.f); | |||
| int x2 = x + dx - ksize.x / 2; | |||
| if (x2 < 0) | |||
| x2 = WRAP_X ? size.x - 1 - ((-x2 - 1) % size.x) : 0; | |||
| else if (x2 >= size.x) | |||
| x2 = WRAP_X ? x2 % size.x : size.x - 1; | |||
| for (int j = 0; j < ksize.y; j++) | |||
| { | |||
| int y2 = y + j - ksize.y / 2; | |||
| if (y2 < 0) | |||
| y2 = wrap_y ? size.y - 1 - ((-y2 - 1) % size.y) : 0; | |||
| else if (y2 >= size.y) | |||
| y2 = wrap_y ? y2 % size.y : size.y - 1; | |||
| for (int i = 0; i < ksize.x; i++) | |||
| { | |||
| float f = kernel[i][j]; | |||
| int x2 = x + i - ksize.x / 2; | |||
| if (x2 < 0) | |||
| x2 = wrap_x ? size.x - 1 - ((-x2 - 1) % size.x) : 0; | |||
| else if (x2 >= size.x) | |||
| x2 = wrap_x ? x2 % size.x : size.x - 1; | |||
| pixel += f * srcp[y2 * size.x + x2]; | |||
| } | |||
| pixel += f * srcp[y2 * size.x + x2]; | |||
| } | |||
| dstp[y * size.x + x] = lol::clamp(pixel, 0.0f, 1.0f); | |||
| } | |||
| } | |||
| src.Unlock(srcp); | |||
| dst.Unlock(dstp); | |||
| dstp[y * size.x + x] = lol::clamp(pixel, 0.0f, 1.0f); | |||
| } | |||
| } | |||
| src.Unlock(srcp); | |||
| dst.Unlock(dstp); | |||
| return dst; | |||
| } | |||
| static Image SepConv(Image &src, Array<float> const &hvec, | |||
| Array<float> const &vvec) | |||
| static Image NonSepConv(Image &src, Array2D<float> const &kernel) | |||
| { | |||
| ivec2 const size = src.GetSize(); | |||
| ivec2 const ksize = ivec2(hvec.Count(), vvec.Count()); | |||
| Image dst(size); | |||
| bool const wrap_x = src.GetWrapX() == WrapMode::Repeat; | |||
| bool const wrap_y = src.GetWrapY() == WrapMode::Repeat; | |||
| if (src.GetFormat() == PixelFormat::Y_8 | |||
| || src.GetFormat() == PixelFormat::Y_F32) | |||
| { | |||
| float const *srcp = src.Lock<PixelFormat::Y_F32>(); | |||
| float *dstp = dst.Lock<PixelFormat::Y_F32>(); | |||
| /* TODO: compare performances with Array2D here */ | |||
| float *tmp = new float[size.x * size.y]; | |||
| for (int y = 0; y < size.y; y++) | |||
| if (wrap_x) | |||
| { | |||
| for (int x = 0; x < size.x; x++) | |||
| { | |||
| float pixel = 0.f; | |||
| if (wrap_y) | |||
| return NonSepConv<PixelFormat::Y_F32, 1, 1>(src, kernel); | |||
| else | |||
| return NonSepConv<PixelFormat::Y_F32, 1, 0>(src, kernel); | |||
| } | |||
| else | |||
| { | |||
| if (wrap_y) | |||
| return NonSepConv<PixelFormat::Y_F32, 0, 1>(src, kernel); | |||
| else | |||
| return NonSepConv<PixelFormat::Y_F32, 0, 0>(src, kernel); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| if (wrap_x) | |||
| { | |||
| if (wrap_y) | |||
| return NonSepConv<PixelFormat::RGBA_F32, 1, 1>(src, kernel); | |||
| else | |||
| return NonSepConv<PixelFormat::RGBA_F32, 1, 0>(src, kernel); | |||
| } | |||
| else | |||
| { | |||
| if (wrap_y) | |||
| return NonSepConv<PixelFormat::RGBA_F32, 0, 1>(src, kernel); | |||
| else | |||
| return NonSepConv<PixelFormat::RGBA_F32, 0, 0>(src, kernel); | |||
| } | |||
| } | |||
| } | |||
| for (int i = 0; i < ksize.x; i++) | |||
| { | |||
| float f = hvec[i]; | |||
| template<PixelFormat FORMAT, int WRAP_X, int WRAP_Y> | |||
| static Image NonSepConv(Image &src, Array<float> const &hvec, | |||
| Array<float> const &vvec) | |||
| { | |||
| typedef typename PixelType<FORMAT>::type pixel_t; | |||
| int x2 = x + i - ksize.x / 2; | |||
| if (x2 < 0) | |||
| x2 = wrap_x ? size.x - 1 - ((-x2 - 1) % size.x) : 0; | |||
| else if (x2 >= size.x) | |||
| x2 = wrap_x ? x2 % size.x : size.x - 1; | |||
| ivec2 const size = src.GetSize(); | |||
| ivec2 const ksize(hvec.Count(), vvec.Count()); | |||
| Image dst(size); | |||
| pixel += f * srcp[y * size.x + x2]; | |||
| } | |||
| pixel_t const *srcp = src.Lock<FORMAT>(); | |||
| pixel_t *dstp = dst.Lock<FORMAT>(); | |||
| tmp[y * size.x + x] = pixel; | |||
| } | |||
| } | |||
| Array2D<pixel_t> tmp(size); | |||
| for (int y = 0; y < size.y; y++) | |||
| for (int y = 0; y < size.y; y++) | |||
| { | |||
| for (int x = 0; x < size.x; x++) | |||
| { | |||
| for (int x = 0; x < size.x; x++) | |||
| { | |||
| float pixel = 0.f; | |||
| for (int j = 0; j < ksize.y; j++) | |||
| { | |||
| double f = vvec[j]; | |||
| int y2 = y + j - ksize.y / 2; | |||
| if (y2 < 0) | |||
| y2 = wrap_y ? size.y - 1 - ((-y2 - 1) % size.y) : 0; | |||
| else if (y2 >= size.y) | |||
| y2 = wrap_y ? y2 % size.y : size.y - 1; | |||
| pixel_t pixel(0.f); | |||
| pixel += f * tmp[y2 * size.x + x]; | |||
| } | |||
| for (int dx = 0; dx < ksize.x; dx++) | |||
| { | |||
| int x2 = x + dx - ksize.x / 2; | |||
| if (x2 < 0) | |||
| x2 = WRAP_X ? size.x - 1 - ((-x2 - 1) % size.x) : 0; | |||
| else if (x2 >= size.x) | |||
| x2 = WRAP_X ? x2 % size.x : size.x - 1; | |||
| dstp[y * size.x + x] = lol::clamp(pixel, 0.0f, 1.0f); | |||
| pixel += hvec[dx] * srcp[y * size.x + x2]; | |||
| } | |||
| } | |||
| src.Unlock(srcp); | |||
| dst.Unlock(dstp); | |||
| tmp[x][y] = pixel; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| vec4 const *srcp = src.Lock<PixelFormat::RGBA_F32>(); | |||
| vec4 *dstp = dst.Lock<PixelFormat::RGBA_F32>(); | |||
| /* TODO: compare performances with Array2D here */ | |||
| vec4 *tmp = new vec4[size.x * size.y]; | |||
| for (int y = 0; y < size.y; y++) | |||
| for (int y = 0; y < size.y; y++) | |||
| { | |||
| for (int x = 0; x < size.x; x++) | |||
| { | |||
| for (int x = 0; x < size.x; x++) | |||
| { | |||
| vec4 pixel(0.f); | |||
| for (int i = 0; i < ksize.x; i++) | |||
| { | |||
| int x2 = x + i - ksize.x / 2; | |||
| if (x2 < 0) | |||
| x2 = wrap_x ? size.x - 1 - ((-x2 - 1) % size.x) : 0; | |||
| else if (x2 >= size.x) | |||
| x2 = wrap_x ? x2 % size.x : size.x - 1; | |||
| pixel_t pixel(0.f); | |||
| pixel += hvec[i] * srcp[y * size.x + x2]; | |||
| } | |||
| for (int j = 0; j < ksize.y; j++) | |||
| { | |||
| int y2 = y + j - ksize.y / 2; | |||
| if (y2 < 0) | |||
| y2 = WRAP_Y ? size.y - 1 - ((-y2 - 1) % size.y) : 0; | |||
| else if (y2 >= size.y) | |||
| y2 = WRAP_Y ? y2 % size.y : size.y - 1; | |||
| tmp[y * size.x + x] = pixel; | |||
| pixel += vvec[j] * tmp[x][y2]; | |||
| } | |||
| dstp[y * size.x + x] = lol::clamp(pixel, 0.0f, 1.0f); | |||
| } | |||
| } | |||
| for (int y = 0; y < size.y; y++) | |||
| { | |||
| for (int x = 0; x < size.x; x++) | |||
| { | |||
| vec4 pixel(0.f); | |||
| src.Unlock(srcp); | |||
| dst.Unlock(dstp); | |||
| for (int j = 0; j < ksize.y; j++) | |||
| { | |||
| int y2 = y + j - ksize.y / 2; | |||
| if (y2 < 0) | |||
| y2 = wrap_y ? size.y - 1 - ((-y2 - 1) % size.y) : 0; | |||
| else if (y2 >= size.y) | |||
| y2 = wrap_y ? y2 % size.y : size.y - 1; | |||
| return dst; | |||
| } | |||
| pixel += vvec[j] * tmp[y2 * size.x + x]; | |||
| } | |||
| static Image SepConv(Image &src, Array<float> const &hvec, | |||
| Array<float> const &vvec) | |||
| { | |||
| bool const wrap_x = src.GetWrapX() == WrapMode::Repeat; | |||
| bool const wrap_y = src.GetWrapY() == WrapMode::Repeat; | |||
| dstp[y * size.x + x] = lol::clamp(pixel, 0.0f, 1.0f); | |||
| } | |||
| if (src.GetFormat() == PixelFormat::Y_8 | |||
| || src.GetFormat() == PixelFormat::Y_F32) | |||
| { | |||
| if (wrap_x) | |||
| { | |||
| if (wrap_y) | |||
| return NonSepConv<PixelFormat::Y_F32, 1, 1>(src, hvec, vvec); | |||
| else | |||
| return NonSepConv<PixelFormat::Y_F32, 1, 0>(src, hvec, vvec); | |||
| } | |||
| else | |||
| { | |||
| if (wrap_y) | |||
| return NonSepConv<PixelFormat::Y_F32, 0, 1>(src, hvec, vvec); | |||
| else | |||
| return NonSepConv<PixelFormat::Y_F32, 0, 0>(src, hvec, vvec); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| if (wrap_x) | |||
| { | |||
| if (wrap_y) | |||
| return NonSepConv<PixelFormat::RGBA_F32, 1, 1>(src, hvec, vvec); | |||
| else | |||
| return NonSepConv<PixelFormat::RGBA_F32, 1, 0>(src, hvec, vvec); | |||
| } | |||
| else | |||
| { | |||
| if (wrap_y) | |||
| return NonSepConv<PixelFormat::RGBA_F32, 0, 1>(src, hvec, vvec); | |||
| else | |||
| return NonSepConv<PixelFormat::RGBA_F32, 0, 0>(src, hvec, vvec); | |||
| } | |||
| src.Unlock(srcp); | |||
| dst.Unlock(dstp); | |||
| } | |||
| return dst; | |||
| } | |||
| } /* namespace lol */ | |||