diff --git a/modules/fuzzy/include/opencv2/fuzzy/fuzzy_F0_math.hpp b/modules/fuzzy/include/opencv2/fuzzy/fuzzy_F0_math.hpp index 5b241575af..f0a07e0ca6 100644 --- a/modules/fuzzy/include/opencv2/fuzzy/fuzzy_F0_math.hpp +++ b/modules/fuzzy/include/opencv2/fuzzy/fuzzy_F0_math.hpp @@ -121,6 +121,24 @@ namespace ft */ CV_EXPORTS_W int FT02D_iteration(InputArray matrix, InputArray kernel, OutputArray output, InputArray mask, OutputArray maskOutput, bool firstStop); + /** @brief Sligtly less accurate version of F0-transfrom computation optimized for higher speed. The methods counts with linear basic function. + @param matrix Input 3 channels matrix. + @param radius Radius of the **LINEAR** basic function. + @param output Output array. + + This function computes F-transfrom and inverse F-transfotm using linear basic function in one step. It is ~10 times faster than **FT02D_process** method. + */ + CV_EXPORTS_W void FT02D_FL_process(InputArray matrix, const int radius, OutputArray output); + + /** @brief Sligtly less accurate version of F0-transfrom computation optimized for higher speed. The methods counts with linear basic function. + @param matrix Input 3 channels matrix. + @param radius Radius of the **LINEAR** basic function. + @param output Output array. + + This function computes F-transfrom and inverse F-transfotm using linear basic function in one step. It is ~9 times faster then **FT02D_process** method and more accurate than **FT02D_FL_process** method. + */ + CV_EXPORTS_W void FT02D_FL_process_float(InputArray matrix, const int radius, OutputArray output); + //! @} } } diff --git a/modules/fuzzy/include/opencv2/fuzzy/fuzzy_image.hpp b/modules/fuzzy/include/opencv2/fuzzy/fuzzy_image.hpp index e5287a9fc2..a2779d61f0 100644 --- a/modules/fuzzy/include/opencv2/fuzzy/fuzzy_image.hpp +++ b/modules/fuzzy/include/opencv2/fuzzy/fuzzy_image.hpp @@ -56,7 +56,7 @@ namespace ft /** @brief Creates kernel from basic functions. @param A Basic function used in axis **x**. @param B Basic function used in axis **y**. - @param kernel Final 32-b kernel derived from **A** and **B**. + @param kernel Final 32-bit kernel derived from **A** and **B**. @param chn Number of kernel channels. The function creates kernel usable for latter fuzzy image processing. @@ -67,7 +67,7 @@ namespace ft @param function Function type could be one of the following: - **LINEAR** Linear basic function. @param radius Radius of the basic function. - @param kernel Final 32-b kernel. + @param kernel Final 32-bit kernel. @param chn Number of kernel channels. The function creates kernel from predefined functions. diff --git a/modules/fuzzy/samples/fuzzy_inpainting.cpp b/modules/fuzzy/samples/fuzzy_inpainting.cpp index 1ee4fb3456..ec4d1f651b 100644 --- a/modules/fuzzy/samples/fuzzy_inpainting.cpp +++ b/modules/fuzzy/samples/fuzzy_inpainting.cpp @@ -38,9 +38,9 @@ int main(void) Mat I = imread("input.png"); // Various masks - Mat mask1 = imread("mask1.png"); - Mat mask2 = imread("mask2.png"); - Mat mask3 = imread("mask3.png"); + Mat mask1 = imread("mask1.png", IMREAD_GRAYSCALE); + Mat mask2 = imread("mask2.png", IMREAD_GRAYSCALE); + Mat mask3 = imread("mask3.png", IMREAD_GRAYSCALE); // Apply the damage Mat input1, input2, input3; diff --git a/modules/fuzzy/src/fuzzy_F0_math.cpp b/modules/fuzzy/src/fuzzy_F0_math.cpp index 892bcbe5c5..09fba00739 100644 --- a/modules/fuzzy/src/fuzzy_F0_math.cpp +++ b/modules/fuzzy/src/fuzzy_F0_math.cpp @@ -43,6 +43,262 @@ using namespace cv; +void ft::FT02D_FL_process(InputArray matrix, const int radius, OutputArray output) +{ + CV_Assert(matrix.channels() == 3); + + int borderPadding = 2 * radius + 1; + Mat imagePadded; + + copyMakeBorder(matrix, imagePadded, radius, borderPadding, radius, borderPadding, BORDER_CONSTANT, Scalar(0)); + + Mat channel[3]; + split(imagePadded, channel); + + uchar *im_r = channel[2].data; + uchar *im_g = channel[1].data; + uchar *im_b = channel[0].data; + + int width = imagePadded.cols; + int height = imagePadded.rows; + int n_width = width / radius + 1; + int n_height = height / radius + 1; + + std::vector c_r(n_width * n_height); + std::vector c_g(n_width * n_height); + std::vector c_b(n_width * n_height); + + int sum_r, sum_g, sum_b, num, c_wei; + int c_pos, pos, pos2, wy; + int cy = 0; + float num_f; + + std::vector wei(radius + 1); + + for (int i = 0; i <= radius; i++) + { + wei[i] = radius - i; + } + + for (int y = radius; y < height - radius; y += radius) + { + c_pos = cy; + + for (int x = radius; x < width - radius; x += radius) + { + num = sum_r = sum_g = sum_b = 0; + + for (int y1 = y - radius; y1 <= y + radius; y1++) + { + pos = y1 * width; + wy = wei[abs(y1 - y)]; + + for (int x1 = x - radius; x1 <= x + radius; x1++) + { + c_wei = wei[abs(x1 - x)] * wy; + pos2 = pos + x1; + sum_r += im_r[pos2] * c_wei; + sum_g += im_g[pos2] * c_wei; + sum_b += im_b[pos2] * c_wei; + num += c_wei; + } + } + + num_f = 1.0f / (float)num; + + c_r[c_pos] = (uchar)cvRound(sum_r * num_f); + c_g[c_pos] = (uchar)cvRound(sum_g * num_f); + c_b[c_pos] = (uchar)cvRound(sum_b * num_f); + + c_pos++; + } + + cy += n_width; + } + + int p1, p2, p3, p4, yw, w1, w2, w3, w4, lx, ly, lx1, ly1, pos_iFT; + float num_iFT; + + int output_height = matrix.rows(); + int output_width = matrix.cols(); + + uchar *img_r = new uchar[output_height * output_width]; + uchar *img_g = new uchar[output_height * output_width]; + uchar *img_b = new uchar[output_height * output_width]; + + for (int y = 0; y < output_height; y++) + { + ly1 = (y % radius); + ly = radius - ly1; + yw = y / radius * n_width; + pos_iFT = y * output_width; + + for (int x = 0; x < output_width; x++) + { + lx1 = (x % radius); + lx = radius - lx1; + + p1 = x / radius + yw; + p2 = p1 + 1; + p3 = p1 + n_width; + p4 = p3 + 1; + + w1 = lx * ly; + w2 = lx1 * ly; + w3 = lx * ly1; + w4 = lx1 * ly1; + + num_iFT = 1.0f / (float)(w1 + w2 + w3 + w4); + + img_r[pos_iFT] = (uchar)((c_r[p1] * w1 + c_r[p2] * w2 + c_r[p3] * w3 + c_r[p4] * w4) * num_iFT); + img_g[pos_iFT] = (uchar)((c_g[p1] * w1 + c_g[p2] * w2 + c_g[p3] * w3 + c_g[p4] * w4) * num_iFT); + img_b[pos_iFT] = (uchar)((c_b[p1] * w1 + c_b[p2] * w2 + c_b[p3] * w3 + c_b[p4] * w4) * num_iFT); + + pos_iFT++; + } + } + + Mat compR(output_height, output_width, CV_8UC1, img_r); + Mat compG(output_height, output_width, CV_8UC1, img_g); + Mat compB(output_height, output_width, CV_8UC1, img_b); + + std::vector oComp; + + oComp.push_back(compB); + oComp.push_back(compG); + oComp.push_back(compR); + + merge(oComp, output); +} + +void ft::FT02D_FL_process_float(InputArray matrix, const int radius, OutputArray output) +{ + CV_Assert(matrix.channels() == 3); + + int borderPadding = 2 * radius + 1; + Mat imagePadded; + + copyMakeBorder(matrix, imagePadded, radius, borderPadding, radius, borderPadding, BORDER_CONSTANT, Scalar(0)); + + Mat channel[3]; + split(imagePadded, channel); + + uchar *im_r = channel[2].data; + uchar *im_g = channel[1].data; + uchar *im_b = channel[0].data; + + int width = imagePadded.cols; + int height = imagePadded.rows; + int n_width = width / radius + 1; + int n_height = height / radius + 1; + + std::vector c_r(n_width * n_height); + std::vector c_g(n_width * n_height); + std::vector c_b(n_width * n_height); + + int sum_r, sum_g, sum_b, num, c_wei; + int c_pos, pos, pos2, wy; + int cy = 0; + float num_f; + + std::vector wei(radius + 1); + + for (int i = 0; i <= radius; i++) + { + wei[i] = radius - i; + } + + for (int y = radius; y < height - radius; y += radius) + { + c_pos = cy; + + for (int x = radius; x < width - radius; x += radius) + { + num = sum_r = sum_g = sum_b = 0; + + for (int y1 = y - radius; y1 <= y + radius; y1++) + { + pos = y1 * width; + wy = wei[abs(y1 - y)]; + + for (int x1 = x - radius; x1 <= x + radius; x1++) + { + c_wei = wei[abs(x1 - x)] * wy; + pos2 = pos + x1; + sum_r += im_r[pos2] * c_wei; + sum_g += im_g[pos2] * c_wei; + sum_b += im_b[pos2] * c_wei; + num += c_wei; + } + } + + num_f = 1.0f / (float)num; + + c_r[c_pos] = sum_r * num_f; + c_g[c_pos] = sum_g * num_f; + c_b[c_pos] = sum_b * num_f; + + c_pos++; + } + + cy += n_width; + } + + int p1, p2, p3, p4, yw, w1, w2, w3, w4, lx, ly, lx1, ly1, pos_iFT; + float num_iFT; + + int output_height = matrix.rows(); + int output_width = matrix.cols(); + + float *img_r = new float[output_height * output_width]; + float *img_g = new float[output_height * output_width]; + float *img_b = new float[output_height * output_width]; + + for (int y = 0; y < output_height; y++) + { + ly1 = (y % radius); + ly = radius - ly1; + yw = y / radius * n_width; + pos_iFT = y * output_width; + + for (int x = 0; x < output_width; x++) + { + lx1 = (x % radius); + lx = radius - lx1; + + p1 = x / radius + yw; + p2 = p1 + 1; + p3 = p1 + n_width; + p4 = p3 + 1; + + w1 = lx * ly; + w2 = lx1 * ly; + w3 = lx * ly1; + w4 = lx1 * ly1; + + num_iFT = 1.0f / (float)(w1 + w2 + w3 + w4); + + img_r[pos_iFT] = (c_r[p1] * w1 + c_r[p2] * w2 + c_r[p3] * w3 + c_r[p4] * w4) * num_iFT; + img_g[pos_iFT] = (c_g[p1] * w1 + c_g[p2] * w2 + c_g[p3] * w3 + c_g[p4] * w4) * num_iFT; + img_b[pos_iFT] = (c_b[p1] * w1 + c_b[p2] * w2 + c_b[p3] * w3 + c_b[p4] * w4) * num_iFT; + + pos_iFT++; + } + } + + Mat compR(output_height, output_width, CV_32FC1, img_r); + Mat compG(output_height, output_width, CV_32FC1, img_g); + Mat compB(output_height, output_width, CV_32FC1, img_b); + + std::vector oComp; + + oComp.push_back(compB); + oComp.push_back(compG); + oComp.push_back(compR); + + merge(oComp, output); +} + void ft::FT02D_components(InputArray matrix, InputArray kernel, OutputArray components, InputArray mask) { CV_Assert(matrix.channels() == kernel.channels() && mask.channels() == 1); diff --git a/modules/fuzzy/test/test_f0.cpp b/modules/fuzzy/test/test_f0.cpp index e9ff61ba03..e4c5bdfcf4 100644 --- a/modules/fuzzy/test/test_f0.cpp +++ b/modules/fuzzy/test/test_f0.cpp @@ -161,4 +161,95 @@ TEST(fuzzy_f0, inversion) double n1 = cvtest::norm(demandedO, O, NORM_INF); EXPECT_DOUBLE_EQ(n1, 0); +} + +TEST(fuzzy_f0, process) +{ + float arI[16][16] = + { + { 0, 0, 0, 10, 34, 57, 80, 104, 127, 150, 174, 197, 221, 244, 255, 255 }, + { 0, 0, 0, 10, 34, 57, 80, 104, 127, 150, 174, 197, 221, 244, 255, 255 }, + { 0, 0, 0, 10, 34, 57, 80, 104, 127, 150, 174, 197, 221, 244, 255, 255 }, + { 0, 0, 0, 10, 34, 57, 80, 104, 127, 150, 174, 197, 221, 244, 255, 255 }, + { 0, 0, 0, 10, 34, 57, 80, 104, 127, 150, 174, 197, 221, 244, 255, 255 }, + { 0, 0, 0, 10, 34, 57, 80, 104, 127, 150, 174, 197, 221, 244, 255, 255 }, + { 0, 0, 0, 10, 34, 57, 80, 104, 127, 150, 174, 197, 221, 244, 255, 255 }, + { 0, 0, 0, 10, 34, 57, 80, 104, 127, 150, 174, 197, 221, 244, 255, 255 }, + { 0, 0, 0, 10, 34, 57, 80, 104, 127, 150, 174, 197, 221, 244, 255, 255 }, + { 0, 0, 0, 10, 34, 57, 80, 104, 127, 150, 174, 197, 221, 244, 255, 255 }, + { 0, 0, 0, 10, 34, 57, 80, 104, 127, 150, 174, 197, 221, 244, 255, 255 }, + { 0, 0, 0, 10, 34, 57, 80, 104, 127, 150, 174, 197, 221, 244, 255, 255 }, + { 0, 0, 0, 10, 34, 57, 80, 104, 127, 150, 174, 197, 221, 244, 255, 255 }, + { 0, 0, 0, 10, 34, 57, 80, 104, 127, 150, 174, 197, 221, 244, 255, 255 }, + { 0, 0, 0, 10, 34, 57, 80, 104, 127, 150, 174, 197, 221, 244, 255, 255 }, + { 0, 0, 0, 10, 34, 57, 80, 104, 127, 150, 174, 197, 221, 244, 255, 255 } + }; + Mat I = Mat(16, 16, CV_32F, arI); + + float arDemandedO[16][16] = + { + { 0, 1.25, 2.5, 18.125, 33.75, 57, 80.25, 103.625, 127, 150.375, 173.75, 197.25, 220.75, 236.5, 252.25, 253.625 }, + { 0, 1.25, 2.5, 18.125, 33.75, 57, 80.25, 103.625, 127, 150.375, 173.75, 197.25, 220.75, 236.5, 252.25, 253.625 }, + { 0, 1.25, 2.5, 18.125, 33.75, 57, 80.25, 103.625, 127, 150.375, 173.75, 197.25, 220.75, 236.5, 252.25, 253.625 }, + { 0, 1.25, 2.5, 18.125, 33.75, 57, 80.25, 103.625, 127, 150.375, 173.75, 197.25, 220.75, 236.5, 252.25, 253.625 }, + { 0, 1.25, 2.5, 18.125, 33.75, 57, 80.25, 103.625, 127, 150.375, 173.75, 197.25, 220.75, 236.5, 252.25, 253.625 }, + { 0, 1.25, 2.5, 18.125, 33.75, 57, 80.25, 103.625, 127, 150.375, 173.75, 197.25, 220.75, 236.5, 252.25, 253.625 }, + { 0, 1.25, 2.5, 18.125, 33.75, 57, 80.25, 103.625, 127, 150.375, 173.75, 197.25, 220.75, 236.5, 252.25, 253.625 }, + { 0, 1.25, 2.5, 18.125, 33.75, 57, 80.25, 103.625, 127, 150.375, 173.75, 197.25, 220.75, 236.5, 252.25, 253.625 }, + { 0, 1.25, 2.5, 18.125, 33.75, 57, 80.25, 103.625, 127, 150.375, 173.75, 197.25, 220.75, 236.5, 252.25, 253.625 }, + { 0, 1.25, 2.5, 18.125, 33.75, 57, 80.25, 103.625, 127, 150.375, 173.75, 197.25, 220.75, 236.5, 252.25, 253.625 }, + { 0, 1.25, 2.5, 18.125, 33.75, 57, 80.25, 103.625, 127, 150.375, 173.75, 197.25, 220.75, 236.5, 252.25, 253.625 }, + { 0, 1.25, 2.5, 18.125, 33.75, 57, 80.25, 103.625, 127, 150.375, 173.75, 197.25, 220.75, 236.5, 252.25, 253.625 }, + { 0, 1.25, 2.5, 18.125, 33.75, 57, 80.25, 103.625, 127, 150.375, 173.75, 197.25, 220.75, 236.5, 252.25, 253.625 }, + { 0, 1.25, 2.5, 18.125, 33.75, 57, 80.25, 103.625, 127, 150.375, 173.75, 197.25, 220.75, 236.5, 252.25, 253.625 }, + { 0, 1.25, 2.5, 18.125, 33.75, 57, 80.25, 103.625, 127, 150.375, 173.75, 197.25, 220.75, 236.5, 252.25, 253.625 }, + { 0, 1.25, 2.5, 18.125, 33.75, 57, 80.25, 103.625, 127, 150.375, 173.75, 197.25, 220.75, 236.5, 252.25, 253.625 } + }; + Mat demandedO = Mat(16, 16, CV_32F, arDemandedO); + + Mat kernel; + ft::createKernel(ft::LINEAR, 2, kernel, 1); + + Mat O; + ft::FT02D_process(I, kernel, O); + + double n1 = cvtest::norm(demandedO, O, NORM_INF); + + EXPECT_DOUBLE_EQ(n1, 0); +} + +TEST(fuzzy_f0, FL_process) +{ + string folder = string(cvtest::TS::ptr()->get_data_path()) + "fuzzy/"; + Mat orig = imread(folder + "orig.png"); + Mat exp5 = imread(folder + "exp5.png"); + + EXPECT_TRUE(!orig.empty() && !exp5.empty()); + + Mat res5; + ft::FT02D_FL_process(orig, 5, res5); + + res5.convertTo(res5, CV_8UC3); + + double n1 = cvtest::norm(exp5, res5, NORM_INF); + + EXPECT_LE(n1, 1); +} + +TEST(fuzzy_f0, FL_process_float) +{ + string folder = string(cvtest::TS::ptr()->get_data_path()) + "fuzzy/"; + Mat orig = imread(folder + "orig.png"); + Mat exp6 = imread(folder + "exp6.png"); + + EXPECT_TRUE(!orig.empty() && !exp6.empty()); + + Mat res6; + ft::FT02D_FL_process_float(orig, 5, res6); + + res6.convertTo(res6, CV_8UC3); + + double n1 = cvtest::norm(exp6, res6, NORM_INF); + + EXPECT_LE(n1, 1); } \ No newline at end of file