diff --git a/src/Core/Geometry/Image.cpp b/src/Core/Geometry/Image.cpp index 05cf0239ad2..1a6ba341181 100644 --- a/src/Core/Geometry/Image.cpp +++ b/src/Core/Geometry/Image.cpp @@ -82,17 +82,22 @@ std::shared_ptr ConvertDepthToFloatImage(const Image &depth, return std::make_shared(); } auto output = CreateFloatImageFromImage(depth); - LinearTransformImage(*output, 1 / depth_scale, 0.0, 0.0, depth_trunc); + for (int y = 0; y < output->height_; y++) { + for (int x = 0; x < output->width_; x++) { + float *p = PointerAt(*output, x, y); + *p /= (float)depth_scale; + if (*p >= depth_trunc) + *p = 0.0f; + } + } return output; } -void LinearTransformImage(Image &input, double scale, - double offset, double min, double max) +void ClipIntensityImage(Image &input, double min/* = 0.0*/, double max/* = 1.0*/) { for (int y = 0; y < input.height_; y++) { for (int x = 0; x < input.width_; x++) { float *p = PointerAt(input, x, y); - (*p) = (float)(scale * (*p) + offset); if (*p > max) *p = (float)max; if (*p < min) @@ -101,15 +106,25 @@ void LinearTransformImage(Image &input, double scale, } } -std::vector> CreateImagePyramid( - const Image& input, size_t num_of_levels) +void LinearTransformImage(Image &input, double scale, double offset/* = 0.0*/) { - std::vector> pyramidImage; - pyramidImage.clear(); + for (int y = 0; y < input.height_; y++) { + for (int x = 0; x < input.width_; x++) { + float *p = PointerAt(input, x, y); + (*p) = (float)(scale * (*p) + offset); + } + } +} + +ImagePyramid CreateImagePyramid( + const Image& input, size_t num_of_levels, bool with_gaussian_filter /*= true*/) +{ + std::vector> pyramid_image; + pyramid_image.clear(); if ((input.num_of_channels_ != 1) || (input.bytes_per_channel_ != 4)) { PrintDebug("[CreateImagePyramid] Unsupported image format.\n"); - return pyramidImage; + return pyramid_image; } for (int i = 0; i < num_of_levels; i++) { @@ -117,15 +132,20 @@ std::vector> CreateImagePyramid( std::shared_ptr input_copy_ptr = std::make_shared(); *input_copy_ptr = input; - pyramidImage.push_back(input_copy_ptr); + pyramid_image.push_back(input_copy_ptr); } else { - // https://en.wikipedia.org/wiki/Pyramid_(image_processing) - auto level_b = FilterImage(*pyramidImage[i - 1], FILTER_GAUSSIAN_3); - auto level_bd = DownsampleImage(*level_b); - pyramidImage.push_back(level_bd); + if (with_gaussian_filter) { + // https://en.wikipedia.org/wiki/Pyramid_(image_processing) + auto level_b = FilterImage(*pyramid_image[i - 1], FILTER_GAUSSIAN_3); + auto level_bd = DownsampleImage(*level_b); + pyramid_image.push_back(level_bd); + } else { + auto level_d = DownsampleImage(*pyramid_image[i - 1]); + pyramid_image.push_back(level_d); + } } } - return pyramidImage; + return pyramid_image; } std::shared_ptr DownsampleImage(const Image &input) @@ -183,7 +203,7 @@ std::shared_ptr FilterHorizontalImage( if (x_shift > input.width_ - 1) x_shift = input.width_ - 1; float* pi = PointerAt(input, x_shift, y, 0); - temp += (*pi * kernel[i + half_kernel_size]); + temp += (*pi * (float)kernel[i + half_kernel_size]); } *po = (float)temp; } @@ -223,6 +243,16 @@ std::shared_ptr FilterImage(const Image &input, FilterType type) return output; } +ImagePyramid FilterImagePyramid(const ImagePyramid &input, FilterType type) +{ + std::vector> output; + for (size_t i = 0; i < input.size(); i++) { + auto layer_filtered = FilterImage(*input[i], type); + output.push_back(layer_filtered); + } + return output; +} + std::shared_ptr FilterImage(const Image &input, const std::vector dx, const std::vector dy) { diff --git a/src/Core/Geometry/Image.h b/src/Core/Geometry/Image.h index 809ca021bcc..5fe63a84183 100644 --- a/src/Core/Geometry/Image.h +++ b/src/Core/Geometry/Image.h @@ -152,13 +152,12 @@ std::shared_ptr DownsampleImage(const Image &input); /// Function to linearly transform pixel intensities /// image_new = scale * image + offset -/// min is lower bound of image_new -/// max is upper bound of image_new -void LinearTransformImage(Image &input, double scale, - double offset, double min = 0.0, double max = 1.0); +void LinearTransformImage(Image &input, double scale = 1.0, double offset = 0.0); -std::vector> CreateImagePyramid( - const Image& image, size_t num_of_levels); +/// Function to cilpping pixel intensities +/// min is lower bound +/// max is upper bound +void ClipIntensityImage(Image &input, double min = 0.0, double max = 1.0); /// Function to change data types of image /// crafted for specific usage such as @@ -186,4 +185,12 @@ std::shared_ptr CreateImageFromFloatImage(const Image &input) return output; } +typedef std::vector> ImagePyramid; + +/// Function to filter pyramid image with pre-defined filtering type +ImagePyramid FilterImagePyramid(const ImagePyramid &input, FilterType type); + +ImagePyramid CreateImagePyramid(const Image& image, + size_t num_of_levels, bool with_gaussian_filter = true); + } // namespace three diff --git a/src/Python/Core/py3d_image.cpp b/src/Python/Core/py3d_image.cpp index 210e144250c..9cac4eca66b 100644 --- a/src/Python/Core/py3d_image.cpp +++ b/src/Python/Core/py3d_image.cpp @@ -124,4 +124,43 @@ void pybind_image_methods(py::module &m) return WriteImage(filename, image, quality); }, "Function to write Image to file", "filename"_a, "image"_a, "quality"_a = 90); + py::enum_(m, "FilterType") + .value("Gaussian3", FILTER_GAUSSIAN_3) + .value("Gaussian5", FILTER_GAUSSIAN_5) + .value("Gaussian7", FILTER_GAUSSIAN_7) + .value("Sobel3dx", FILTER_SOBEL_3_DX) + .value("Sobel3dy", FILTER_SOBEL_3_DY) + .export_values(); + m.def("FilterImage", [](const Image &input, + FilterType filter_type) { + if (input.num_of_channels_ != 1 || + input.bytes_per_channel_ != 4) { + auto input_f = CreateFloatImageFromImage(input); + auto output = FilterImage(*input_f, filter_type); + return *output; + } else { + auto output = FilterImage(input, filter_type); + return *output; + } + }, "Function to filter Image", "image"_a, "filter_type"_a); + m.def("CreateImagePyramid", [](const Image &input, + size_t num_of_levels, bool with_gaussian_filter) { + if (input.num_of_channels_ != 1 || + input.bytes_per_channel_ != 4) { + auto input_f = CreateFloatImageFromImage(input); + auto output = CreateImagePyramid( + *input_f, num_of_levels, with_gaussian_filter); + return output; + } else { + auto output = CreateImagePyramid( + input, num_of_levels, with_gaussian_filter); + return output; + } + }, "Function to create ImagePyramid", "image"_a, + "num_of_levels"_a, "with_gaussian_filter"_a); + m.def("FilterImagePyramid", [](const ImagePyramid &input, + FilterType filter_type) { + auto output = FilterImagePyramid(input, filter_type); + return output; + }, "Function to filter ImagePyramid", "image_pyramid"_a, "filter_type"_a); } diff --git a/src/Python/Test/test_py3d.py b/src/Python/Test/test_py3d.py index 5ec4523a17b..0adf839fd77 100644 --- a/src/Python/Test/test_py3d.py +++ b/src/Python/Test/test_py3d.py @@ -133,6 +133,33 @@ def test_py3d_image(): plt.imshow(zzz) plt.show() + print("Testing basic image processing module.") + im_raw = mpimg.imread("../TestData/lena_color.jpg") + im = Image(im_raw) + im_g3 = FilterImage(im, FilterType.Gaussian3) + im_g5 = FilterImage(im, FilterType.Gaussian5) + im_g7 = FilterImage(im, FilterType.Gaussian7) + im_gaussian = [im, im_g3, im_g5, im_g7] + pyramid_levels = 4 + pyramid_with_gaussian_filter = True + im_pyramid = CreateImagePyramid(im, pyramid_levels, + pyramid_with_gaussian_filter) + im_dx = FilterImage(im, FilterType.Sobel3dx) + im_dx_pyramid = FilterImagePyramid(im_pyramid, FilterType.Sobel3dx) + im_dy = FilterImage(im, FilterType.Sobel3dy) + im_dy_pyramid = FilterImagePyramid(im_pyramid, FilterType.Sobel3dy) + switcher = { + 0: im_gaussian, + 1: im_pyramid, + 2: im_dx_pyramid, + 3: im_dy_pyramid, + } + for i in range(4): + for j in range(pyramid_levels): + plt.subplot(4, pyramid_levels, i*4+j+1) + plt.imshow(switcher.get(i)[j]) + plt.show() + print("Final test: load an RGB-D image pair and convert to pointcloud.") im1 = ReadImage("../TestData/RGBD/depth/00000.png") im2 = ReadImage("../TestData/RGBD/color/00000.jpg") diff --git a/src/Test/TestImage.cpp b/src/Test/TestImage.cpp index 1a6772c9d75..31d5d1489dc 100644 --- a/src/Test/TestImage.cpp +++ b/src/Test/TestImage.cpp @@ -73,11 +73,14 @@ int main(int argc, char **argv) PrintDebug("Sobel Filtering\n"); auto gray_image_dx = FilterImage(*gray_image, FILTER_SOBEL_3_DX); - LinearTransformImage(*gray_image_dx, 0.5, 0.5); // make [-1,1] to [0,1]. + // make [-1,1] to [0,1]. + LinearTransformImage(*gray_image_dx, 0.5, 0.5); + ClipIntensityImage(*gray_image_dx); WriteImage("gray_sobel_dx.png", *CreateImageFromFloatImage(*gray_image_dx)); auto gray_image_dy = FilterImage(*gray_image, FILTER_SOBEL_3_DY); LinearTransformImage(*gray_image_dy, 0.5, 0.5); + ClipIntensityImage(*gray_image_dy); WriteImage("gray_sobel_dy.png", *CreateImageFromFloatImage(*gray_image_dy)); @@ -116,11 +119,14 @@ int main(int argc, char **argv) PrintDebug("Sobel Filtering\n"); auto depth_image_dx = FilterImage(*depth_image, FILTER_SOBEL_3_DX); - LinearTransformImage(*depth_image_dx, 1.0, 0.0, 0.0, 32768.0); // make [-1,1] to [0,1]. + // make [-65536,65536] to [0,13107.2]. // todo: need to test this + LinearTransformImage(*depth_image_dx, 0.1, 6553.6); + ClipIntensityImage(*depth_image_dx, 0.0, 13107.2); WriteImage("depth_sobel_dx.png", *CreateImageFromFloatImage(*depth_image_dx)); auto depth_image_dy = FilterImage(*depth_image, FILTER_SOBEL_3_DY); - LinearTransformImage(*depth_image_dy, 1.0, 0.0, 0.0, 32768.0); + LinearTransformImage(*depth_image_dy, 0.1, 6553.6); + ClipIntensityImage(*depth_image_dx, 0.0, 13107.2); WriteImage("depth_sobel_dy.png", *CreateImageFromFloatImage(*depth_image_dy));