├── .gitignore
├── .idea
├── .gitignore
├── dictionaries
│ └── dyh.xml
├── inspectionProfiles
│ ├── Project_Default.xml
│ └── profiles_settings.xml
├── misc.xml
├── modules.xml
├── opencv_tools.iml
├── other.xml
└── vcs.xml
├── README.md
├── cover.jpg
├── image_color.py
├── image_enhancement.py
├── image_filtering.py
├── image_outline.py
├── image_transformation.py
├── images
└── 000000507081.jpg
├── main.py
├── output
└── .gitignore
├── push.sh
├── requirements.txt
└── utils.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
130 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Datasource local storage ignored files
5 | /dataSources/
6 | /dataSources.local.xml
7 | # Editor-based HTTP Client requests
8 | /httpRequests/
9 |
--------------------------------------------------------------------------------
/.idea/dictionaries/dyh.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/opencv_tools.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/other.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 图像预处理 pipeline 工具
2 |
3 | 一键预览 OpenCV 60 种图像效果
4 |
5 | ### 视频
6 |
7 | bilibili
8 |
9 | [](https://www.bilibili.com/video/BV1Cy4y127P7/ "bilibili")
10 |
11 |
12 | ### 图像色彩
13 |
14 | image_color.py
15 |
16 | - 色度/色调
17 | - 饱和度
18 | - 纯度/亮度
19 | - 固定饱和度s
20 | - 固定亮度v
21 | - 固定色度h + 固定饱和度s
22 | - 固定色度h + 固定亮度v
23 | - 固定饱和度s + 固定亮度v
24 |
25 | ### 图像变换
26 |
27 | image_transformation.py
28 |
29 | - 形态学滤波器腐蚀和膨胀图像
30 | - 腐蚀 3x3
31 | - 膨胀 3x3 3次
32 | - 腐蚀 7x7
33 | - 腐蚀 3x3 3次
34 | - 形态学滤波器开启和闭合图像
35 | - Close the image
36 | - Open the image
37 | - 灰度图像中应用形态学运算 Gradient | Edge
38 | - Apply threshold to obtain a binary image
39 | - 7x7 Black Top-hat Image
40 | - Apply threshold to obtain a binary image
41 | - Apply the black top-hat transform using a 7x7 structuring element
42 |
43 | ### 图像过滤
44 |
45 | - Blur the image with a mean filter
46 | - Blur the image with a mean filter 9x9
47 | - 缩减 采样
48 | - resizing with NN
49 | - resizing with bilinear
50 | - 中值滤波
51 | - 定向滤波器
52 | - Compute Sobel X derivative
53 | - Compute Sobel Y derivative
54 | - Compute norm of Sobel
55 | - Compute Sobel X derivative (7x7)
56 | - Apply threshold to Sobel norm (low threshold value)
57 | - Apply threshold to Sobel norm (high threshold value)
58 | - down-sample and up-sample the image
59 | - down-sample and up-sample the image
60 | - cv2.subtract
61 | - cv2.subtract gauss15 - gauss05
62 | - cv2.subtract gauss22 - gauss20
63 |
64 | ### 提取直线、轮廓、区域
65 |
66 | image_outline.py
67 |
68 | - Canny Contours
69 | - Canny Contours Gray
70 | - Hough tranform for line detection
71 | - Circles with HoughP
72 | - Get the contours, Contours with RETR_LIST
73 |
74 | ### 图像增强-白平衡等
75 |
76 | image_enhancement.py
77 |
78 | - 简单白平衡
79 | - 灰度世界算法
80 | - 直方图均衡化
81 | - 视网膜-大脑皮层(Retinex)增强算法
82 | - Single Scale Retinex
83 | - Multi Scale Retinex
84 | - Multi Scale Retinex With Color Restoration
85 | - 自动白平衡 AWB
86 | - 自动色彩均衡 ACE
87 |
88 |
89 | ## 运行环境
90 |
91 | - python 3.6+,pip 20+
92 | - pip install -r requirements.txt
93 |
94 | ```
95 | Pillow==8.0.1
96 | numpy==1.19.4
97 | opencv-python==4.4.0.46
98 | six==1.15.0
99 | matplotlib==3.3.3
100 | cycler==0.10.0
101 | kiwisolver==1.3.1
102 | pkg-resources==0.0.0
103 | pyparsing==2.4.7
104 | python-dateutil==2.8.1
105 | ```
106 |
107 | ## 如何运行
108 |
109 | 1. 克隆代码
110 |
111 | ```
112 | $ git clone https://github.com/dyh/opencv_tools.git
113 | ```
114 |
115 | 2. 进入目录
116 |
117 | ```
118 | $ cd opencv_tools
119 | ```
120 |
121 | 3. 创建 python 虚拟环境
122 |
123 | ```
124 | $ python3 -m venv venv
125 | ```
126 |
127 | 4. 激活虚拟环境
128 |
129 | ```
130 | $ source venv/bin/activate
131 | ```
132 |
133 | 5. 升级pip
134 |
135 | ```
136 | $ python -m pip install --upgrade pip
137 | ```
138 |
139 | 6. 安装软件包
140 |
141 | ```
142 | $ pip install -r requirements.txt
143 | ```
144 |
145 | 7. 在 main.py 文件中,设置要处理的图片路径 file_path,例如
146 |
147 | ```
148 | file_path = './images/000000050145.jpg'
149 | ```
150 |
151 | 8. 运行程序
152 |
153 | ```
154 | python main.py
155 | ```
156 |
157 | 9. 程序将在 output 目录下输出60张图片
158 |
159 |
--------------------------------------------------------------------------------
/cover.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dyh/opencv_tools/89a33d55dc12ded2290000c52ddeabb61cf4dd40/cover.jpg
--------------------------------------------------------------------------------
/image_color.py:
--------------------------------------------------------------------------------
1 | import cv2 as cv
2 | import numpy as np
3 |
4 | from utils import plt_save
5 |
6 |
7 | def show_hsv(src):
8 | # 转换成HSV色彩空间
9 | hsv = cv.cvtColor(src=src, code=cv.COLOR_BGR2HSV)
10 |
11 | # 转回BGR
12 | # bgr = cv.cvtColor(src=hsv, code=cv.COLOR_HSV2BGR)
13 | # plt_save(bgr, 'BGR')
14 |
15 | h, s, v = cv.split(hsv)
16 |
17 | # 色度/色调
18 | plt_save(image=h, title='Hue')
19 | # 饱和度
20 | plt_save(image=s, title='Saturation')
21 | # 纯度/亮度
22 | plt_save(image=v, title='Value')
23 |
24 | # 固定色度h
25 | h_new = np.full_like(a=h, fill_value=255)
26 | merge = cv.merge([h_new, s, v])
27 | plt_save(merge, 'Fixed Hue')
28 |
29 | # 固定饱和度s
30 | s_new = np.full_like(a=s, fill_value=255)
31 | merge = cv.merge([h, s_new, v])
32 | plt_save(merge, 'Fixed Saturation')
33 |
34 | # 固定亮度v
35 | v_new = np.full_like(a=v, fill_value=255)
36 | merge = cv.merge([h, s, v_new])
37 | plt_save(merge, 'Fixed Value')
38 |
39 | # 固定色度h + 固定饱和度s
40 | merge = cv.merge([h_new, s_new, v])
41 | plt_save(merge, 'Fixed Hue & Saturation')
42 |
43 | # 固定色度h + 固定亮度v
44 | merge = cv.merge([h_new, s, v_new])
45 | plt_save(merge, 'Fixed Hue & Value')
46 |
47 | # 固定饱和度s + 固定亮度v
48 | merge = cv.merge([h, s_new, v_new])
49 | plt_save(merge, 'Fixed Saturation & Value')
50 |
--------------------------------------------------------------------------------
/image_enhancement.py:
--------------------------------------------------------------------------------
1 | import random
2 | import sys
3 |
4 | import cv2
5 | import numpy as np
6 |
7 | from utils import plt_save
8 |
9 |
10 | def show_enhancement(src):
11 | balance_img1 = white_balance(src)
12 | plt_save(balance_img1, 'White Balance 1')
13 |
14 | # 灰度世界算法
15 | balance_img2 = grey_world(src)
16 | plt_save(balance_img2, 'White Balance 2')
17 |
18 | # 直方图均衡化
19 | balance_img3 = his_equl_color(src)
20 | plt_save(balance_img3, 'White Balance 3')
21 |
22 | # 视网膜-大脑皮层(Retinex)增强算法
23 | # 单尺度Retinex
24 | ssr1 = single_scale_retinex(src, 300)
25 | ssr1[ssr1 < 0] = 0
26 | plt_save(ssr1, 'Single Scale Retinex 1')
27 |
28 | ssr2 = s_s_r(src)
29 | plt_save(ssr2, 'Single Scale Retinex 2')
30 |
31 | # 多尺度Retinex
32 | msr1 = multi_scale_retinex(src, [15, 80, 250])
33 | msr1[msr1 < 0] = 0
34 | plt_save(msr1, 'Multi Scale Retinex 1')
35 |
36 | msr2 = m_s_r(src, sigma_list=[15, 80, 250])
37 | plt_save(msr2, 'Multi Scale Retinex 2')
38 |
39 | msrcr1 = m_s_r_c_r(src, sigma_list=[15, 80, 250])
40 | plt_save(msrcr1, 'Multi Scale Retinex With Color Restoration 1')
41 |
42 | # 自动白平衡 AWB
43 | awb = automatic_white_balance(src)
44 | plt_save(awb, 'Automatic White Balance')
45 |
46 | # 自动色彩均衡 ACE
47 | balance_img4 = automatic_color_equalization(src)
48 | plt_save(balance_img4, 'Automatic Color Equalization')
49 |
50 |
51 | def white_balance(src):
52 | r, g, b = cv2.split(src)
53 | r_avg = cv2.mean(r)[0]
54 | g_avg = cv2.mean(g)[0]
55 | b_avg = cv2.mean(b)[0]
56 |
57 | # 求各个通道所占增益
58 | k = (r_avg + g_avg + b_avg) / 3
59 | kr = k / r_avg
60 | kg = k / g_avg
61 | kb = k / b_avg
62 |
63 | r = cv2.addWeighted(src1=r, alpha=kr, src2=0, beta=0, gamma=0)
64 | g = cv2.addWeighted(src1=g, alpha=kg, src2=0, beta=0, gamma=0)
65 | b = cv2.addWeighted(src1=b, alpha=kb, src2=0, beta=0, gamma=0)
66 |
67 | return cv2.merge([b, g, r])
68 |
69 |
70 | # 灰度世界算法
71 | def grey_world(nimg):
72 | nimg = nimg.transpose(2, 0, 1).astype(np.uint32)
73 | avg_b = np.average(nimg[0])
74 | avg_g = np.average(nimg[1])
75 | avg_r = np.average(nimg[2])
76 |
77 | avg = (avg_b + avg_g + avg_r) / 3
78 |
79 | nimg[0] = np.minimum(nimg[0] * (avg / avg_b), 255)
80 | nimg[1] = np.minimum(nimg[1] * (avg / avg_g), 255)
81 | nimg[2] = np.minimum(nimg[2] * (avg / avg_r), 255)
82 | return nimg.transpose(1, 2, 0).astype(np.uint8)
83 |
84 |
85 | # 直方图均衡化
86 | def his_equl_color(img):
87 | ycrcb = cv2.cvtColor(img, cv2.COLOR_BGR2YCR_CB)
88 | channels = cv2.split(ycrcb)
89 | cv2.equalizeHist(channels[0], channels[0]) # equalizeHist(in,out)
90 | cv2.merge(channels, ycrcb)
91 | img_eq = cv2.cvtColor(ycrcb, cv2.COLOR_YCR_CB2BGR)
92 | return img_eq
93 |
94 |
95 | # 视网膜-大脑皮层(Retinex)增强算法
96 | def single_scale_retinex(img, sigma):
97 | temp = cv2.GaussianBlur(img, (0, 0), sigma)
98 | gaussian = np.where(temp == 0, 0.01, temp)
99 | retinex = np.log10(img + 0.01) - np.log10(gaussian)
100 | return retinex
101 |
102 |
103 | def multi_scale_retinex(img, sigma_list):
104 | retinex = np.zeros_like(img * 1.0)
105 | for sigma in sigma_list:
106 | retinex = single_scale_retinex(img, sigma)
107 | retinex = retinex / len(sigma_list)
108 | return retinex
109 |
110 |
111 | def color_restoration(img, alpha, beta):
112 | img_sum = np.sum(img, axis=2, keepdims=True)
113 | color_restoration_temp = beta * (np.log10(alpha * img) - np.log10(img_sum))
114 | return color_restoration_temp
115 |
116 |
117 | def multi_scale_retinex_with_color_restoration(img, sigma_list, g, b, alpha, beta):
118 | img = np.float64(img) + 1.0
119 | img_retinex = multi_scale_retinex(img, sigma_list)
120 | img_color = color_restoration(img, alpha, beta)
121 | img_msrcr = g * (img_retinex * img_color + b)
122 | return img_msrcr
123 |
124 |
125 | def simplest_color_balance(img, low_clip, high_clip):
126 | total = img.shape[0] * img.shape[1]
127 |
128 | low_val = 0.0
129 | high_val = 0.0
130 |
131 | for i in range(img.shape[2]):
132 | unique, counts = np.unique(img[:, :, i], return_counts=True)
133 | current = 0
134 |
135 | for u, c in zip(unique, counts):
136 | if float(current) / total < low_clip:
137 | low_val = u
138 | if float(current) / total < high_clip:
139 | high_val = u
140 | current += c
141 | img[:, :, i] = np.maximum(np.minimum(img[:, :, i], high_val), low_val)
142 | return img
143 |
144 |
145 | def touint8(img):
146 | for i in range(img.shape[2]):
147 | img[:, :, i] = (img[:, :, i] - np.min(img[:, :, i])) / \
148 | (np.max(img[:, :, i]) - np.min(img[:, :, i])) * 255
149 | img = np.uint8(np.minimum(np.maximum(img, 0), 255))
150 | return img
151 |
152 |
153 | def s_s_r(img, sigma=300):
154 | ssr = single_scale_retinex(img, sigma)
155 | ssr = touint8(ssr)
156 | return ssr
157 |
158 |
159 | def m_s_r(img, sigma_list):
160 | if sigma_list is None:
161 | sigma_list = [15, 80, 250]
162 | msr = multi_scale_retinex(img, sigma_list)
163 | msr = touint8(msr)
164 | return msr
165 |
166 |
167 | def m_s_r_c_r(img, sigma_list, g=5, b=25, alpha=125, beta=46, low_clip=0.01, high_clip=0.99):
168 | if sigma_list is None:
169 | sigma_list = [15, 80, 250]
170 | msrcr = multi_scale_retinex_with_color_restoration(img, sigma_list, g, b, alpha, beta)
171 | msrcr = touint8(msrcr)
172 | msrcr = simplest_color_balance(msrcr, low_clip, high_clip)
173 | return msrcr
174 |
175 |
176 | # 自动白平衡(AWB)
177 | def automatic_white_balance(img):
178 | result = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
179 | avg_a = np.average(result[:, :, 1])
180 | avg_b = np.average(result[:, :, 2])
181 | for x in range(result.shape[0]):
182 | for y in range(result.shape[1]):
183 | l, a, b = result[x, y, :]
184 | # fix for CV correction
185 | l *= 100 / 255.0
186 | result[x, y, 1] = a - ((avg_a - 128) * (l / 100.0) * 1.1)
187 | result[x, y, 2] = b - ((avg_b - 128) * (l / 100.0) * 1.1)
188 | result = cv2.cvtColor(result, cv2.COLOR_LAB2BGR)
189 | return result
190 |
191 |
192 | # 自动色彩均衡(ACE)
193 | # 饱和函数
194 | def calc_saturation(diff, slope, limit):
195 | ret = diff * slope
196 | if ret > limit:
197 | ret = limit
198 | elif ret < (-limit):
199 | ret = -limit
200 | return ret
201 |
202 |
203 | def automatic_color_equalization(nimg, slope=10, limit=1000, samples=500):
204 | nimg = nimg.transpose(2, 0, 1)
205 |
206 | # Convert input to an ndarray with column-major memory order(仅仅是地址连续,内容和结构不变)
207 | nimg = np.ascontiguousarray(nimg, dtype=np.uint8)
208 |
209 | width = nimg.shape[2]
210 | height = nimg.shape[1]
211 |
212 | cary = []
213 |
214 | # 随机产生索引
215 | for i in range(0, samples):
216 | _x = random.randint(0, width) % width
217 | _y = random.randint(0, height) % height
218 |
219 | dict_temp = {"x": _x, "y": _y}
220 | cary.append(dict_temp)
221 | pass
222 |
223 | mat = np.zeros((3, height, width), float)
224 |
225 | r_max = sys.float_info.min
226 | r_min = sys.float_info.max
227 |
228 | g_max = sys.float_info.min
229 | g_min = sys.float_info.max
230 |
231 | b_max = sys.float_info.min
232 | b_min = sys.float_info.max
233 |
234 | for i in range(height):
235 | for j in range(width):
236 | r = nimg[0, i, j]
237 | g = nimg[1, i, j]
238 | b = nimg[2, i, j]
239 |
240 | r_rscore_sum = 0.0
241 | g_rscore_sum = 0.0
242 | b_rscore_sum = 0.0
243 | denominator = 0.0
244 |
245 | for _dict in cary:
246 | _x = _dict["x"] # width
247 | _y = _dict["y"] # height
248 |
249 | # 计算欧氏距离
250 | dist = np.sqrt(np.square(_x - j) + np.square(_y - i))
251 | if dist < height / 5:
252 | continue
253 |
254 | _sr = nimg[0, _y, _x]
255 | _sg = nimg[1, _y, _x]
256 | _sb = nimg[2, _y, _x]
257 |
258 | r_rscore_sum += calc_saturation(int(r) - int(_sr), slope, limit) / dist
259 | g_rscore_sum += calc_saturation(int(g) - int(_sg), slope, limit) / dist
260 | b_rscore_sum += calc_saturation(int(b) - int(_sb), slope, limit) / dist
261 |
262 | denominator += limit / dist
263 |
264 | r_rscore_sum = r_rscore_sum / denominator
265 | g_rscore_sum = g_rscore_sum / denominator
266 | b_rscore_sum = b_rscore_sum / denominator
267 |
268 | mat[0, i, j] = r_rscore_sum
269 | mat[1, i, j] = g_rscore_sum
270 | mat[2, i, j] = b_rscore_sum
271 |
272 | if r_max < r_rscore_sum:
273 | r_max = r_rscore_sum
274 | if r_min > r_rscore_sum:
275 | r_min = r_rscore_sum
276 |
277 | if g_max < g_rscore_sum:
278 | g_max = g_rscore_sum
279 | if g_min > g_rscore_sum:
280 | g_min = g_rscore_sum
281 |
282 | if b_max < b_rscore_sum:
283 | b_max = b_rscore_sum
284 | if b_min > b_rscore_sum:
285 | b_min = b_rscore_sum
286 |
287 | for i in range(height):
288 | for j in range(width):
289 | nimg[0, i, j] = (mat[0, i, j] - r_min) * 255 / (r_max - r_min)
290 | nimg[1, i, j] = (mat[1, i, j] - g_min) * 255 / (g_max - g_min)
291 | nimg[2, i, j] = (mat[2, i, j] - b_min) * 255 / (b_max - b_min)
292 |
293 | return nimg.transpose([1, 2, 0]).astype(np.uint8)
294 |
--------------------------------------------------------------------------------
/image_filtering.py:
--------------------------------------------------------------------------------
1 | import cv2
2 | import numpy as np
3 |
4 | from utils import plt_save
5 |
6 |
7 | def show_filtering(src):
8 | # 低通滤波
9 | # Blur the image with a mean filter
10 | blur = cv2.blur(src=src, ksize=(5, 5))
11 | plt_save(image=blur, title='Mean filtered (5x5)')
12 |
13 | # Blur the image with a mean filter 9x9
14 | blur = cv2.blur(src=src, ksize=(9, 9))
15 | plt_save(image=blur, title='Mean filtered (9x9)')
16 |
17 | blur = cv2.GaussianBlur(src=src, ksize=(9, 9), sigmaX=1.5)
18 | plt_save(image=blur, title='Gaussian filtered Image (9x9)')
19 |
20 | gauss = cv2.getGaussianKernel(ksize=9, sigma=1.5, ktype=cv2.CV_32F)
21 | print('GaussianKernel 1.5 = [', end='')
22 | for item in gauss:
23 | print(item, end='')
24 | pass
25 | print(']')
26 |
27 | gauss = cv2.getGaussianKernel(ksize=9, sigma=-1, ktype=cv2.CV_32F)
28 | print('GaussianKernel -1 = [', end='')
29 | for item in gauss:
30 | print(item, end='')
31 | pass
32 | print(']')
33 |
34 | # 缩减 采样
35 | blur = cv2.GaussianBlur(src=src, ksize=(11, 11), sigmaX=1.75)
36 | resized1 = cv2.resize(src=blur, dsize=(0, 0), fx=0.25, fy=0.25, interpolation=cv2.INTER_CUBIC)
37 | plt_save(image=resized1, title='resize CUBIC 0.25')
38 |
39 | # resizing with NN
40 | resized2 = cv2.resize(src=resized1, dsize=(0, 0), fx=4, fy=4, interpolation=cv2.INTER_NEAREST)
41 | plt_save(image=resized2, title='resize NEAREST x4')
42 |
43 | # resizing with bilinear
44 | resized3 = cv2.resize(src=resized1, dsize=(0, 0), fx=4, fy=4, interpolation=cv2.INTER_LINEAR)
45 | plt_save(image=resized3, title='resize LINEAR x4')
46 |
47 | # 中值滤波
48 | median_blur = cv2.medianBlur(src=src, ksize=5)
49 | plt_save(image=median_blur, title='Median filtered')
50 |
51 | # 定向滤波器
52 | image_gray = cv2.cvtColor(src=src, code=cv2.COLOR_BGR2GRAY)
53 |
54 | # Compute Sobel X derivative
55 | sobel_x = cv2.Sobel(src=image_gray, ddepth=cv2.CV_8U, dx=1, dy=0, ksize=3, scale=0.4, delta=128,
56 | borderType=cv2.BORDER_DEFAULT)
57 | plt_save(image=sobel_x, title='Sobel X')
58 |
59 | # Compute Sobel Y derivative
60 | sobel_y = cv2.Sobel(src=image_gray, ddepth=cv2.CV_8U, dx=0, dy=1, ksize=3, scale=0.4, delta=128,
61 | borderType=cv2.BORDER_DEFAULT)
62 | plt_save(image=sobel_y, title='Sobel Y')
63 |
64 | # Compute norm of Sobel
65 | sobel_x = cv2.Sobel(src=image_gray, ddepth=cv2.CV_16S, dx=1, dy=0)
66 | sobel_y = cv2.Sobel(src=image_gray, ddepth=cv2.CV_16S, dx=0, dy=1)
67 |
68 | sobel_1 = abs(sobel_x) + abs(sobel_y)
69 | plt_save(image=sobel_1, title='abs Sobel X+Y')
70 |
71 | sobel_2 = cv2.convertScaleAbs(sobel_x) + cv2.convertScaleAbs(sobel_y)
72 | plt_save(image=sobel_2, title='cv2.convertScaleAbs Sobel X+Y')
73 |
74 | # Compute Sobel X derivative (7x7)
75 | sobel_x_7x7 = cv2.Sobel(src=image_gray, ddepth=cv2.CV_8U, dx=1, dy=0, ksize=7, scale=0.001, delta=128)
76 | plt_save(image=sobel_x_7x7, title='Sobel X (7x7)')
77 |
78 | uint8_sobel_1 = np.uint8(sobel_1)
79 | plt_save(image=uint8_sobel_1, title='uint8 sobel_1')
80 |
81 | uint8_sobel_2 = np.uint8(sobel_2)
82 | plt_save(image=uint8_sobel_2, title='uint8 sobel_2')
83 |
84 | int8_sobel_1 = np.int8(sobel_1)
85 | plt_save(image=int8_sobel_1, title='int8 sobel_1')
86 |
87 | int8_sobel_2 = np.int8(sobel_2)
88 | plt_save(image=int8_sobel_2, title='int8 sobel_2')
89 |
90 | # Apply threshold to Sobel norm (low threshold value)
91 | _, thresh_binary = cv2.threshold(src=uint8_sobel_1, thresh=255, maxval=255, type=cv2.THRESH_BINARY)
92 | plt_save(image=thresh_binary, title='Binary Sobel (low) cv2.threshold uint8_sobel_1')
93 |
94 | _, thresh_binary = cv2.threshold(src=uint8_sobel_2, thresh=255, maxval=255, type=cv2.THRESH_BINARY)
95 | plt_save(image=thresh_binary, title='Binary Sobel (low) cv2.threshold uint8_sobel_2')
96 |
97 | # Apply threshold to Sobel norm (high threshold value)
98 | _, thresh_binary = cv2.threshold(src=uint8_sobel_1, thresh=190, maxval=255, type=cv2.THRESH_BINARY)
99 | plt_save(image=thresh_binary, title='Binary Sobel Image (high) cv2.threshold uint8_sobel_1')
100 |
101 | _, thresh_binary = cv2.threshold(src=uint8_sobel_2, thresh=190, maxval=255, type=cv2.THRESH_BINARY)
102 | plt_save(image=thresh_binary, title='Binary Sobel Image (high) cv2.threshold uint8_sobel_2')
103 |
104 | add_weighted_sobel = cv2.addWeighted(sobel_x, 0.5, sobel_y, 0.5, 0)
105 | plt_save(image=add_weighted_sobel, title='cv2.addWeighted abs')
106 |
107 | # down-sample and up-sample the image
108 | reduced = cv2.pyrDown(src=src)
109 | rescaled = cv2.pyrUp(src=reduced)
110 | plt_save(image=rescaled, title='Rescaled')
111 |
112 | # down-sample and up-sample the image
113 | reduced = cv2.pyrDown(src=image_gray)
114 | rescaled = cv2.pyrUp(src=reduced)
115 |
116 | w, h = src.shape[0:2]
117 | rescaled = cv2.resize(rescaled, (h, w))
118 |
119 | plt_save(image=rescaled, title='Rescaled')
120 |
121 | subtract = cv2.subtract(src1=rescaled, src2=image_gray)
122 | subtract = np.uint8(subtract)
123 | plt_save(image=subtract, title='cv2.subtract')
124 |
125 | gauss05 = cv2.GaussianBlur(src=image_gray, ksize=(0, 0), sigmaX=0.5)
126 | gauss15 = cv2.GaussianBlur(src=image_gray, ksize=(0, 0), sigmaX=1.5)
127 | subtract = cv2.subtract(src1=gauss15, src2=gauss05, dtype=cv2.CV_16S)
128 | subtract = np.uint8(subtract)
129 | plt_save(image=subtract, title='cv2.subtract gauss15 - gauss05')
130 |
131 | gauss20 = cv2.GaussianBlur(src=image_gray, ksize=(0, 0), sigmaX=2.0)
132 | gauss22 = cv2.GaussianBlur(src=image_gray, ksize=(0, 0), sigmaX=2.2)
133 | subtract = cv2.subtract(src1=gauss22, src2=gauss20, dtype=cv2.CV_32F)
134 | subtract = np.uint8(subtract)
135 | plt_save(image=subtract, title='cv2.subtract gauss22 - gauss20')
136 |
--------------------------------------------------------------------------------
/image_outline.py:
--------------------------------------------------------------------------------
1 | import cv2
2 | import numpy as np
3 |
4 | from utils import plt_save
5 |
6 |
7 | def show_outline(src):
8 | contours = cv2.Canny(image=src, threshold1=125, threshold2=350)
9 | plt_save(255 - contours, title='Canny Contours')
10 |
11 | image_gray = cv2.cvtColor(src=src, code=cv2.COLOR_BGR2GRAY)
12 |
13 | contours = cv2.Canny(image=image_gray, threshold1=125, threshold2=350)
14 | plt_save(255 - contours, title='Canny Contours Gray')
15 |
16 | # Hough tranform for line detection
17 | theta = np.pi / 180
18 | threshold = 50
19 | lines = cv2.HoughLinesP(image=contours, rho=1, theta=theta, threshold=threshold)
20 |
21 | if lines is not None:
22 | src_clone = src.copy()
23 |
24 | for line in lines:
25 | for x1, y1, x2, y2 in line:
26 | cv2.line(img=src_clone, pt1=(x1, y1), pt2=(x2, y2), color=(0, 255, 0), thickness=2)
27 | pass
28 | pass
29 |
30 | plt_save(src_clone, title='Lines with HoughP, threshold: ' + str(threshold))
31 | pass
32 |
33 | # Detect circles
34 | # blur = cv2.GaussianBlur(src=image_gray, ksize=(5, 5), sigmaX=1.5)
35 | threshold = 200
36 | min_votes = 100
37 |
38 | circles = cv2.HoughCircles(image=image_gray, method=cv2.HOUGH_GRADIENT, dp=2, minDist=20,
39 | param1=threshold, param2=min_votes, minRadius=15, maxRadius=50)
40 |
41 | if circles is not None:
42 | src_clone = src.copy()
43 | for circle in circles:
44 | for x1, y1, r in circle:
45 | cv2.circle(img=src_clone, center=(x1, y1), radius=int(r), color=(0, 255, 0), thickness=2)
46 | pass
47 |
48 | plt_save(src_clone, title='Circles with HoughP, threshold: ' + str(threshold) + ', min_votes=' + str(min_votes))
49 |
50 | pass
51 |
52 | # Get the contours
53 | src_clone = src.copy()
54 | contours, _ = cv2.findContours(image=image_gray, mode=cv2.RETR_LIST, method=cv2.CHAIN_APPROX_NONE)
55 | image_contours = cv2.drawContours(image=src_clone, contours=contours, contourIdx=-1, color=(255, 255, 255),
56 | thickness=2)
57 |
58 | plt_save(image_contours, title='Contours with RETR_LIST')
59 |
--------------------------------------------------------------------------------
/image_transformation.py:
--------------------------------------------------------------------------------
1 | import cv2
2 | import numpy as np
3 |
4 | from utils import plt_save
5 |
6 |
7 | def show_transformation(src):
8 | # 用形态学滤波器腐蚀和膨胀图像
9 | element_3x3 = np.ones((3, 3), np.uint8)
10 |
11 | # 腐蚀 3x3
12 | eroded = cv2.erode(src=src, kernel=element_3x3)
13 | plt_save(image=eroded, title='eroded')
14 |
15 | # 膨胀 3x3 3次
16 | dilated = cv2.dilate(src=src, kernel=element_3x3, iterations=3)
17 | plt_save(image=dilated, title='dilated 3 times')
18 |
19 | # 腐蚀 7x7
20 | element_7x7 = np.ones((7, 7), np.uint8)
21 | eroded_7x7 = cv2.erode(src=src, kernel=element_7x7, iterations=1)
22 | plt_save(image=eroded_7x7, title='eroded 7x7')
23 |
24 | # 腐蚀 3x3 3次
25 | eroded_3 = cv2.erode(src=src, kernel=element_3x3, iterations=3)
26 | plt_save(image=eroded_3, title='eroded 3 times')
27 |
28 | # 用形态学滤波器开启和闭合图像
29 | image_gray = cv2.cvtColor(src=src, code=cv2.COLOR_RGB2GRAY)
30 | # plt_save(image=image_gray, title='image_gray')
31 |
32 | # Close the image
33 | element_5x5 = np.ones((5, 5), np.uint8)
34 |
35 | closed = cv2.morphologyEx(src=image_gray, op=cv2.MORPH_CLOSE, kernel=element_5x5)
36 | plt_save(image=closed, title='closed')
37 |
38 | # Open the image
39 | opened = cv2.morphologyEx(src=image_gray, op=cv2.MORPH_OPEN, kernel=element_5x5)
40 | plt_save(image=opened, title='opened')
41 |
42 | closed = cv2.morphologyEx(src=image_gray, op=cv2.MORPH_CLOSE, kernel=element_5x5)
43 | closed_opened = cv2.morphologyEx(src=closed, op=cv2.MORPH_OPEN, kernel=element_5x5)
44 | plt_save(image=closed_opened, title='Closed -> Opened')
45 |
46 | opened = cv2.morphologyEx(src=image_gray, op=cv2.MORPH_OPEN, kernel=element_5x5)
47 | opened_closed = cv2.morphologyEx(src=opened, op=cv2.MORPH_CLOSE, kernel=element_5x5)
48 | plt_save(image=opened_closed, title='Opened -> Closed')
49 |
50 | # 在灰度图像中应用形态学运算
51 | edge = cv2.morphologyEx(src=image_gray, op=cv2.MORPH_GRADIENT, kernel=element_3x3)
52 | plt_save(image=255 - edge, title='Gradient | Edge')
53 |
54 | # Apply threshold to obtain a binary image
55 | threshold = 80
56 | _, thresh_binary = cv2.threshold(src=edge, thresh=threshold, maxval=255, type=cv2.THRESH_BINARY)
57 | plt_save(image=thresh_binary, title='Gradient | Edge -> Thresh Binary | Edge')
58 |
59 | # 7x7 Black Top-hat Image
60 | black_hat = cv2.morphologyEx(src=image_gray, op=cv2.MORPH_BLACKHAT, kernel=element_7x7)
61 | plt_save(image=255 - black_hat, title='7x7 Black Top-hat')
62 |
63 | # Apply threshold to obtain a binary image
64 | threshold = 25
65 | _, thresh_binary = cv2.threshold(src=black_hat, thresh=threshold, maxval=255, type=cv2.THRESH_BINARY)
66 | plt_save(image=255 - thresh_binary, title='7x7 Black Top-hat -> Thresh Binary | Edge')
67 |
68 | # Apply the black top-hat transform using a 7x7 structuring element
69 | closed = cv2.morphologyEx(src=thresh_binary, op=cv2.MORPH_CLOSE, kernel=element_7x7)
70 | plt_save(image=255 - closed, title='7x7 Black Top-hat -> Closed')
71 |
72 | pass
73 |
--------------------------------------------------------------------------------
/images/000000507081.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dyh/opencv_tools/89a33d55dc12ded2290000c52ddeabb61cf4dd40/images/000000507081.jpg
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | import cv2
2 |
3 | from image_filtering import show_filtering
4 | from image_outline import show_outline
5 | from image_transformation import show_transformation
6 | from utils import plt_save
7 | from image_color import show_hsv
8 | from image_enhancement import show_enhancement
9 |
10 | if __name__ == '__main__':
11 | file_path = './images/000000507081.jpg'
12 |
13 | origin = cv2.imread(file_path)
14 | origin = origin[:, :, [2, 1, 0]]
15 |
16 | # x, y = origin.shape[0:2]
17 | # origin = cv2.resize(origin, (int(y / 3), int(x / 3)))
18 | plt_save(image=origin, title='Origin')
19 |
20 | # --------------------图像色彩--------------------
21 | # 转换成HSV色彩空间
22 | show_hsv(origin)
23 |
24 | # --------------------图像变换--------------------
25 | show_transformation(origin)
26 |
27 | # --------------------图像过滤--------------------
28 | show_filtering(origin)
29 |
30 | # --------------------提取直线、轮廓、区域--------------------
31 | show_outline(origin)
32 |
33 | # -------------------- 图像增强-白平衡等--------------------
34 | show_enhancement(origin)
35 |
36 | print('done')
37 |
--------------------------------------------------------------------------------
/output/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dyh/opencv_tools/89a33d55dc12ded2290000c52ddeabb61cf4dd40/output/.gitignore
--------------------------------------------------------------------------------
/push.sh:
--------------------------------------------------------------------------------
1 | echo "#################### config global user name & email ####################"
2 | git config --global user.email "1358366+dyh@users.noreply.github.com"
3 | git config --global user.name "dyh"
4 |
5 | echo "#################### git add . ####################"
6 | git add .
7 |
8 | echo "#################### git pull ####################"
9 | git pull
10 |
11 | echo "#################### git commit -m \"unbox\" ####################"
12 | git commit -m "unbox"
13 |
14 | echo "#################### git push -u origin main ####################"
15 | git push -u origin main
16 |
17 | echo "#################### done ####################"
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | Pillow==8.0.1
2 | numpy==1.19.4
3 | opencv-python==4.4.0.46
4 | six==1.15.0
5 | matplotlib==3.3.3
6 | cycler==0.10.0
7 | kiwisolver==1.3.1
8 | pkg-resources==0.0.0
9 | pyparsing==2.4.7
10 | python-dateutil==2.8.1
11 |
--------------------------------------------------------------------------------
/utils.py:
--------------------------------------------------------------------------------
1 | import matplotlib.pyplot as plt
2 |
3 |
4 | def plt_save(image, title=''):
5 | plt.imshow(X=image, cmap='gray')
6 | plt.title(title)
7 | file_path = './output/' + title + '.png'
8 | plt.savefig(file_path)
9 | print(file_path)
10 | pass
11 |
12 |
13 | def plt_show(image, title=''):
14 | plt.imshow(X=image, cmap='gray')
15 | plt.title(title)
16 | plt.show()
17 |
--------------------------------------------------------------------------------