文章目录
- 相关链接
- 前言
- 图像资源
- 图像平滑处理
- 图像学知识补充(重点)
- 什么是卷积
- 什么是图像滤波
- 什么是方框滤波和均值滤波
- 代码
- Python
- C++
- Csharp
- 总结
相关链接
C++&Python&Csharp in OpenCV 专栏
【2022B站最好的OpenCV课程推荐】OpenCV从入门到实战 全套课程(附带课程课件资料+课件笔记)
前言
这次来了解一下图像平滑处理。还是老套路,先写Python,再C++,再Csharp。本篇文章难的不是代码,难的是图像学的知识
图像资源
为什么Lena的那张图会成为数字图像处理的标准图?
图像平滑处理
图像平滑处理就是PS中常用的模糊工具,涂抹工具。算法怎么计算的可以看这个文章
数字图像处理之均值滤波
图像学知识补充(重点)
本章的难点就是图形学的知识了。这里推荐一本书【数字图像处理(中译第三版)[冈萨雷斯]】
目录简单展示
什么是卷积
卷积是一种叠加态的问题的解法。叠加态的问题有:
- 水池有两个水龙头,一个放水一个抽水。
- 人一天的体重变化,早餐还没彻底消化完,就吃午餐了
- 冰箱里面的食物整体的新鲜度。不新鲜的会被拿走,新鲜的食材会被放进来
- 湖泊的水位,下雨天水位上升,不下雨水位慢慢下降
这个就是卷积的特点:叠加状态。
那么图像的卷积是什么意思?就是考虑到每个像素对应周围像素的影响,卷积后的图像是卷积前的图像像素叠加卷积的结果。通俗点来说,就是黑色像素点周围黑一点,自己白一点。白色像素点周围白一点,自己黑一点。
就像你用墨水写字,然后用水浇上去,字就糊了。这个过程就是卷积。
如何通俗易懂地解释卷积?
瞬时行为的持续性后果,用吃冰淇淋来理解卷积
什么是图像滤波
图标滤波就是对图像的噪点进行抑制,尽量不影响图像的信息量。如果不了解这两个概念,可以近似的看成。
- 卷积是方法论,是公式
- 图像滤波是具体实现。
数字图像处理——图像滤波概念及方法
什么是方框滤波和均值滤波
我感觉两个差不多。
十分钟带你了解均值滤波和方框滤波
代码
Python
# %%import cv2
import matplotlib.pyplot as plt
import numpy as npimage_src = cv2.imread('d:\workSpace\OpenCV\HellOpenCV\Resources\images\lena.png')image_config = (7,7)
# 均值滤波
# 最简单的卷积操作
image_blur = cv2.blur(image_src,image_config)# 方框滤波,基本和均值一样
image_box_t = cv2.boxFilter(image_src,-1,image_config,normalize=True)
# 方框滤波,反方向
image_box_f = cv2.boxFilter(image_src,-1,image_config,normalize=False)# 高斯滤波,更重视中间值
image_gaussian = cv2.GaussianBlur(image_src,image_config,1)image_median = cv2.medianBlur(image_src,7)# 因为openCV默认BGR,Plt默认RGB,所以要转化一下
# 因为有Csharp的习惯,我习惯默认大写驼峰
def PltImshowRgb(row:int,col:int,index:int,image:np.mat,title:str):plt.subplot(row,col,index)plt.imshow(cv2.cvtColor(image,cv2.COLOR_BGR2RGB))plt.title(title)PltImshowRgb(2,3,1,image_src,'image_src')
PltImshowRgb(2,3,2,image_blur,'image_blur')
PltImshowRgb(2,3,3,image_box_t,'image_box_t')
PltImshowRgb(2,3,4,image_box_f,'image_box_f')
PltImshowRgb(2,3,5,image_gaussian,'image_gaussian')
PltImshowRgb(2,3,6,image_median,'image_median')plt.show()
用下来的感觉,滤波的效果确实差不多。
想详细了解其中的区别,可以看这篇文章。我就不展开说明了。
数字图像处理(11): 图像平滑 (均值滤波、中值滤波和高斯滤波)
C++
C++我自己写了一个图像合并显示的代码
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include<iostream>
#include <vector>
using namespace std;
using namespace cv;vector<Mat> imageVector;/// <summary>
/// 将图案并排显示
/// </summary>
/// <param name="imgVector"></param>
/// <param name="dst"></param>
/// <param name="imgCols"></param>
void multipleImage(vector<Mat> imgVector, Mat& dst, int imgCols)
{const int MAX_PIXEL = 300;int imgNum = imgVector.size();//选择图片最大的一边 将最大的边按比例变为300像素Size imgOriSize = imgVector[0].size();int imgMaxPixel = max(imgOriSize.height, imgOriSize.width);//获取最大像素变为MAX_PIXEL的比例因子double prop = imgMaxPixel < MAX_PIXEL ? (double)imgMaxPixel / MAX_PIXEL : MAX_PIXEL / (double)imgMaxPixel;Size imgStdSize(imgOriSize.width * prop, imgOriSize.height * prop); //窗口显示的标准图像的SizeMat imgStd; //标准图片Point2i location(0, 0); //坐标点(从0,0开始)//构建窗口大小 通道与imageVector[0]的通道一样Mat imgWindow(imgStdSize.height * ((imgNum - 1) / imgCols + 1), imgStdSize.width * imgCols, imgVector[0].type());for (int i = 0; i < imgNum; i++){location.x = (i % imgCols) * imgStdSize.width;location.y = (i / imgCols) * imgStdSize.height;resize(imgVector[i], imgStd, imgStdSize, prop, prop, INTER_LINEAR); //设置为标准大小//imgWindow()imgStd.copyTo(imgWindow(Rect(location, imgStdSize)));}dst = imgWindow;
}int main()
{auto image = imread("D:/workSpace/OpenCV/HellOpenCV/Resources/images/lena.png");Mat showImage;Mat image_blur;Mat image_box_t;Mat image_box_f;Mat image_gaussian;Mat image_median;Size image_config = Size(7, 7);blur(image, image_blur, image_config);boxFilter(image, image_box_t, -1, image_config,Point(-1,-1),true);boxFilter(image, image_box_f,-1, image_config, Point(-1, -1), false);GaussianBlur(image, image_gaussian, image_config, 1);medianBlur(image, image_median, 7);vector<Mat> images{image,image_blur,image_box_t,image_box_f,image_gaussian,image_median};multipleImage(images, showImage, 3);imshow("C++", showImage);waitKey(0);destroyAllWindows();return 0;
}
Csharp
还是那句话。C++写好了,Csharp也能写。
internal class Program
{static void Main(string[] args){Mat image = Cv2.ImRead("D:/workSpace/OpenCV/HellOpenCV/Resources/images/lena.png");Mat image_blur = new Mat();Mat image_box_t = new Mat();Mat image_box_f = new Mat();Mat image_gaussian = new Mat();Mat image_median = new Mat();Size image_config = new Size(7,7);Cv2.Blur(image, image_blur, image_config);Cv2.BoxFilter(image, image_box_t, -1, image_config, new Point(-1, -1), true);Cv2.BoxFilter(image, image_box_f, -1, image_config, new Point(-1, -1), false);Cv2.GaussianBlur(image, image_gaussian, image_config, 1);Cv2.MedianBlur(image, image_median, 3);var res = MyOpenCV_Extensions.MultipleImage(new List<Mat>() { image,image_blur,image_box_t,image_box_f,image_gaussian,image_median}, 3);Cv2.ImShow("Csharp", res);Cv2.WaitKey(0);Cv2.DestroyAllWindows();}}
MyOpenCV_Extensions.cs
public static class MyOpenCV_Extensions{/// <summary>/// 3通道遍历/// </summary>/// <param name="mat"></param>/// <returns></returns>public static int[,,] MatToArray(Mat mat){var res = new int[mat.Rows, mat.Cols, mat.Channels()];for (var i = 0; i < mat.Rows; i++){for (var j = 0; j < mat.Cols; j++){var temp = mat.At<Vec3b>(i, j);res[i, j, 0] = temp[0];res[i, j, 1] = temp[1];res[i, j, 2] = temp[2];}}return res;}public static Mat MultipleImage(List<Mat> lists, int imgCols){const int MAX_PIXEL = 300;int imgNum = lists.Count;//选择图片最大的一边 将最大的边按比例变为300像素Size imgOriSize = lists[0].Size();int imgMaxPixel = Math.Max(imgOriSize.Height, imgOriSize.Width);//获取最大像素变为MAX_PIXEL的比例因子double prop = imgMaxPixel < MAX_PIXEL ? (double)imgMaxPixel / MAX_PIXEL : MAX_PIXEL / (double)imgMaxPixel;Size imgStdSize= new Size(imgOriSize.Width* prop, imgOriSize.Height* prop); //窗口显示的标准图像的SizeMat imgStd = new Mat(); //标准图片Point location = new Point(0, 0); //坐标点(从0,0开始)//构建窗口大小 通道与imageVector[0]的通道一样Mat imgWindow = new Mat(imgStdSize.Height* ((imgNum -1) / imgCols + 1), imgStdSize.Width* imgCols, lists[0].Type());for (int i = 0; i < imgNum; i++){location.X = (i % imgCols) * imgStdSize.Width;location.Y = (i / imgCols) * imgStdSize.Height;Cv2.Resize(lists[i], imgStd, imgStdSize, prop, prop, InterpolationFlags.Linear); //设置为标准大小imgStd.CopyTo(new Mat(imgWindow, new Rect(location, imgStdSize)) ); }return imgWindow;}}
总结
学这个还是挺无聊的,因为刚开始就是学一些算子的使用。而且用于OpenCV没有Halcon那种进一步的封装,所以代码写起来还是挺痛苦的。最近的学习速度也有点慢下来了。最近在尽力坚持学习。