C++&Python&C# 三语言OpenCV从零开发(8):图像平滑处理

文章目录

相关链接

C++&Python&Csharp in OpenCV 专栏
【2022B站最好的OpenCV课程推荐】OpenCV从入门到实战 全套课程(附带课程课件资料+课件笔记)

前言

这次来了解一下图像平滑处理。还是老套路,先写Python,再C++,再Csharp。本篇文章难的不是代码,难的是图像学的知识

图像资源

为什么Lena的那张图会成为数字图像处理的标准图?

图像平滑处理

图像平滑处理就是PS中常用的模糊工具,涂抹工具。算法怎么计算的可以看这个文章

数字图像处理之均值滤波


图像学知识补充(重点)

本章的难点就是图形学的知识了。这里推荐一本书【数字图像处理(中译第三版)[冈萨雷斯]】

目录简单展示

什么是卷积

卷积是一种叠加态的问题的解法。叠加态的问题有:

  • 水池有两个水龙头,一个放水一个抽水。
  • 人一天的体重变化,早餐还没彻底消化完,就吃午餐了
  • 冰箱里面的食物整体的新鲜度。不新鲜的会被拿走,新鲜的食材会被放进来
  • 湖泊的水位,下雨天水位上升,不下雨水位慢慢下降

这个就是卷积的特点:叠加状态。

那么图像的卷积是什么意思?就是考虑到每个像素对应周围像素的影响,卷积后的图像是卷积前的图像像素叠加卷积的结果。通俗点来说,就是黑色像素点周围黑一点,自己白一点。白色像素点周围白一点,自己黑一点。

就像你用墨水写字,然后用水浇上去,字就糊了。这个过程就是卷积。

如何通俗易懂地解释卷积?
瞬时行为的持续性后果,用吃冰淇淋来理解卷积

什么是图像滤波

图标滤波就是对图像的噪点进行抑制,尽量不影响图像的信息量。如果不了解这两个概念,可以近似的看成。

  • 卷积是方法论,是公式
  • 图像滤波是具体实现。

数字图像处理------图像滤波概念及方法

什么是方框滤波和均值滤波

我感觉两个差不多。

十分钟带你了解均值滤波和方框滤波

代码

Python

python 复制代码
# %%

import cv2
import matplotlib.pyplot as plt
import numpy as np

image_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++我自己写了一个图像合并显示的代码

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); //窗口显示的标准图像的Size

	Mat 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也能写。

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

csharp 复制代码
 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); //窗口显示的标准图像的Size

         Mat 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那种进一步的封装,所以代码写起来还是挺痛苦的。最近的学习速度也有点慢下来了。最近在尽力坚持学习。

相关推荐
小吴同学·3 小时前
.NET6 WebApi第1讲:VSCode开发.NET项目、区别.NET5框架【两个框架启动流程详解】
c#·.netcore·.net core
南东山人4 小时前
一文说清:C和C++混合编程
c语言·c++
湫ccc5 小时前
《Python基础》之字符串格式化输出
开发语言·python
mqiqe6 小时前
Python MySQL通过Binlog 获取变更记录 恢复数据
开发语言·python·mysql
AttackingLin6 小时前
2024强网杯--babyheap house of apple2解法
linux·开发语言·python
哭泣的眼泪4086 小时前
解析粗糙度仪在工业制造及材料科学和建筑工程领域的重要性
python·算法·django·virtualenv·pygame
Ysjt | 深7 小时前
C++多线程编程入门教程(优质版)
java·开发语言·jvm·c++
ephemerals__7 小时前
【c++丨STL】list模拟实现(附源码)
开发语言·c++·list
湫ccc7 小时前
《Python基础》之基本数据类型
开发语言·python