Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 7b9858a

Browse files
authored
Merge pull request #1 from Oxid15/repo_update
Repo update
2 parents d95ac86 + e80ec07 commit 7b9858a

16 files changed

+163
-71
lines changed

‎.gitignore‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,13 @@
3535
*.xml
3636
*.pyc
3737
*.iml
38-
.idea/*
38+
.idea/*
39+
*.suo
40+
*.db
41+
*.ipch
42+
*.sqlite
43+
*.tlog
44+
*.log
45+
*.pdb
46+
*.sln
47+
*.vcxproj*

‎README.md‎

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,8 @@
11
# Image Smoothing Algorithm Based on Gradient Analysis
2-
This repository contains C++ and Python 3.6 implementation of an image smoothing algorithm that was proposed in this [publication](https://ieeexplore.ieee.org/document/9117646) in IEEE conference "2020 Ural Symposium on Biomedical Engineering, Radioelectronics and Information Technology (USBEREIT)".
2+
This repository contains C++ and Python 3 implementation of an image smoothing algorithm that was proposed in this [publication](https://ieeexplore.ieee.org/document/9117646).
3+
4+
![example1](/images/example.png)
35

4-
![example1](/images/example1.jpg)
5-
6-
## General idea
7-
In this paper image smoothing algorithm based on gradient analysis is proposed. Our algorithm uses filtering and to achieve edge-preserving smoothing it uses two components of gradient vectors: their magnitudes (or lengths) and directions. Our method discriminates between two types of boundaries in given neighborhood: regular and irregular ones.
8-
![boundaries](/images/boundaries.png)
9-
Regular boundaries have small deviations of gradient angles and the opposite for irregular ones. To measure closeness of angles cosine of doubled difference is used. As additional measure that helps to discriminate the types of boundaries inverted gradient values were used.
10-
![gradients](/images/gradients.png)
11-
When gradient magnitudes are inverted bigger values refer to textures (insignificant changes in gradient) and smaller refer to strong boundaries. So textures would have bigger weights and hence they would appear smoother. We also propose to smooth image of gradient magnitudes with median filter to enhance visual quality of results. The method proposed in this paper is easy to implement and compute and it gives good results in comparison with other techniques like bilateral filter.
12-
13-
## Examples
14-
![example2](/images/example2.jpg)
15-
## Comparison
16-
Here is the comparison with other smoothing algorithms.
17-
a) - original image
18-
b) - guided filter
19-
c) - bilateral filter
20-
d) - our filter
21-
![comparison](/images/comparison.png)
22-
## Edge detection
23-
Here is the output of Canny edge detector that was applied on the image with and without preprocessing with our filter.
24-
![edges](/images/edge_detection.png)
256

267
## How to use code
278
Libraries used:
@@ -30,8 +11,6 @@ Libraries used:
3011
Here is the simple example of filter usage with opencv Mat images:
3112

3213
```cpp
33-
//opencv included in Source.cpp if you need to change include path,
34-
//you should change it there
3514
#include "FilterBasedOnGradientAnalysis.cpp"
3615

3716
int main()
@@ -41,7 +20,7 @@ int main()
4120
int kernelSize = 3; //set kernelSize = 3 for filtering with 3x3 kernel
4221
int runsNumber = 2; //set number of runs: parameter n is 1 by default
4322
Filter<float, uint8_t> filter; //create the instance of filter
44-
cv::Mat output = filter(img, kernelSize, n=runsNumber); //smooth image
23+
cv::Mat output = filter(img, kernelSize, runsNumber); //smooth image
4524

4625
cv::imwrite("your_output_file_name", output); //write the result
4726
return 0;
@@ -58,3 +37,26 @@ runs_number = 2 # set number of runs
5837
output = fga.smooth(img, kernel_size, n=runs_number) # smooth image
5938
cv2.imwrite('your_output_file_name', output) # write the result
6039
```
40+
41+
## General idea
42+
Our algorithm uses filtering and to achieve edge-preserving smoothing it uses two components of gradient vectors: their magnitudes (or lengths) and directions. Our method discriminates between two types of boundaries in given neighborhood: regular and irregular ones.
43+
![boundaries](/images/boundaries.png)
44+
Regular boundaries have small deviations of gradient angles and the opposite for irregular ones. To measure closeness of angles cosine of doubled difference is used. As additional measure that helps to discriminate the types of boundaries inverted gradient values were used.
45+
![gradients](/images/gradients.png)
46+
When gradient magnitudes are inverted bigger values refer to textures (insignificant changes in gradient) and smaller refer to strong boundaries. So textures would have bigger weights and hence they would appear smoother. We also propose to smooth image of gradient magnitudes with median filter to enhance visual quality of results. The method proposed in this paper is easy to implement and compute and it gives good results in comparison with other techniques like bilateral filter.
47+
48+
## Citation
49+
50+
If you used the code or want to reference this method in your work, please cite:
51+
52+
```
53+
@inproceedings{gudkov2020image,
54+
title={Image smoothing algorithm based on gradient analysis},
55+
author={Gudkov, Vladimir and Moiseev, Ilia},
56+
booktitle={2020 Ural Symposium on Biomedical Engineering, Radioelectronics and Information Technology (USBEREIT)},
57+
pages={403--406},
58+
year={2020},
59+
organization={IEEE}
60+
}
61+
```
62+

‎examples/example.cpp‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//opencv included in Source.cpp if you need to change include path,
2+
//you should change it there
3+
#include "../src/FilterBasedOnGradientAnalysis.cpp"
4+
5+
6+
int main()
7+
{
8+
cv::Mat img = cv::imread("your_input_file_name", cv::IMREAD_COLOR); //read image using opencv from file into Mat type
9+
10+
int kernelSize = 3; //set kernelSize = 3 for filtering with 3x3 kernel
11+
int runsNumber = 2; //set number of runs: parameter n is 1 by default
12+
Filter<float, uint8_t, uint32_t> filter; //create the instance of filter
13+
cv::Mat output = filter(img, kernelSize, runsNumber); //smooth image
14+
15+
cv::imwrite("your_output_file_name", output); //write the result
16+
return 0;
17+
}

‎examples/example.py‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import sys
2+
import os
3+
import cv2
4+
5+
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
6+
sys.path.append(os.path.dirname(SCRIPT_DIR))
7+
from fga import smooth
8+
9+
10+
os.makedirs('../output', exist_ok=True)
11+
12+
img = cv2.imread('../images/engel_sm.bmp', cv2.IMREAD_COLOR)
13+
kernel_size = 7
14+
runs_number = 1
15+
16+
output = smooth(img, kernel_size, n=runs_number)
17+
cv2.imwrite(f'../output/engel_sm_{kernel_size}_{runs_number}.bmp', output)

‎fga/__init__.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .filter_based_on_gradient_analysis import smooth

‎Python3Source/filter_based_on_gradient_analysis.py‎ renamed to ‎fga/filter_based_on_gradient_analysis.py‎

Lines changed: 30 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,11 @@
33
# Image smoothing Algorithm Based on Gradient Analysis
44

55
import numpy as np
6-
from math import sqrt, atan2, cos, isnan
7-
8-
9-
def _euclid_norm(vect):
10-
return sqrt(vect[0]*vect[0] + vect[1]*vect[1])
11-
12-
13-
def _angle_rad(vect):
14-
return atan2(vect[1], vect[0])
156

167

178
def _grad(x, y, image):
18-
gradx = image[y][x-1] - image[y][x+1]
19-
grady = image[y+1][x] - image[y-1][x]
9+
gradx = image[y][x-1] - image[y][x+1]
10+
grady = image[y+1][x] - image[y-1][x]
2011
return [gradx, grady]
2112

2213

@@ -28,35 +19,26 @@ def compute_grads_channel(image, grads):
2819

2920

3021
def compute_grads(image, grads):
31-
list(map(lambda i: compute_grads_channel(image[:, :, i], grads[:, :, :, i]), [i for i in range(3)]))
22+
for i in range(3):
23+
compute_grads_channel(image[:, :, i], grads[:, :, :, i])
3224

3325

34-
def compute_modules_channel(image, modules, grads):
35-
for y in range(image.shape[0]):
36-
for x in range(image.shape[1]):
37-
if 0 < x < image.shape[1] - 1 and 0 < y < image.shape[0] - 1:
38-
modules[y][x] = _euclid_norm(grads[y][x])
39-
40-
41-
def compute_modules(image, modules, grads):
42-
list(map(lambda i: compute_modules_channel(image[:, :, i], modules[:, :, i], grads[:, :, :, i]),
43-
[i for i in range(3)]))
26+
def compute_modules(grads):
27+
return np.linalg.norm(grads, axis=2)
4428

4529

4630
def compute_angles_channel(image, angles, grads):
4731
for y in range(image.shape[0]):
4832
for x in range(image.shape[1]):
4933
if 0 < x < image.shape[1] - 1 and 0 < y < image.shape[0] - 1:
50-
angle = _angle_rad(grads[y, x])
51-
if not isnan(angle):
52-
angles[y, x] = angle
53-
else:
54-
angles[y, x] = 0
34+
g = grads[y, x]
35+
angle = np.arctan2(g[1], g[0])
36+
angles[y, x] = angle if not np.isnan(angle) else 0
5537

5638

5739
def compute_angles(image, angles, grads):
58-
list(map(lambdai: compute_angles_channel(image[:, :, i], angles[:, :, i], grads[:, :, :, i]),
59-
[iforiinrange(3)]))
40+
foriinrange(3):
41+
compute_angles_channel(image[:, :, i], angles[:, :, i], grads[:, :, :, i])
6042

6143

6244
def smooth_channel(src, k_size, n=1, grads=None, modules=None, angles=None, dst=None):
@@ -93,8 +75,7 @@ def _smooth_channel(src, k_size, grads=None, modules=None, angles=None, dst=None
9375
grads = np.zeros((src.shape[0], src.shape[1], 2))
9476
compute_grads_channel(src.astype(np.float64), grads)
9577
if modules is None:
96-
modules = np.zeros((src.shape[0], src.shape[1]))
97-
compute_modules_channel(src.astype(np.float64), modules, grads)
78+
modules = compute_modules(grads)
9879
if angles is None:
9980
angles = np.zeros((src.shape[0], src.shape[1]))
10081
compute_angles_channel(src.astype(np.float64), angles, grads)
@@ -119,7 +100,7 @@ def _smooth_channel(src, k_size, grads=None, modules=None, angles=None, dst=None
119100
if s != i or t != j:
120101
alpha = 1. / modules[s][t]
121102
beta = 2. * (angles[i][j] - angles[s][t])
122-
weight = (cos(beta) + 1) * alpha
103+
weight = (np.cos(beta) + 1) * alpha
123104
else:
124105
# weight of central pixel
125106
weight = 1.
@@ -139,18 +120,18 @@ def _smooth(src, dst, k_size, grads=None, modules=None, angles=None):
139120
grads = np.zeros((src.shape[0], src.shape[1], 2, 3))
140121
compute_grads(src.astype(np.float64), grads)
141122
if modules is None:
142-
modules = np.zeros((src.shape[0], src.shape[1], 3))
143-
compute_modules(src.astype(np.float64), modules, grads)
123+
modules = compute_modules(grads)
144124
if angles is None:
145125
angles = np.zeros((src.shape[0], src.shape[1], 3))
146126
compute_angles(src.astype(np.float64), angles, grads)
147127

148-
list(map(lambda i: smooth_channel(src[:, :, i].astype(np.float64),
149-
k_size,
150-
grads=grads[:, :, :, i],
151-
modules=modules[:, :, i],
152-
angles=angles[:, :, i],
153-
dst=dst[:, :, i]), [i for i in range(3)]))
128+
for i in range(3):
129+
smooth_channel(src[:, :, i].astype(np.float64),
130+
k_size,
131+
grads=grads[:, :, :, i],
132+
modules=modules[:, :, i],
133+
angles=angles[:, :, i],
134+
dst=dst[:, :, i])
154135
return dst
155136

156137

@@ -166,12 +147,16 @@ def smooth(src, k_size, n=1, grads=None, modules=None, angles=None):
166147
:param angles: gradient angles for each pixel with shape (n, m, 3)
167148
:return: smoothed image with same shape as src and type np.float64
168149
"""
150+
if k_size % 2 == 0:
151+
raise ValueError(f'k_size should be odd, got {k_size} instead')
152+
169153
src_proxy = np.copy(src)
170154
dst = np.zeros(src.shape, np.float64)
171-
for i in range(n):
172-
if i == 0:
173-
_smooth(src_proxy, dst, k_size, grads=grads, modules=modules, angles=angles)
174-
else:
155+
156+
if n == 1:
157+
_smooth(src_proxy, dst, k_size, grads=grads, modules=modules, angles=angles)
158+
else:
159+
for i in range(n):
175160
_smooth(src_proxy, dst, k_size)
176-
src_proxy = dst
161+
src_proxy = dst
177162
return dst

‎images/comparison.png‎

-506 KB
Binary file not shown.

‎images/edge_detection.png‎

-826 KB
Binary file not shown.

‎images/engel_sm.bmp‎

10.6 KB
Binary file not shown.

‎images/engel_sm_3_2.bmp‎

10.6 KB
Binary file not shown.

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /