Skip to content

Commit e94ec40

Browse files
authored
Merge pull request #3090 from kuloPo:add-hough_space_transform
Add Radon transform function to ximgproc * resolve rebash conflict * add test for hough_space_transform * add perf for hough_space_transform * add sample for hough_space_transform * fix compatible issue and trailing space * fix EOF issue * resolve rebash conflict * update radon transform fix always cropping corner when rotate transpose the output - now each column is integral add support for float number add comment * add float type to perf test * add float type to accu test * update document * fix trailing space * resolve rebase conflict * Resolve coding style issues
1 parent a26f713 commit e94ec40

File tree

7 files changed

+269
-0
lines changed

7 files changed

+269
-0
lines changed

modules/ximgproc/include/opencv2/ximgproc.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
#include "ximgproc/run_length_morphology.hpp"
6161
#include "ximgproc/edgepreserving_filter.hpp"
6262
#include "ximgproc/color_match.hpp"
63+
#include "ximgproc/radon_transform.hpp"
6364

6465

6566
/** @defgroup ximgproc Extended Image Processing
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// This file is part of OpenCV project.
2+
// It is subject to the license terms in the LICENSE file found in the top-level directory
3+
// of this distribution and at http://opencv.org/license.html.
4+
5+
#ifndef __OPENCV_RADON_TRANSFORM_HPP__
6+
#define __OPENCV_RADON_TRANSFORM_HPP__
7+
8+
#include "opencv2/core.hpp"
9+
#include "opencv2/imgproc.hpp"
10+
11+
namespace cv { namespace ximgproc {
12+
/**
13+
* @brief Calculate Radon Transform of an image.
14+
* @param src The source (input) image.
15+
* @param dst The destination image, result of transformation.
16+
* @param theta Angle resolution of the transform in degrees.
17+
* @param start_angle Start angle of the transform in degrees.
18+
* @param end_angle End angle of the transform in degrees.
19+
* @param crop Crop the source image into a circle.
20+
* @param norm Normalize the output Mat to grayscale and convert type to CV_8U
21+
*
22+
* This function calculates the Radon Transform of a given image in any range.
23+
* See https://engineering.purdue.edu/~malcolm/pct/CTI_Ch03.pdf for detail.
24+
* If the input type is CV_8U, the output will be CV_32S.
25+
* If the input type is CV_32F or CV_64F, the output will be CV_64F
26+
* The output size will be num_of_integral x src_diagonal_length.
27+
* If crop is selected, the input image will be crop into square then circle,
28+
* and output size will be num_of_integral x min_edge.
29+
*
30+
*/
31+
CV_EXPORTS_W void RadonTransform(InputArray src,
32+
OutputArray dst,
33+
double theta = 1,
34+
double start_angle = 0,
35+
double end_angle = 180,
36+
bool crop = false,
37+
bool norm = false);
38+
} }
39+
40+
#endif
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// This file is part of OpenCV project.
2+
// It is subject to the license terms in the LICENSE file found in the top-level directory
3+
// of this distribution and at http://opencv.org/license.html.
4+
5+
#include "perf_precomp.hpp"
6+
7+
namespace opencv_test { namespace {
8+
9+
typedef tuple<Size, MatType> RadonTransformPerfTestParam;
10+
typedef perf::TestBaseWithParam<RadonTransformPerfTestParam> RadonTransformPerfTest;
11+
12+
PERF_TEST_P(RadonTransformPerfTest, perf,
13+
testing::Combine(
14+
testing::Values(TYPICAL_MAT_SIZES),
15+
testing::Values(CV_8UC1, CV_32FC1, CV_64FC1)
16+
)
17+
)
18+
{
19+
Size srcSize = get<0>(GetParam());
20+
int srcType = get<1>(GetParam());
21+
22+
Mat src(srcSize, srcType);
23+
Mat radon;
24+
25+
declare.in(src, WARMUP_RNG);
26+
27+
TEST_CYCLE()
28+
{
29+
RadonTransform(src, radon);
30+
}
31+
32+
SANITY_CHECK_NOTHING();
33+
}
34+
35+
} }
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// This file is part of OpenCV project.
2+
// It is subject to the license terms in the LICENSE file found in the top-level directory
3+
// of this distribution and at http://opencv.org/license.html.
4+
5+
#include <opencv2/highgui.hpp>
6+
#include <opencv2/ximgproc/radon_transform.hpp>
7+
8+
using namespace cv;
9+
10+
int main() {
11+
Mat src = imread("peilin_plane.png", IMREAD_GRAYSCALE);
12+
Mat radon;
13+
ximgproc::RadonTransform(src, radon, 1, 0, 180, false, true);
14+
imshow("src image", src);
15+
imshow("Radon transform", radon);
16+
waitKey();
17+
return 0;
18+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# This file is part of OpenCV project.
2+
# It is subject to the license terms in the LICENSE file found in the top-level directory
3+
# of this distribution and at http://opencv.org/license.html.
4+
5+
import numpy as np
6+
import cv2 as cv
7+
8+
if __name__ == "__main__":
9+
src = cv.imread("peilin_plane.png", cv.IMREAD_GRAYSCALE)
10+
radon = cv.ximgproc.RadonTransform(src)
11+
cv.imshow("src image", src)
12+
cv.imshow("Radon transform", radon)
13+
cv.waitKey()
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// This file is part of OpenCV project.
2+
// It is subject to the license terms in the LICENSE file found in the top-level directory
3+
// of this distribution and at http://opencv.org/license.html.
4+
5+
#include "precomp.hpp"
6+
7+
namespace cv {namespace ximgproc {
8+
void RadonTransform(InputArray src,
9+
OutputArray dst,
10+
double theta,
11+
double start_angle,
12+
double end_angle,
13+
bool crop,
14+
bool norm)
15+
{
16+
CV_Assert(src.dims() == 2);
17+
CV_Assert(src.channels() == 1);
18+
CV_Assert((end_angle - start_angle) * theta > 0);
19+
20+
Mat _srcMat = src.getMat();
21+
22+
int _row_num, _col_num, _out_mat_type;
23+
_col_num = cvRound((end_angle - start_angle) / theta);
24+
transpose(_srcMat, _srcMat);
25+
Mat _masked_src;
26+
cv::Point _center;
27+
28+
if (_srcMat.type() == CV_32FC1 || _srcMat.type() == CV_64FC1) {
29+
_out_mat_type = CV_64FC1;
30+
}
31+
else {
32+
_out_mat_type = CV_32SC1;
33+
}
34+
35+
if (crop) {
36+
// crop the source into square
37+
_row_num = min(_srcMat.rows, _srcMat.cols);
38+
cv::Rect _crop_ROI(
39+
_srcMat.cols / 2 - _row_num / 2,
40+
_srcMat.rows / 2 - _row_num / 2,
41+
_row_num, _row_num);
42+
_srcMat = _srcMat(_crop_ROI);
43+
// crop the source into circle
44+
Mat _mask(_srcMat.size(), CV_8UC1, Scalar(0));
45+
_center = Point(_srcMat.cols / 2, _srcMat.rows / 2);
46+
circle(_mask, _center, _srcMat.cols / 2, Scalar(255), FILLED);
47+
_srcMat.copyTo(_masked_src, _mask);
48+
}
49+
else {
50+
// avoid cropping corner when rotating
51+
_row_num = cvCeil(sqrt(_srcMat.rows * _srcMat.rows + _srcMat.cols * _srcMat.cols));
52+
_masked_src = Mat(Size(_row_num, _row_num), _srcMat.type(), Scalar(0));
53+
_center = Point(_masked_src.cols / 2, _masked_src.rows / 2);
54+
_srcMat.copyTo(_masked_src(Rect(
55+
(_row_num - _srcMat.cols) / 2,
56+
(_row_num - _srcMat.rows) / 2,
57+
_srcMat.cols, _srcMat.rows)));
58+
}
59+
60+
double _t;
61+
Mat _rotated_src;
62+
Mat _radon(_row_num, _col_num, _out_mat_type);
63+
64+
for (int _col = 0; _col < _col_num; _col++) {
65+
// rotate the source by _t
66+
_t = (start_angle + _col * theta);
67+
cv::Mat _r_matrix = cv::getRotationMatrix2D(_center, _t, 1);
68+
cv::warpAffine(_masked_src, _rotated_src, _r_matrix, _masked_src.size());
69+
Mat _col_mat = _radon.col(_col);
70+
// make projection
71+
cv::reduce(_rotated_src, _col_mat, 1, REDUCE_SUM, _out_mat_type);
72+
}
73+
74+
if (norm) {
75+
normalize(_radon, _radon, 0, 255, NORM_MINMAX, CV_8UC1);
76+
}
77+
78+
_radon.copyTo(dst);
79+
return;
80+
}
81+
} }
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// This file is part of OpenCV project.
2+
// It is subject to the license terms in the LICENSE file found in the top-level directory
3+
// of this distribution and at http://opencv.org/license.html.
4+
5+
#include "test_precomp.hpp"
6+
7+
namespace opencv_test {namespace {
8+
9+
TEST(RadonTransformTest, output_size)
10+
{
11+
Mat src(Size(256, 256), CV_8U, Scalar(0));
12+
circle(src, Point(128, 128), 64, Scalar(255), FILLED);
13+
Mat radon;
14+
cv::ximgproc::RadonTransform(src, radon);
15+
16+
EXPECT_EQ(363, radon.rows);
17+
EXPECT_EQ(180, radon.cols);
18+
19+
cv::ximgproc::RadonTransform(src, radon, 1, 0, 180, true);
20+
21+
EXPECT_EQ(256, radon.rows);
22+
EXPECT_EQ(180, radon.cols);
23+
}
24+
25+
TEST(RadonTransformTest, output_type)
26+
{
27+
Mat src_int(Size(256, 256), CV_8U, Scalar(0));
28+
circle(src_int, Point(128, 128), 64, Scalar(255), FILLED);
29+
Mat radon, radon_norm;
30+
cv::ximgproc::RadonTransform(src_int, radon);
31+
cv::ximgproc::RadonTransform(src_int, radon_norm, 1, 0, 180, false, true);
32+
33+
EXPECT_EQ(CV_32SC1, radon.type());
34+
EXPECT_EQ(CV_8U, radon_norm.type());
35+
36+
Mat src_float(Size(256, 256), CV_32FC1, Scalar(0));
37+
Mat src_double(Size(256, 256), CV_32FC1, Scalar(0));
38+
cv::ximgproc::RadonTransform(src_float, radon);
39+
cv::ximgproc::RadonTransform(src_float, radon_norm, 1, 0, 180, false, true);
40+
EXPECT_EQ(CV_64FC1, radon.type());
41+
EXPECT_EQ(CV_8U, radon_norm.type());
42+
cv::ximgproc::RadonTransform(src_double, radon);
43+
EXPECT_EQ(CV_64FC1, radon.type());
44+
EXPECT_EQ(CV_8U, radon_norm.type());
45+
}
46+
47+
TEST(RadonTransformTest, accuracy_by_pixel)
48+
{
49+
Mat src(Size(256, 256), CV_8U, Scalar(0));
50+
circle(src, Point(128, 128), 64, Scalar(255), FILLED);
51+
Mat radon;
52+
cv::ximgproc::RadonTransform(src, radon);
53+
54+
ASSERT_EQ(CV_32SC1, radon.type());
55+
56+
EXPECT_EQ(0, radon.at<int>(0, 0));
57+
58+
EXPECT_LT(18000, radon.at<int>(128, 128));
59+
EXPECT_GT(19000, radon.at<int>(128, 128));
60+
}
61+
62+
TEST(RadonTransformTest, accuracy_uchar)
63+
{
64+
Mat src(Size(10, 10), CV_8UC1, Scalar(1));
65+
cv::Mat radon;
66+
ximgproc::RadonTransform(src, radon, 45, 0, 180, false, false);
67+
68+
EXPECT_EQ(100, sum(radon.col(0))[0]);
69+
}
70+
71+
TEST(RadonTransformTest, accuracy_float)
72+
{
73+
Mat src(Size(10, 10), CV_32FC1, Scalar(1.1));
74+
cv::Mat radon;
75+
ximgproc::RadonTransform(src, radon, 45, 0, 180, false, false);
76+
77+
EXPECT_LT(109, sum(radon.col(0))[0]);
78+
EXPECT_GT(111, sum(radon.col(0))[0]);
79+
}
80+
81+
} }

0 commit comments

Comments
 (0)