├── .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 | 17 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 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 | 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 | [![bilibili](https://github.com/dyh/opencv_tools/blob/main/cover.jpg?raw=true)](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 | --------------------------------------------------------------------------------