Skip to content

Commit 93c9d41

Browse files
committed
Merge pull request opencv#5 from KruchDmitriy/photoeffects
New optimization and documentation for edgeBlur
2 parents 3b3ce69 + cf3c71f commit 93c9d41

File tree

7 files changed

+97
-81
lines changed

7 files changed

+97
-81
lines changed

modules/photoeffects/doc/edge_blur.rst

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,41 @@ Edge Blur
33
=======================================
44
Blurs the edges of an image, keeping center in focus.
55

6-
.. cpp:function:: int edgeBlur(InputArray src, OutputArray dst, int indentTop, int indentLeft)
6+
.. ocv:function:: void edgeBlur(InputArray src, OutputArray dst, int indentTop, int indentLeft)
77
88
:param src: RGB image.
99
:param dst: Destination image of the same size and the same type as **src**.
1010
:param indentTop: The indent from the top and the bottom of the image. It will be blured.
1111
:param indentLeft: The indent from the left and right side of the image. It will be blured.
12-
:return: Error code.
1312

1413
The algorithm.
1514

16-
The amount of blurring is defined by the gaussian filter's kernel size and standard deviation and depends on the weighted distance from the center of the image:
15+
1. Apply boxFilter to the src image.
1716

18-
.. math::
19-
d(x, y) = \frac{(x - a)^2}{(a - indentLeft)^2} + \frac{(y - b)^2}{(b - indentTop)^2},
17+
2. For each pixel (x, y) in dst
2018

21-
where :math:`a = \frac{src_{width}}{2}, b = \frac{src_{height}}{2}`. For each pixel :math:`(x, y)` of the image, if the distance :math:`d(x, y)` is greater than 1, gaussian filter with center at :math:`(x,y)`, kernel size :math:`d(x, y)` and standard deviation :math:`(radius - 0.5)` is applied, otherwise **src** image pixel is left unchanged. Border pixels are replicated to fit the kernel size.
19+
* Calculate distance:
2220

21+
.. math::
22+
dist(x, y) = \frac{(x - x_c)^2}{a^2} + \frac{(y - y_c)^2}{b^2},
23+
24+
where :math:`(x_c, y_c)` - center of the image, :math:`a = x_c - indentLeft, b = y_c - indentTop`.
25+
26+
* Form the mask:
27+
28+
.. math::
29+
mask(x, y) = \left\{
30+
\begin{array}{lll}
31+
0 & \quad dist(x, y) \leq 0.75\\
32+
2 * dist(x, y) + 1.5 & \quad dist(x, y ) \in (0.75, 1.25]\\
33+
1 & \quad dsit(x, y) > 1.25
34+
\end{array}
35+
\right.
36+
37+
* Result
38+
39+
.. math::
40+
dst = boxFilter(src) * mask + src * (1 - mask)
2341
2442
Example.
2543

modules/photoeffects/doc/tint.rst

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,12 @@ Tint
33
=======================================
44
Add a tint to the image.
55

6-
.. cpp:function:: int tint(cv::InputArray src, cv::OutputArray dst, const cv::Vec3b &colorTint, float density)
6+
.. ocv:function:: void tint(cv::InputArray src, cv::OutputArray dst, const cv::Vec3b &colorTint, float density)
77
88
:param src: Source 8-bit 3-channel (RGB) image.
99
:param dst: Destination image of the same size and the same type as **src**.
1010
:param colorTint: It's a bearing color. All color of the image **src** will be shifted to it.
1111
:param density: Float value between 0 and 1, defines a range of shift to the colorTint.
12-
:return: Error code.
1312

1413
The algorithm:
1514

modules/photoeffects/include/opencv2/photoeffects.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ CV_EXPORTS_W int filmGrain(cv::InputArray src, cv::OutputArray dst, int grainVa
1414

1515
CV_EXPORTS_W int fadeColor(cv::InputArray src, cv::OutputArray dst,cv::Point startPoint,cv::Point endPoint);
1616

17-
CV_EXPORTS_W int tint(cv::InputArray src, cv::OutputArray dst, const cv::Vec3b &colorTint, float density);
17+
CV_EXPORTS_W void tint(cv::InputArray src, cv::OutputArray dst, const cv::Vec3b &colorTint, float density);
1818

1919
CV_EXPORTS_W void glow(cv::InputArray src, cv::OutputArray dst, int radius = 0, float intensity = 0.0f);
2020

21-
CV_EXPORTS_W int edgeBlur(cv::InputArray src, cv::OutputArray dst, int indentTop, int indentLeft);
21+
CV_EXPORTS_W void edgeBlur(cv::InputArray src, cv::OutputArray dst, int indentTop, int indentLeft);
2222

2323
CV_EXPORTS_W void boostColor(cv::InputArray src, cv::OutputArray dst, float intensity = 0.0f);
2424

modules/photoeffects/src/edgeBlur.cpp

Lines changed: 69 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,69 +2,86 @@
22

33
namespace cv { namespace photoeffects {
44

5-
int edgeBlur(InputArray src, OutputArray dst, int indentTop, int indentLeft)
5+
class edgeBlurInvoker :public ParallelLoopBody
6+
{
7+
public:
8+
edgeBlurInvoker(const Mat& src,
9+
const Mat& boxFilt,
10+
Mat& dst,
11+
int indentTop,
12+
int indentLeft)
13+
: src_(src),
14+
boxFilt_(boxFilt),
15+
dst_(dst),
16+
indentTop_(indentTop),
17+
indentLeft_(indentLeft) {}
18+
19+
void operator()(const Range& range) const
20+
{
21+
float halfWidth = src_.cols / 2.0f;
22+
float halfHeight = src_.rows / 2.0f;
23+
float a = (halfWidth - indentLeft_) * (halfWidth - indentLeft_);
24+
float b = (halfHeight - indentTop_) * (halfHeight - indentTop_);
25+
26+
Mat srcStripe = src_.rowRange(range.start, range.end);
27+
Mat boxStripe = boxFilt_.rowRange(range.start, range.end);
28+
Mat dstStripe = dst_.rowRange(range.start, range.end);
29+
30+
int rows = srcStripe.rows;
31+
for (int i = 0; i < rows; i++)
32+
{
33+
uchar* row = (uchar*)srcStripe.row(i).data;
34+
uchar* boxRow = (uchar*)boxStripe.row(i).data;
35+
uchar* dstRow = (uchar*)dstStripe.row(i).data;
36+
float y_part = (halfHeight - (i + range.start)) *
37+
(halfHeight - (i + range.start)) / b;
38+
39+
for (int j = 0; j < 3 * src_.cols; j += 3)
40+
{
41+
float maskEl = min(max(2.0f *
42+
((halfWidth - j / 3) * (halfWidth - j / 3) / a +
43+
y_part - 0.5f), 0.0f), 1.0f);
44+
float negMask = 1.0f - maskEl;
45+
46+
dstRow[j] = boxRow[j] * maskEl + row[j] * negMask;
47+
dstRow[j + 1] = boxRow[j + 1] * maskEl + row[j + 1] * negMask;
48+
dstRow[j + 2] = boxRow[j + 2] * maskEl + row[j + 2] * negMask;
49+
}
50+
}
51+
}
52+
53+
private:
54+
const Mat& src_;
55+
const Mat& boxFilt_;
56+
Mat& dst_;
57+
int indentTop_;
58+
int indentLeft_;
59+
60+
edgeBlurInvoker& operator=(const edgeBlurInvoker&);
61+
};
62+
63+
void edgeBlur(InputArray src, OutputArray dst, int indentTop, int indentLeft)
664
{
765
CV_Assert(!src.empty());
866
CV_Assert(src.type() == CV_8UC3);
67+
968
dst.create(src.size(), src.type());
1069
Mat image = src.getMat(), outputImage = dst.getMat();
1170

1271
CV_Assert(indentTop >= 0 && indentTop <= (image.rows / 2 - 10));
1372
CV_Assert(indentLeft >= 0 && indentLeft <= (image.cols / 2 - 10));
1473

15-
float halfWidth = image.cols / 2.0f;
16-
float halfHeight = image.rows / 2.0f;
17-
float a = (halfWidth - indentLeft)
18-
* (halfWidth - indentLeft);
19-
float b = (halfHeight - indentTop)
20-
* (halfHeight - indentTop);
21-
int kSizeEdges = halfWidth * halfWidth / a + halfHeight * halfHeight / b;
22-
23-
// 15 is a maximal kernel size
24-
kSizeEdges = MIN(kSizeEdges, 15);
25-
Mat bearingImage;
26-
copyMakeBorder(image, bearingImage, kSizeEdges, kSizeEdges,
27-
kSizeEdges, kSizeEdges, BORDER_REPLICATE);
74+
Mat boxFilt;
2875

76+
boxFilter(image, boxFilt, -1, Size(7, 7), Point(-1, -1),
77+
true, BORDER_REPLICATE);
2978

30-
for (int i = kSizeEdges; i < bearingImage.rows - kSizeEdges; i++)
31-
{
32-
for (int j = kSizeEdges; j < bearingImage.cols - kSizeEdges; j++)
33-
{
34-
float radius = (halfHeight - i)
35-
* (halfHeight- i)
36-
/ b
37-
+ (halfWidth - j)
38-
* (halfWidth - j)
39-
/ a;
40-
if (radius < 1.0f)
41-
{
42-
outputImage.at<Vec3b>(i - kSizeEdges, j - kSizeEdges) =
43-
bearingImage.at<Vec3b>(i, j);
44-
continue;
45-
}
46-
int size = MIN(radius, kSizeEdges);
47-
radius = 2.0f * (radius - 0.5f) * (radius - 0.5f);
48-
float sumC = 0.0f;
49-
Vec3f sumF;
50-
float coeff1 = 1.0f / (CV_PI * radius);
51-
for (int x = -size; x <= size; x++)
52-
{
53-
for (int y = -size; y <= size; y++)
54-
{
55-
float coeff2 = coeff1 * exp(- (x * x + y * y) / radius);
56-
Vec3b Color = bearingImage.at<Vec3b>(x + i, y + j);
57-
sumF += coeff2 * (Vec3f)Color;
58-
sumC += coeff2;
59-
}
60-
}
61-
sumF *= (1.0f / sumC);
62-
outputImage.at<Vec3b>(i - kSizeEdges,
63-
j - kSizeEdges) = (Vec3b)sumF;
64-
}
65-
}
66-
67-
return 0;
79+
parallel_for_(Range(0, image.rows),
80+
edgeBlurInvoker(image,
81+
boxFilt,
82+
outputImage,
83+
indentTop,
84+
indentLeft));
6885
}
6986

7087
}}

modules/photoeffects/src/tint.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ using namespace cv;
44

55
namespace cv { namespace photoeffects {
66

7-
int tint(InputArray src, OutputArray dst, const Vec3b &colorTint, float density)
7+
void tint(InputArray src, OutputArray dst, const Vec3b &colorTint, float density)
88
{
99
CV_Assert(src.type() == CV_8UC3);
1010
CV_Assert(density >= 0.0f && density <= 1.0f);
@@ -14,7 +14,6 @@ int tint(InputArray src, OutputArray dst, const Vec3b &colorTint, float density)
1414
Scalar(colorTint[0], colorTint[1], colorTint[2]));
1515

1616
outputImage = matColTint * density + image * (1 - density);
17-
return 0;
1817
}
1918

2019
}}

modules/photoeffects/test/edgeBlur_test.cpp

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,6 @@ using namespace cv::photoeffects;
55

66
using namespace std;
77

8-
TEST(photoeffects_edgeBlur, test)
9-
{
10-
Mat src(50, 50, CV_8UC3), dst;
11-
src = Mat::zeros(50, 50, CV_8UC3);
12-
13-
EXPECT_EQ(0, edgeBlur(src, dst, 1, 1));
14-
}
15-
168
TEST(photoeffects_edgeBlur, wrong_image)
179
{
1810
Mat src1(50, 50, CV_8UC1), src2, dst;
@@ -46,8 +38,6 @@ TEST(photoeffects_edgeBlur, regression)
4638
if (rightDst.empty())
4739
FAIL() << "Can't read " + expectedOutput + " image";
4840

49-
EXPECT_EQ(0, edgeBlur(src, dst, 130, 160));
50-
5141
Mat diff = abs(rightDst - dst);
5242
Mat mask = diff.reshape(1) > 1;
5343
EXPECT_EQ(0, countNonZero(mask));

modules/photoeffects/test/tint_test.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,6 @@ using namespace cv::photoeffects;
55

66
using namespace std;
77

8-
TEST(photoeffects_tint, test) {
9-
Mat src(10, 10, CV_8UC3), dst;
10-
Vec3b color;
11-
EXPECT_EQ(0, tint(src, dst, color, 0.0f));
12-
}
13-
148
TEST(photoeffects_tint, wrong_image)
159
{
1610
Mat src(10, 10, CV_8UC2), dst;
@@ -43,7 +37,6 @@ TEST(photoeffects_tint, regression)
4337
FAIL() << "Can't read " + expectedOutput + " image";
4438

4539
Vec3b color(128, 255, 0);
46-
EXPECT_EQ(0, tint(src, dst, color, 0.1f));
4740

4841
Mat diff = abs(rightDst - dst);
4942
Mat mask = diff.reshape(1) > 1;

0 commit comments

Comments
 (0)