简 介: 本文首先讨论了卷积核的概念,以及如何用于对图像进行滤波。然后通过他们对图像进行数学运算来实现特定的效果,比如平和和锐化。展示了如何在OpenCV中实现2D滤波。 在等同卷积卷积核之后,我们创建了更多定制的核,用在OpenCV中的 filter2D()函数中。 介绍了OpenCV中的重要内置函数MediaBlur(),GaussianBlur()。 最后展示了 OpenCV中的 bilateralFilter()函数,是如何在保留图片中清晰边缘的同时又平滑了图像。

关键词中值滤波平滑滤波图像滤波

#mermaid-svg-b8zZwXRwZKNnR3Y4 .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-b8zZwXRwZKNnR3Y4 .label text{fill:#333}#mermaid-svg-b8zZwXRwZKNnR3Y4 .node rect,#mermaid-svg-b8zZwXRwZKNnR3Y4 .node circle,#mermaid-svg-b8zZwXRwZKNnR3Y4 .node ellipse,#mermaid-svg-b8zZwXRwZKNnR3Y4 .node polygon,#mermaid-svg-b8zZwXRwZKNnR3Y4 .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-b8zZwXRwZKNnR3Y4 .node .label{text-align:center;fill:#333}#mermaid-svg-b8zZwXRwZKNnR3Y4 .node.clickable{cursor:pointer}#mermaid-svg-b8zZwXRwZKNnR3Y4 .arrowheadPath{fill:#333}#mermaid-svg-b8zZwXRwZKNnR3Y4 .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-b8zZwXRwZKNnR3Y4 .flowchart-link{stroke:#333;fill:none}#mermaid-svg-b8zZwXRwZKNnR3Y4 .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-b8zZwXRwZKNnR3Y4 .edgeLabel rect{opacity:0.9}#mermaid-svg-b8zZwXRwZKNnR3Y4 .edgeLabel span{color:#333}#mermaid-svg-b8zZwXRwZKNnR3Y4 .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-b8zZwXRwZKNnR3Y4 .cluster text{fill:#333}#mermaid-svg-b8zZwXRwZKNnR3Y4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-b8zZwXRwZKNnR3Y4 .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-b8zZwXRwZKNnR3Y4 text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-b8zZwXRwZKNnR3Y4 .actor-line{stroke:grey}#mermaid-svg-b8zZwXRwZKNnR3Y4 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-b8zZwXRwZKNnR3Y4 .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-b8zZwXRwZKNnR3Y4 #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-b8zZwXRwZKNnR3Y4 .sequenceNumber{fill:#fff}#mermaid-svg-b8zZwXRwZKNnR3Y4 #sequencenumber{fill:#333}#mermaid-svg-b8zZwXRwZKNnR3Y4 #crosshead path{fill:#333;stroke:#333}#mermaid-svg-b8zZwXRwZKNnR3Y4 .messageText{fill:#333;stroke:#333}#mermaid-svg-b8zZwXRwZKNnR3Y4 .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-b8zZwXRwZKNnR3Y4 .labelText,#mermaid-svg-b8zZwXRwZKNnR3Y4 .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-b8zZwXRwZKNnR3Y4 .loopText,#mermaid-svg-b8zZwXRwZKNnR3Y4 .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-b8zZwXRwZKNnR3Y4 .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-b8zZwXRwZKNnR3Y4 .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-b8zZwXRwZKNnR3Y4 .noteText,#mermaid-svg-b8zZwXRwZKNnR3Y4 .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-b8zZwXRwZKNnR3Y4 .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-b8zZwXRwZKNnR3Y4 .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-b8zZwXRwZKNnR3Y4 .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-b8zZwXRwZKNnR3Y4 .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-b8zZwXRwZKNnR3Y4 .section{stroke:none;opacity:0.2}#mermaid-svg-b8zZwXRwZKNnR3Y4 .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-b8zZwXRwZKNnR3Y4 .section2{fill:#fff400}#mermaid-svg-b8zZwXRwZKNnR3Y4 .section1,#mermaid-svg-b8zZwXRwZKNnR3Y4 .section3{fill:#fff;opacity:0.2}#mermaid-svg-b8zZwXRwZKNnR3Y4 .sectionTitle0{fill:#333}#mermaid-svg-b8zZwXRwZKNnR3Y4 .sectionTitle1{fill:#333}#mermaid-svg-b8zZwXRwZKNnR3Y4 .sectionTitle2{fill:#333}#mermaid-svg-b8zZwXRwZKNnR3Y4 .sectionTitle3{fill:#333}#mermaid-svg-b8zZwXRwZKNnR3Y4 .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-b8zZwXRwZKNnR3Y4 .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-b8zZwXRwZKNnR3Y4 .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-b8zZwXRwZKNnR3Y4 .grid path{stroke-width:0}#mermaid-svg-b8zZwXRwZKNnR3Y4 .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-b8zZwXRwZKNnR3Y4 .task{stroke-width:2}#mermaid-svg-b8zZwXRwZKNnR3Y4 .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-b8zZwXRwZKNnR3Y4 .taskText:not([font-size]){font-size:11px}#mermaid-svg-b8zZwXRwZKNnR3Y4 .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-b8zZwXRwZKNnR3Y4 .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-b8zZwXRwZKNnR3Y4 .task.clickable{cursor:pointer}#mermaid-svg-b8zZwXRwZKNnR3Y4 .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-b8zZwXRwZKNnR3Y4 .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-b8zZwXRwZKNnR3Y4 .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-b8zZwXRwZKNnR3Y4 .taskText0,#mermaid-svg-b8zZwXRwZKNnR3Y4 .taskText1,#mermaid-svg-b8zZwXRwZKNnR3Y4 .taskText2,#mermaid-svg-b8zZwXRwZKNnR3Y4 .taskText3{fill:#fff}#mermaid-svg-b8zZwXRwZKNnR3Y4 .task0,#mermaid-svg-b8zZwXRwZKNnR3Y4 .task1,#mermaid-svg-b8zZwXRwZKNnR3Y4 .task2,#mermaid-svg-b8zZwXRwZKNnR3Y4 .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-b8zZwXRwZKNnR3Y4 .taskTextOutside0,#mermaid-svg-b8zZwXRwZKNnR3Y4 .taskTextOutside2{fill:#000}#mermaid-svg-b8zZwXRwZKNnR3Y4 .taskTextOutside1,#mermaid-svg-b8zZwXRwZKNnR3Y4 .taskTextOutside3{fill:#000}#mermaid-svg-b8zZwXRwZKNnR3Y4 .active0,#mermaid-svg-b8zZwXRwZKNnR3Y4 .active1,#mermaid-svg-b8zZwXRwZKNnR3Y4 .active2,#mermaid-svg-b8zZwXRwZKNnR3Y4 .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-b8zZwXRwZKNnR3Y4 .activeText0,#mermaid-svg-b8zZwXRwZKNnR3Y4 .activeText1,#mermaid-svg-b8zZwXRwZKNnR3Y4 .activeText2,#mermaid-svg-b8zZwXRwZKNnR3Y4 .activeText3{fill:#000 !important}#mermaid-svg-b8zZwXRwZKNnR3Y4 .done0,#mermaid-svg-b8zZwXRwZKNnR3Y4 .done1,#mermaid-svg-b8zZwXRwZKNnR3Y4 .done2,#mermaid-svg-b8zZwXRwZKNnR3Y4 .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-b8zZwXRwZKNnR3Y4 .doneText0,#mermaid-svg-b8zZwXRwZKNnR3Y4 .doneText1,#mermaid-svg-b8zZwXRwZKNnR3Y4 .doneText2,#mermaid-svg-b8zZwXRwZKNnR3Y4 .doneText3{fill:#000 !important}#mermaid-svg-b8zZwXRwZKNnR3Y4 .crit0,#mermaid-svg-b8zZwXRwZKNnR3Y4 .crit1,#mermaid-svg-b8zZwXRwZKNnR3Y4 .crit2,#mermaid-svg-b8zZwXRwZKNnR3Y4 .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-b8zZwXRwZKNnR3Y4 .activeCrit0,#mermaid-svg-b8zZwXRwZKNnR3Y4 .activeCrit1,#mermaid-svg-b8zZwXRwZKNnR3Y4 .activeCrit2,#mermaid-svg-b8zZwXRwZKNnR3Y4 .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-b8zZwXRwZKNnR3Y4 .doneCrit0,#mermaid-svg-b8zZwXRwZKNnR3Y4 .doneCrit1,#mermaid-svg-b8zZwXRwZKNnR3Y4 .doneCrit2,#mermaid-svg-b8zZwXRwZKNnR3Y4 .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-b8zZwXRwZKNnR3Y4 .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-b8zZwXRwZKNnR3Y4 .milestoneText{font-style:italic}#mermaid-svg-b8zZwXRwZKNnR3Y4 .doneCritText0,#mermaid-svg-b8zZwXRwZKNnR3Y4 .doneCritText1,#mermaid-svg-b8zZwXRwZKNnR3Y4 .doneCritText2,#mermaid-svg-b8zZwXRwZKNnR3Y4 .doneCritText3{fill:#000 !important}#mermaid-svg-b8zZwXRwZKNnR3Y4 .activeCritText0,#mermaid-svg-b8zZwXRwZKNnR3Y4 .activeCritText1,#mermaid-svg-b8zZwXRwZKNnR3Y4 .activeCritText2,#mermaid-svg-b8zZwXRwZKNnR3Y4 .activeCritText3{fill:#000 !important}#mermaid-svg-b8zZwXRwZKNnR3Y4 .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-b8zZwXRwZKNnR3Y4 g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-b8zZwXRwZKNnR3Y4 g.classGroup text .title{font-weight:bolder}#mermaid-svg-b8zZwXRwZKNnR3Y4 g.clickable{cursor:pointer}#mermaid-svg-b8zZwXRwZKNnR3Y4 g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-b8zZwXRwZKNnR3Y4 g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-b8zZwXRwZKNnR3Y4 .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-b8zZwXRwZKNnR3Y4 .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-b8zZwXRwZKNnR3Y4 .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-b8zZwXRwZKNnR3Y4 .dashed-line{stroke-dasharray:3}#mermaid-svg-b8zZwXRwZKNnR3Y4 #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-b8zZwXRwZKNnR3Y4 #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-b8zZwXRwZKNnR3Y4 #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-b8zZwXRwZKNnR3Y4 #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-b8zZwXRwZKNnR3Y4 #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-b8zZwXRwZKNnR3Y4 #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-b8zZwXRwZKNnR3Y4 #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-b8zZwXRwZKNnR3Y4 #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-b8zZwXRwZKNnR3Y4 .commit-id,#mermaid-svg-b8zZwXRwZKNnR3Y4 .commit-msg,#mermaid-svg-b8zZwXRwZKNnR3Y4 .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-b8zZwXRwZKNnR3Y4 .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-b8zZwXRwZKNnR3Y4 .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-b8zZwXRwZKNnR3Y4 g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-b8zZwXRwZKNnR3Y4 g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-b8zZwXRwZKNnR3Y4 g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-b8zZwXRwZKNnR3Y4 g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-b8zZwXRwZKNnR3Y4 g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-b8zZwXRwZKNnR3Y4 g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-b8zZwXRwZKNnR3Y4 .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-b8zZwXRwZKNnR3Y4 .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-b8zZwXRwZKNnR3Y4 .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-b8zZwXRwZKNnR3Y4 .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-b8zZwXRwZKNnR3Y4 .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-b8zZwXRwZKNnR3Y4 .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-b8zZwXRwZKNnR3Y4 .edgeLabel text{fill:#333}#mermaid-svg-b8zZwXRwZKNnR3Y4 .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-b8zZwXRwZKNnR3Y4 .node circle.state-start{fill:black;stroke:black}#mermaid-svg-b8zZwXRwZKNnR3Y4 .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-b8zZwXRwZKNnR3Y4 #statediagram-barbEnd{fill:#9370db}#mermaid-svg-b8zZwXRwZKNnR3Y4 .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-b8zZwXRwZKNnR3Y4 .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-b8zZwXRwZKNnR3Y4 .statediagram-state .divider{stroke:#9370db}#mermaid-svg-b8zZwXRwZKNnR3Y4 .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-b8zZwXRwZKNnR3Y4 .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-b8zZwXRwZKNnR3Y4 .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-b8zZwXRwZKNnR3Y4 .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-b8zZwXRwZKNnR3Y4 .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-b8zZwXRwZKNnR3Y4 .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-b8zZwXRwZKNnR3Y4 .note-edge{stroke-dasharray:5}#mermaid-svg-b8zZwXRwZKNnR3Y4 .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-b8zZwXRwZKNnR3Y4 .error-icon{fill:#522}#mermaid-svg-b8zZwXRwZKNnR3Y4 .error-text{fill:#522;stroke:#522}#mermaid-svg-b8zZwXRwZKNnR3Y4 .edge-thickness-normal{stroke-width:2px}#mermaid-svg-b8zZwXRwZKNnR3Y4 .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-b8zZwXRwZKNnR3Y4 .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-b8zZwXRwZKNnR3Y4 .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-b8zZwXRwZKNnR3Y4 .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-b8zZwXRwZKNnR3Y4 .marker{fill:#333}#mermaid-svg-b8zZwXRwZKNnR3Y4 .marker.cross{stroke:#333}:root { --mermaid-font-family: "trebuchet ms", verdana, arial;}#mermaid-svg-b8zZwXRwZKNnR3Y4 {color: rgba(0, 0, 0, 0.75);font: ;}

前 言
目 录
Contents
实现代码
卷积核
什么是卷积核?
如何利用卷积核来
锐化或者平滑图像?
教程中的实验
图像卷积
在OpenCV中
使用同等核
利用定制的2D卷积
核来模糊化图像
利用OpenCV内置
函数来平滑图像
在OpenCV中使用高斯
核对于图像进行平滑
使用OpenCV中的中
值滤波来处理图像
利用定制2D卷
积来锐化图像
双边滤波器
滤波总结

本文是根据 Image Filtering Using Convolution in OpenCV 中的内容进行整理,以备今后的学习和应用。

§00 前  言

  你是否在Photoshop中,或者在手机应用的帮助下对图片进行模糊化或者清晰化处理过?如果是,实际上你已经是用过卷积核。这里,我们解释如何通过卷积在OpenCV中对于图像进行滤波。

  你将会使用2D卷积以及OpenCV中计算机视觉库对图片进行不同的模糊化和锐化。我们将会通过Python,C++程序展示如何实现这些技术。

0.1 实现代码

  首先看一下下面的图像滤波代码。后面会对代码的每一行进行讨论,你可以对他们更加了解。

  • Python
import cv2
import numpy as npimage = cv2.imread('test.jpg')# Print error message if image is null
if image is None:print('Could not read image')# Apply identity kernel
kernel1 = np.array([[0, 0, 0],[0, 1, 0],[0, 0, 0]])identity = cv2.filter2D(src=image, ddepth=-1, kernel=kernel1)cv2.imshow('Original', image)
cv2.imshow('Identity', identity)cv2.waitKey()
cv2.imwrite('identity.jpg', identity)
cv2.destroyAllWindows()# Apply blurring kernel
kernel2 = np.ones((5, 5), np.float32) / 25
img = cv2.filter2D(src=image, ddepth=-1, kernel=kernel2)cv2.imshow('Original', image)
cv2.imshow('Kernel Blur', img)cv2.waitKey()
cv2.imwrite('blur_kernel.jpg', img)
cv2.destroyAllWindows()
  • C++
// Import dependencies
#include <opencv2/opencv.hpp>
#include <iostream>// Using namespaces to nullify use of c::function(); syntax and std::function(); syntax
using namespace std;
using namespace cv;int main()
{// Read ImageMat image = imread("test.jpg");// Print Error message if image is nullif (image.empty()) {cout << "Could not read image" << endl;}// Apply identity filter using kernelMat kernel1 = (Mat_<double>(3,3) << 0, 0, 0, 0, 1, 0, 0, 0, 0);Mat identity; filter2D(image, identity, -1 , kernel1, Point(-1, -1), 0, 4);imshow("Original", image);imshow("Identity", identity);waitKey();imwrite("identity.jpg", identity);destroyAllWindows();// Blurred using kernel// Initialize matrix with all onesMat kernel2 = Mat::ones(5,5, CV_64F);// Normalize the elementskernel2 = kernel2 / 25;Mat img;filter2D(image, img, -1 , kernel2, Point(-1, -1), 0, 4);imshow("Original", image);imshow("Kernel blur", img);imwrite("blur_kernel.jpg", img);waitKey();destroyAllWindows();
}

§01 卷积核


1.1 什么是卷积核?

  在图像处理中一个卷积核就是一个用于图像滤波的二维矩阵。被称为卷积矩阵的卷积核通常是方针,即M×N矩阵,其中M,N都是相同的奇数(比如,3×3,5×5,7×7)。下面是一个3×3的平滑矩阵示例。

  这就是一个3×3的2D卷积核。

  这样的卷积核可以用于图像中每个像素执行数据操作来实现所需效果(比如平滑,或者锐化一个图像)。但是为什么你希望对一个图像进行模糊化?这里有两个重要的原因:

  • 模糊化可以减少图像特定的噪声。因为这个原因,模糊化也常被称为平滑;
  • 将容易分神的背景去除,你会有意图像中部分背景,就像移动相机在“肖像”模式下的拍摄效果。
      作为计算机视觉的基本处理技术,利用卷积核进行图像滤波具有广泛的应用。

1.2 如何利用卷积核来锐化或者平滑图像?

  通过吧卷积核与图像进行卷积来实现对原图进行滤波。简单说通过卷积核来卷积一个图像代表着一个在卷积核与对应的图像像素之间的数学运算:

  • 假定卷积核的中心位于图像中待处理的像素 p(x,y)p\left( {x,y} \right)p(x,y) 上面;
  • 将核的每个元素与源图像中对应的像素相乘;
  • 现在把这些乘积进行相加,取平均;
  • 最后,加计算的平均数值作为输出图片对应的位置的数值。

  当你对于源图像中每个像素,都利用上面的3×3的核进行处理之后,你便得到一个模糊化的图像。这是因为这个核对应的卷积运算具有平均的效果,它可以对图像进行平滑、模糊化。你自己很快就会了解卷积核的每个元素都代表着自然滤波,因此你也可以实现锐化的效果。这个概念简单而强大,可以应用在大量图像处理流水线过程中。

  现在你了解了卷积核的用法,下面让我们看看如何在OpenCV中 实现卷积。

1.3 教程中的实验

  你可以点击 这个链接 访问Colab Notebook 访问这个教程,通过它你可以运行所有的实验,而不必在你的计算机中设置相关环境。

▲ 图1.3.1 用于实验的样例图片

  输入样例图片,我们将在后面的核操作中使用这个图片。

§02 图像卷积


2.1 在OpenCV中使用同等核

  • Python
import cv2
import numpy as np
  • C++
// Import dependencies
#include <opencv2/opencv.hpp>
#include <iostream>// Using namespaces to nullify use of c::function(); syntax and std::function(); syntax
using namespace std;
using namespace cv;

  在刻画如何实现图像的模糊和锐化之前,我们先讲讲关于同等核。 单位核是一个仿真,中间元素为1,其余都是0,。下面就是一个同等卷积矩阵:

  这是一个3×3的同等卷积核。

  同等卷积核的特点就是任何与其卷积的图像所得到的结构都与原图相同。下面我们展示一下如何使用OpenCV的函数使用这个同等核。在第一个例子中,我们适用上面的同等核展示滤波之后的结果与原始图像相同。

  首先输入OpenCV,NumPy软件包。

  • Python
import cv2
import numpy as np
  • C++
// Import dependencies
#include <opencv2/opencv.hpp>
#include <iostream>// Using namespaces to nullify use of c::function(); syntax and std::function(); syntax
using namespace std;
using namespace cv;

  接下来的代码中将执行如下操作:

  • 读入测试图像;
  • 定义同等卷积核,使用3×3的Numpy矩阵;
  • 使用OpenCV中的filter2D()函数完成线性滤波操作;
  • 利用imshow() 显示 原始和滤波后的图像;
  • 利用imwrite()来存储滤波后的图像;

filter2D(src, ddepth, kernel)

  函数 filter2d() 需要三个输入参数:

  • 第一个参数是原始图像;
  • 迪若个参数是 ddepth,代表结果图像的深度。 -1 表示结果图像的深度与原始图像相同;
  • 最后输入的参数是卷积核,它将被作用的输入图像中;

  下面是Python和C++的实现代码:

  • Python
image = cv2.imread('test.jpg')
"""
Apply identity kernel
"""
kernel1 = np.array([[0, 0, 0],[0, 1, 0],[0, 0, 0]])
# filter2D() function can be used to apply kernel to an image.
# Where ddepth is the desired depth of final image. ddepth is -1 if...
# ... depth is same as original or source image.
identity = cv2.filter2D(src=image, ddepth=-1, kernel=kernel1)# We should get the same image
cv2.imshow('Original', image)
cv2.imshow('Identity', identity)cv2.waitKey()
cv2.imwrite('identity.jpg', identity)
cv2.destroyAllWindows()
  • C++
// Apply identity filter using kernel
Mat kernel1 = (Mat_<double>(3,3) << 0, 0, 0, 0, 1, 0, 0, 0, 0);
Mat identity;
filter2D(image, identity, -1 , kernel1, Point(-1, -1), 0, 4);
imshow("Original", image);
imshow("Identity", identity);
waitKey();
imwrite("identity.jpg", identity);
destroyAllWindows();

  下面图像就是滤波后的图像(右边),可以看到它与原始图像(左边)是一样的。

▲ 图1.4.1 原始图像与滤波后的图像

2.2 利用定制的2D卷积核来模糊化图像

  下面我们展示如何把一个图像进行平滑。 我们再次定义一个定制的卷积核,然后利用OpenCV中的 filter2D() 函数在对 原始图像进行滤波。

  首先定义一个仅具有1元素的5×5的矩阵。注意它也被除以25。为什么呢?那好,你在对图像进行2D矩阵卷积之前你需要保证所有的取值都归一化。这通过将卷积核的元素都除以卷积核元素的累加和来实现。本示例中,就是将卷积核除以25.这保证了输出图像的元素取值 [0,1]\left[ {0,1} \right][0,1] 范围之内。

  利用 filter2D() 来对图像进行滤波。正如你所见,filter2D可以使用任何用户定义的卷积核来对图像进行卷积。

  • Python
"""
Apply blurring kernel
"""
kernel2 = np.ones((5, 5), np.float32) / 25
img = cv2.filter2D(src=image, ddepth=-1, kernel=kernel2)cv2.imshow('Original', image)
cv2.imshow('Kernel Blur', img)cv2.waitKey()
cv2.imwrite('blur_kernel.jpg', img)
cv2.destroyAllWindows()
  • C++
// Blurred using kernel
// Initialize matrix with all ones
Mat kernel2 = Mat::ones(5,5, CV_64F);// Normalize the elements
kernel2 = kernel2 / 25;
Mat img;
filter2D(image, img, -1 , kernel2, Point(-1, -1), 0, 4);
imshow("Original", image);
imshow("Kernel blur", img);
imwrite("blur_kernel.jpg", img);
waitKey();
destroyAllWindows();

  对比一下下面给出的结果。可以注意到输出图像(右边)相比于原始图像(左边)已经被模糊化了。

▲ 图2.2.1 平滑后的图像

2.3 利用OpenCV内置函数来平滑图像

  在OpenCV中 有一个内置 blur() 函数,专门用于对图像进行模糊化的。重要的是在使用它对图像进行平滑过程中你不必定义卷积核,而只是声明特定的卷积的的尺寸,通过 ksize 的输入参数传递给 blur() 函数。下面代码示例给出的了具体过程。blur() 函数自动会建立一个5×5的模糊卷积核应用到输入图像中。

  下面的示例中使用 blur() 函数就会产生与前面 filter2D() 相同的结果。

  • Python
"""
Apply blur using `blur()` function
"""
img_blur = cv2.blur(src=image, ksize=(5,5)) # Using the blur function to blur an image where ksize is the kernel size# Display using cv2.imshow()
cv2.imshow('Original', image)
cv2.imshow('Blurred', img_blur)cv2.waitKey()
cv2.imwrite('blur.jpg', img_blur)
cv2.destroyAllWindows()
  • C++
// Blurred using OpenCV C++ blur() function
Mat img_blur;
blur(image, img_blur, Size(5,5));
imshow("Original", image);
imshow("Blurred", img_blur);
imwrite("blur.jpg", img_blur);
waitKey();
destroyAllWindows();

2.4 在OpenCV中使用高斯核对于图像进行平滑

  我们下面利用OpenCV中的高斯函数来平滑图像。 在这个算法中使用高斯滤波器,它执行一个加权平均,与前面示例均匀平均不同。这种情况下,高斯平滑算法对于像素的加权值取决于它们距离核中心的距离。距离核中心越远的像素对于加权平均结果的影响与越小。下面的代码是踹死OpenCV中GaussianBlue() 函数卷积输入图像。

GaussianBlur(src, ksize, sigmaX[, dst[, sigmaY[, borderType]]])

  函数 GaussianBlue() 有四个输入参数:

  • 第一个参数,src,指明需要滤波的输入图像;

  • 第二个参数为 ksize,定义了高斯核的尺寸。这里我们使用5×5的高斯核;

  • 最后两个参数为 sigmaX, sigmaY都设置为0.这是高斯核在X(水平)和Y(垂直)方向上的标准方差。缺省的sigmaY的取值为0。 如果你讲sigmaX设置为0, 标准方差则通过核的尺寸(宽和高)分别进行计算。你也可以明确 设置他们为一个大于零的数值;

  • Python

"""
Apply Gaussian blur
"""
# sigmaX is Gaussian Kernel standard deviation
# ksize is kernel size
gaussian_blur = cv2.GaussianBlur(src=image, ksize=(5,5), \\
sigmaX=0, sigmaY=0)cv2.imshow('Original', image)
cv2.imshow('Gaussian Blurred', gaussian_blur)cv2.waitKey()
cv2.imwrite('gaussian_blur.jpg', gaussian_blur)
cv2.destroyAllWindows()
  • C++
// Performing Gaussian Blur
Mat gaussian_blur;
GaussianBlur(image, gaussian_blur, Size(5,5), SigmaX=0, SigmaY=0);
imshow("Original", image);
imshow("Gaussian Blurred", gaussian_blur);
imwrite("gaussian_blur.jpg", gaussian_blur);
waitKey();
destroyAllWindows();

  下面给出的结果,你可以看出输出结果(右边)仅仅比输入图像(左边)稍微模糊了一些。

▲ 图2.4.1 高斯平滑后的结果

2.5 使用OpenCV中的中值滤波来处理图像

  你可以通过OpenCV中的 medianBlur() 函数来对图像进行中值滤波。在中值滤波中,原始图像中的每个像素都被在卷积核尺寸范围内中的像素中间取值(注意:不是平均值)来取代。

medianBlur(src, ksize)

  这个函数有两个 输入参数:

  • 第一个参数是原始图像;

  • 第二个是核的尺寸,必须取值为奇数,正整数;

  • Python

"""
Apply Median blur
"""
# medianBlur() is used to apply Median blur to image
# ksize is the kernel size
median = cv2.medianBlur(src=image, ksize=5)cv2.imshow('Original', image)
cv2.imshow('Median Blurred', median)cv2.waitKey()
cv2.imwrite('median_blur.jpg', median)
cv2.destroyAllWindows()
  • C++
// Apply Median Blur
Mat median_blurred;
medianBlur(image, median_blurred, (5,5));
imshow("Original", image);
imshow("Median Blurred", median_blurred);
imwrite("median_blur.jpg", median_blurred);
waitKey();
destroyAllWindows();

  下面是中值滤波的结果。可以发现在相同的卷积核的大小下,中值滤波的结果相比于高斯滤波保留了更多重要的图像元素。中值滤波通常用语减少图像中的椒盐噪声。

▲ 图2.5.1 中值滤波的结果

2.6 利用定制2D卷积来锐化图像

  你可以通过2D卷积核来锐化图像。首先定制一个2D卷积核,然后使用 filter2D()来对图像进行卷积。

  下面的代码中,3×3的卷积核定义了锐化和。查看这个资源来了解更多的常用卷积核。

  • Python
"""
Apply sharpening using kernel
"""
kernel3 = np.array([[0, -1,  0],[-1,  5, -1],[0, -1,  0]])
sharp_img = cv2.filter2D(src=image, ddepth=-1, kernel=kernel3)cv2.imshow('Original', image)
cv2.imshow('Sharpened', sharp_img)cv2.waitKey()
cv2.imwrite('sharp_image.jpg', sharp_img)
cv2.destroyAllWindows()
  • C++
// Apply sharpening using kernel
Mat sharp_img;
Mat kernel3 = (Mat_<double>(3,3) << 0, -1,  0, -1,  5, -1, 0, -1, 0);
filter2D(image, sharp_img, -1 , kernel3, Point(-1, -1), 0, BORDER_DEFAULT);
imshow("Original", image);
imshow("Sharpenned", sharp_img);
imwrite("sharp_image.jpg", sharp_img);
waitKey();
destroyAllWindows();

  下面看看我们得到了什么。下面给出的结果可以看出锐化的效果还是非常明显的。右边锐化的图像将木头中的裂纹凸显,很多都是在左边被忽略的。

▲ 图2.6.1 图像锐化的结果

2.7 双边滤波器

  平滑可以有效除去 图像中的噪声,但有时我们并不想吧整个图像都进行模糊化,这样会使得重要的边缘信息丢失。在这种情况下, 双边滤波器也许可以让你的生活更加轻松。

  • 这种技术对相邻亮度值相似的像素选择性的执行滤波,可以保留边缘的锋利。
  • 你可以控制滤波空间尺寸,也可以定义在滤波输出相邻像素的相似程度。这需要根据他发明的颜色强度变化情况以及距离滤波像素的距离来确定。

  双边滤波整体上是对图像进行2D高速平滑,但同时也考虑了相邻像素的数值差异,来尽可以减少对边缘模糊化(这是我们想保留的)。这也就是说卷积核的形状与图像局部内容有关系,每一个像素位置都不同。

  下面给出一个具体的例子。假设你对图像中距离边缘附近区域进行滤波。如果采用简单的高斯平滑将会吧边缘模糊化。这是因为它烤间滤波区域(靠近高斯核的中心)。但采用双边滤波器可以感知边缘存在,这是因为像素数值差一笔较大。所以他就会对边缘两侧的像素使用很小的权值,减少滤波区域对边缘的与。对于数值相对统一的区域则使用更强的平滑,这是因为它们不具有边缘元素。

  感谢OpenCV 提供了bilateralFilter() 函数来实现对图像的双边滤波。

bilateralFilter(src, d, sigmaColor, sigmaSpace)

  这个函数 要求四个输入参数:

  • 第一个参数是图像;
  • 接我下来的参数 d ,是邻近滤波像素对应的直径;
  • 后面两个参数 sigmaColor , sigmaSpace 分别定义了颜色强度分布标准方差 (1D)以及2D空间分布。
    • sigmaSpace参数定义了核的在x和y方向上的空间延展(与前面讨论高斯核时的概念相同)
    • sigmaColor 参数定义了一个一维高斯分布,它指明能够被允许的像素强度差异的程度。

  滤波图像最后(加权)值时它的空间和强度权重的成绩,因此:

  • 对于滤波像素附近的相似像素对于结果影响比较大;

  • 对于远离滤波中心像素对结果影响较小,这是由空间高斯定义引起的;

  • 对于和中心滤波像素相差比较大的像素对于结果影响较小(这是由颜色强度高斯定义引起的),尽管他们靠近滤波中心也不行。

  • Python

"""
Apply Bilateral Filtering
"""
# Using the function bilateralFilter() where d is diameter of each...
# ...pixel neighborhood that is used during filtering.
# sigmaColor is used to filter sigma in the color space.
# sigmaSpace is used to filter sigma in the coordinate space.
bilateral_filter = cv2.bilateralFilter(src=image, d=9, sigmaColor=75, sigmaSpace=75)cv2.imshow('Original', image)
cv2.imshow('Bilateral Filtering', bilateral_filter)cv2.waitKey(0)
cv2.imwrite('bilateral_filtering.jpg', bilateral_filter)
cv2.destroyAllWindows()
  • C++
// Apply bilateral filtering
Mat bilateral_filter;
bilateralFilter(image, bilateral_filter, 9, 75, 75);
imshow("Original", image);
imshow("Bilateral filtering", bilateral_filter);
imwrite("bilateral_filtering.jpg", bilateral_filter);
waitKey(0);
destroyAllWindows();
return 0;

  下面是 双边滤波的结果。可以看到在均匀区域的像素并平滑,而保留了那些模板中裂纹(边缘)的成分。双边滤波是一个非常有效的技术,对于大的核尺寸它的计算量比较大。 所以依据你的应用合理的选择它。

▲ 图2.7.1 双边滤波的结果

※ 滤波总结 ※


  本文首先讨论了卷积核的概念,以及如何用于对图像进行滤波。然后通过他们对图像进行数学运算来实现特定的效果,比如平和和锐化。展示了如何在OpenCV中实现2D滤波。 在等同卷积卷积核之后,我们创建了更多定制的核,用在OpenCV中的 filter2D()函数中。 介绍了OpenCV中的重要内置函数MediaBlur(),GaussianBlur()。 最后展示了 OpenCV中的 bilateralFilter()函数,是如何在保留图片中清晰边缘的同时又平滑了图像。


■ 相关文献链接:

  • 这个链接

● 相关图表链接:

  • 图1.3.1 用于实验的样例图片
  • 图1.4.1 原始图像与滤波后的图像
  • 图2.2.1 平滑后的图像
  • 图2.4.1 高斯平滑后的结果
  • 图2.5.1 中值滤波的结果
  • 图2.6.1 图像锐化的结果

在OpenCV中利用卷积进行图像滤波相关推荐

  1. OpenCV中利用cvConvertScale()将图像的数据类型由u8转化为64f...

    图像处理开发需求.图像处理接私活挣零花钱,请加微信/QQ 2487872782 图像处理开发资料.图像处理技术交流请加QQ群,群号 271891601 OpenCV中利用cvConvertScale( ...

  2. OpenCV中利用cvConvertScale()对图像数据作线性变换

    图像处理开发需求.图像处理接私活挣零花钱,请加微信/QQ 2487872782 图像处理开发资料.图像处理技术交流请加QQ群,群号 271891601 在OpenCV的IplImage结构体char ...

  3. OpenCV与图像处理学习五——图像滤波与增强:线性、非线性滤波、直方图均衡化与Gamma变换

    OpenCV与图像处理学习五--图像滤波与增强:线性.非线性滤波.直方图均衡化与Gamma变换 三.图像滤波与增强 3.1 线性滤波 3.1.1 方框滤波 3.1.2 均值滤波 3.1.3 高斯滤波 ...

  4. OpenCV实战(12)——图像滤波详解

    OpenCV实战(12)--图像滤波详解 0. 前言 1. 频域分析 2. 低通滤波器 3. 图像下采样 3.1 使用低通滤波器下采样图像 3.2 内插像素值 4. 中值滤波器 5. 完整代码 小结 ...

  5. OpenCV中的5种平滑滤波操作

    平滑滤波是一种简单又常见的图像处理操作.平滑图像的目的有很多,但通常都是为了减少噪声和伪影. 在OpenCV中共有5种平滑滤波操作,分别是以下几种: 实验测试代码如下: #include<ios ...

  6. OpenCV-Python实战(番外篇)——OpenCV中利用鼠标事件动态绘制图形

    OpenCV-Python实战(番外篇)--OpenCV中利用鼠标事件动态绘制图形 使用鼠标事件动态绘制 动态绘制图形 动态绘制图形和文本 相关链接 使用鼠标事件动态绘制 我们已经在<OpenC ...

  7. opencv学习笔记11:图像滤波(均值,方框,高斯,中值)

    为什么要使用滤波 消除图像中的噪声成分叫作图像的平滑化或滤波操作.信号或图像的能量大部分集中在幅度谱的低频和中频段是很常见的,而在较高频段,感兴趣的信息经常被噪声淹没.因此一个能降低高频成分幅度的滤波 ...

  8. OpenCV成长之路:图像滤波

    滤波实际上是信号处理里的一个概念,而图像本身也可以看成是一个二维的信号.其中像素点灰度值的高低代表信号的强弱. 高频:图像中灰度变化剧烈的点. 低频:图像中平坦的,灰度变化不大的点. 根据图像的高频与 ...

  9. OpenCV图像处理使用笔记(六)——图像滤波

    前言 1.图像滤波也叫图像模糊,是平滑图像像素常用处理的方式,通常是为了达到减少图像噪声和伪影,或者降低图像分辨率,OpenCV提供了常用的五种图像模糊操作. 2.我的这里演示的系统环境是Linux, ...

最新文章

  1. 不是现在新型的计算机技术,浅谈计算机技术的发展趋势
  2. Flutter 混合开发实战问题记录(三)打包并上传flutter aar(包含三方plugin) 到maven...
  3. Intellij IDEA——创建MyBatis的Mapper.xml模板
  4. C语言2011计算机二级c语言考点:c语言的基础知识
  5. 从学校到现在的一个总结
  6. python打印9宫格,25宫格等奇数格,且横竖斜相加和相等
  7. The 'Microsoft.ACE.OLEDB.12.0' provider is not registered on the local Machine 完美解决
  8. 「代码随想录」 377. 组合总和 Ⅳ 【动态规划】力扣详解!
  9. Oracle官网下载JDK8需要注册怎么办
  10. 一世人中,最黑仔嘅一日
  11. 2022MRCTF-wp
  12. 使用 Hyper-V 安装 Linux
  13. 计算机论文专著 论文集,学习计算机方面论文参考文献 学习计算机专著类参考文献有哪些...
  14. EditPlus v2.12 注册过程分析(转)
  15. 蔡萍:深耕半导体行业二十年,复旦MBA“朋友圈”成智囊团
  16. 脚下,梦開始的地方——七月总结
  17. Android刷机脚本——updater-script
  18. 你好,offer(最终版)
  19. SDNUOJ 1703.字谜|STL库中map的使用/map映射
  20. 基于 KU115+MPSOC 的 6U VPX 高速信号处理板(XCKU115 + ZU9EG +DSP)

热门文章

  1. jsp中的contentType与pageEncoding的区别和作用
  2. Pipenv – 超好用的 Python 包管理工具
  3. 设计一条简单的等待工作队列之软件模型设计与实现(二)
  4. 如何进行有效的需求调研
  5. iOS开发中接口调用使用https
  6. 全面解读:腾讯 CDB 内核特性与优化实践
  7. Spring定时任务的几种实现
  8. windows下python脚本程序的运行
  9. 详细介绍springData
  10. 互联网项目管理之常见冲突浅谈