2019-05-29 12:11:35 WHU_StudentZhong 阅读数 412

(环境:VS2017+OPENCV4.0)

K值聚类算法并不复杂,这里不多介绍,直接上代码。

#include "pch.h"
#include <iostream>
#include <opencv2/opencv.hpp>
#include <math.h>
using namespace std;
using namespace cv;

struct SPoint {//点和点的类别
    double v1;
    double v2;
    double v3;
    int id;
};
void Color(SPoint* pt, int id) {//给不同类别赋予颜色,可以自己加
    switch (pt->id) {
    case 0:
        pt->v1 = 0;
        pt->v2 = 255;
        pt->v3 = 170;
        break;
    case 1:
        pt->v1 = 255;
        pt->v2 = 0;
        pt->v3 = 0;
        break;
    case 2:
        pt->v1 = 0;
        pt->v2 = 255;
        pt->v3 = 0;
        break;
    case 3:
        pt->v1 = 0;
        pt->v2 = 0;
        pt->v3 = 255;
        break;
    case 4:
        pt->v1 = 255;
        pt->v2 = 170;
        pt->v3 = 200;
        break;
    case 5:
        pt->v1 = 100;
        pt->v2 = 100;
        pt->v3 = 100;
        break;
    case 6:
        pt->v1 = 200;
        pt->v2 = 200;
        pt->v3 = 200;
        break;
    }
}
double Norm2(SPoint p1, SPoint p2) {//距离的平方
    return (pow((p1.v1 - p2.v1), 2) + pow((p1.v2 - p2.v2), 2) + pow((p1.v3 - p2.v3), 2));
    //return (p1.v1 - p2.v1)*(p1.v1 - p2.v1) + (p1.v2 - p2.v2)*(p1.v2 - p2.v2) + (p1.v3 - p2.v3)*(p1.v3 - p2.v3);
}
Mat Kmean(Mat src, int species, int iterations) {//原图像,种类数,迭代次数
    Mat K = src;
    int  rows = src.rows, cols = src.cols;//行,列
    SPoint* Points = new SPoint[rows*cols];
    SPoint* CenterPt1 = new SPoint[species];
    SPoint* CenterPt2 = new SPoint[species];
    int* num = new int[species];
    double* sum1 = new double[species];
    double* sum2 = new double[species];
    double* sum3 = new double[species];
    for (int x = 0; x < cols; x++) {//把图像信息读入
        for (int y = 0; y < rows; y++) {
            Points[x + y * cols].v1 = src.at<Vec3b>(y, x)[0]; // blue
            Points[x + y * cols].v2 = src.at<Vec3b>(y, x)[1]; // green
            Points[x + y * cols].v3 = src.at<Vec3b>(y, x)[2]; // red
            Points[x + y * cols].id = 0;
        }
    }
    for (int i = 0; i < species; i++) {//初始化中心
        CenterPt1[i] = Points[i * 10];
        CenterPt1[i].id = i;
        CenterPt2[i] = Points[i * 10];
        CenterPt2[i].id = i;
        num[i] = 0;
        sum1[i] = sum2[i] = sum3[i] = 0;
    }
    double min = 20000000;
    double error = 0;
    while (iterations > 0) {
        for (int x = 0; x < cols; x++) {//把图像信息读入
            for (int y = 0; y < rows; y++) {
                for (int i = 0; i < species; i++) {
                    int id = CenterPt1[i].id;
                    if (Norm2(Points[x + y * cols], CenterPt1[i]) < min) {
                        Points[x + y * cols].id = id;
                        min = Norm2(Points[x + y * cols], CenterPt1[i]);
                        num[id]++;
                        sum1[id] += Points[x + y * cols].v1;
                        sum2[id] += Points[x + y * cols].v2;
                        sum3[id] += Points[x + y * cols].v3;
                    }
                }
                min = 20000000;
            }
        }
        iterations--;

        for (int i = 0; i < species; i++) {
            CenterPt2[i].v1 = sum1[i] / num[i];
            CenterPt2[i].v2 = sum2[i] / num[i];
            CenterPt2[i].v3 = sum3[i] / num[i];
            error += pow((CenterPt2[i].v1 - CenterPt1[i].v1), 2) + pow((CenterPt2[i].v2 - CenterPt1[i].v2), 2) + pow((CenterPt2[i].v3 - CenterPt1[i].v3), 2);
        }
        if (error < 0.0001)
            break;
        for (int i = 0; i < species; i++) {
            CenterPt1[i].v1 = CenterPt2[i].v1;
            CenterPt1[i].v2 = CenterPt2[i].v2;
            CenterPt1[i].v3 = CenterPt2[i].v3;
            num[i] = 0;
            sum1[i] = sum2[i] = sum3[i] = 0;
        }
        error = 0;
    }//分类结束
    for (int x = 0; x < cols; x++) {//上色
        for (int y = 0; y < rows; y++) {
            Color(&Points[x + y * cols], Points[x + y * cols].id);
        }
    }
    for (int x = 0; x < cols; x++) {//显示
        for (int y = 0; y < rows; y++) {
            K.at<Vec3b>(y, x)[0] = (int)Points[x + y * cols].v1;
            K.at<Vec3b>(y, x)[1] = (int)Points[x + y * cols].v2;
            K.at<Vec3b>(y, x)[2] = (int)Points[x + y * cols].v3;
        }
    }
    //显示图片
    imshow("Output", K);
    //不加此语句图片会一闪而过
    waitKey(0);
    delete[] Points, CenterPt1, CenterPt2, num, sum1, sum2, sum3;
    return K;
}

int main()
{
    //读取图片(使用图片的相对路径)
    Mat src = imread("ik_beijing_c.bmp");
    Kmean(src, 7, 3);
    return 0;
}

/*
int b=img.at<Vec3b>(y,x)[0]; // blue
int g=img.at<Vec3b>(y,x)[1]; // green
int r=img.at<Vec3b>(y,x)[2]; // red
*/

                                                                               分类效果图

由于迭代次数有限,可以看到并不收敛,另外初始点的选择也不是很好,需要改进。

2019-10-22 16:05:53 weixin_43031412 阅读数 37

章节


图像处理和分析通常被看作是对二维值数组的操作。然而,在一些领域中,必须对高维数的图像进行处理分析,例如,医学成像和生物成像。由于对多维特性的良好支持,numpy非常适合这种类型的应用程序。scipy.ndimage包提供了许多通用的图像处理和分析功能,这些功能支持操作任意维度的数组。

scipy.ndimage中提供了图像矩阵变换、图像滤波、图像卷积等功能。

旋转图片

旋转图片,可以使用ndimage.rotate函数。

测试图片下载: face.png

示例

加载原图片

from scipy import ndimage
import matplotlib.image as mpimg
import matplotlib.pyplot as plt

# 加载图片
face = mpimg.imread('./face.png')

# 显示图片
plt.imshow(face)
# plt.savefig('./img2-1.png') # 保存要显示的图片
plt.show()

输出

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NTwDVFBg-1571731533309)(https://www.qikegu.com/wp-content/uploads/2019/06/img2-1.png)]

示例

from scipy import ndimage
import matplotlib.image as mpimg
import matplotlib.pyplot as plt

# 加载图片
face = mpimg.imread('./face.png')

# 旋转图片
rotate_face = ndimage.rotate(face, 45)

plt.imshow(rotate_face)
# plt.savefig('./img3-1.png') # 保存要显示的图片
plt.show()

输出

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-roYeWLno-1571731533312)(https://www.qikegu.com/wp-content/uploads/2019/06/img3-1.png)]

图像滤波

图像滤波是一种修改/增强图像的技术。例如,可以通过图像滤波突出图像的某些特性,弱化或滤除图像的另一些特性。滤波有很多种,例如:平滑、锐化、边缘增强等等。

示例

对图像进行高斯滤波。高斯滤波是一种模糊滤波,广泛用于滤除图像噪声。

from scipy import ndimage
import matplotlib.image as mpimg
import matplotlib.pyplot as plt

# 加载图片
face = mpimg.imread('./face.png')

# 处理图片
face1 = ndimage.gaussian_filter(face, sigma=3)

# 显示图片
plt.imshow(face1)
# plt.savefig('./img4-1.png') # 保存要显示的图片
plt.show()

输出

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ri8BeFuk-1571731533317)(https://www.qikegu.com/wp-content/uploads/2019/06/img4-1.png)]

sigma=3表示模糊程度为3,我们可以通过调整sigma值,来比较图像质量的变化。

边缘检测

边缘检测是一种寻找图像中物体边界的图像处理技术。它的原理是通过检测图像中的亮度突变,来识别物体边缘。边缘检测在图像处理、计算机视觉、机器视觉等领域中广泛应用。

常用边缘检测算法包括:

  • Sobel
  • Canny
  • Prewitt
  • Roberts
  • Fuzzy Logic methods

让我们考虑下面的例子。

import scipy.ndimage as nd
import numpy as np

im = np.zeros((256, 256))
im[64:-64, 64:-64] = 1
im[90:-90,90:-90] = 2
im = nd.gaussian_filter(im, 8)

import matplotlib.pyplot as plt
plt.imshow(im)
# plt.savefig('./img5-1.png') # 保存要显示的图片
plt.show()

上面的程序将生成以下输出。

scipy.ndimage pic5

图像看起来像一个正方形的色块,我们将检测这些彩色块的边缘。这里使用ndimage的Sobel函数来检测图像边缘,该函数会对图像数组的每个轴分开操作,产生两个矩阵,然后我们使用NumPy中的Hypot函数将这两个矩阵合并为一个矩阵,得到最后结果。

示例

import scipy.ndimage as nd
import numpy as np
import matplotlib.pyplot as plt

im = np.zeros((256, 256))
im[64:-64, 64:-64] = 1
im[90:-90,90:-90] = 2
im = nd.gaussian_filter(im, 8)

sx = nd.sobel(im, axis = 0, mode = 'constant')
sy = nd.sobel(im, axis = 1, mode = 'constant')
sob = np.hypot(sx, sy)

plt.imshow(sob)
# plt.savefig('./img6-1.png') # 保存要显示的图片
plt.show()

上面的程序将生成以下输出。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qc6G227y-1571731533323)(https://www.qikegu.com/wp-content/uploads/2019/06/img6-1.png)]

2018-08-07 20:45:37 ChenQihome9 阅读数 1520

这篇文章将以北京工商大学良乡校区附近的共享单车图进行鼠标取点操作以及对相关图像的处理,将共享单车以数据点的形式呈现,并且对数据点进行K值聚类找到最佳的中心点。

图1-1 一周的数据点位移展示图

PART 1 导入图片并对图片进行取点操作

下面是对tif型的正方形图片进行取点操作(其他形状或者格式的图片可以,即将ylim注释掉),将所要进行操作的图片拖入工作区执行下面的代码:

%% 建立主函数
function BTBU_mouse_track()  %BTBU_mouse_track() 运行时只要运行函数名即可
in = imread('照片名称.tif');  %imread函数为读取图像的函数
figure,imshow(in);
%% 显示坐标系,以及画平行于x轴与y轴的水平线,将图片分为四个区域
axis on;  % 打开(显示)坐标系
hold on;  plot([550,550],[0,1100],'r-'); %ylim 用于绘制y轴的取值范围
hold on;  plot([0,1100],[550,550],'r-'); %m-- 用于描述线型,粉色,虚线
BTBUbikedata = zeros(0,2);   %保存所取得点,并且以mat形式储存
save BTBUbikedata BTBUbikedata;
set(gcf,'WindowButtonDownFcn',@BTBU_ButttonDownFcn);

%% 回调函数
function [x,y]=BTBU_ButttonDownFcn(src,event)
load('BTBUbikedata.mat');
pt = get(gca,'CurrentPoint');
x = pt(1,1);
y = pt(1,2);
BTBUbikedata(end+1,1)=round(y); %每次新产生的坐标加到BTBUbikedata中,注意figure中的x,y坐标跟实际的是反过来的
BTBUbikedata(end,2)=round(x);  %由于图像放大会产生非整数坐标,这里取整
[len,~] = size(BTBUbikedata);
fprintf('单车编号=%d,x=%f,y=%f\n',len,y,x);
save BTBUbikedata BTBUbikedata;

PART 2 对数据进行K值聚类

接着在先导入需要在上面展示数据点的底图,执行下列代码,可以得到图1-2所示的展示图

load('BTBUbikedata.mat');  %% 导入数据
x=BTBUbikedata(:,2);       y=BTBUbikedata(:,1);
in = imread('图片底层.tif');%% 导入需要在图片上显示点的底图
figure,imshow(in);         axis on;  
hold on;  plot([550,550],[0,1100],'r-');   %ylim 用于绘制y轴的取值范围
hold on;  plot([0,1100],[550,550],'r-');   %m-- 用于描述线型,粉色,虚线
hold on;  plot(x,y,'ro'); xlim([0,1100]); ylim([0,1100]);%对X轴和Y轴设定显示范围 
title('研究样本散点分布图')
图1-2 将所取点在图上展示
图1-3 二类聚类的轮廓图

执行下列代码可以得到图1-3 二类聚类的轮廓图,由轮库图可以知道第一类数据大部分在0.8以下,而第二类的数据则比较集中,随后采用分层聚类进一步验证,根据分层聚类的方法得出的树状图可以看出,有两大类具有明显差异性的数据,而第三类则是也具有差异但数目极少的数据,因此可以分成明显的两类。

[cidx2,cmeans2,sumd2,D2] = kmeans(BTBUbikedata,2,'dist','sqEuclidean'); %二类K值
P2 = figure;clf;
[silh2,h2] = silhouette(BTBUbikedata,cidx2,'sqeuclidean'); 
eucD = pdist(BTBUbikedata,'euclidean'); %分层聚类
clustTreeEuc = linkage(eucD,'average');
cophenet(clustTreeEuc,eucD);
P3 = figure;clf;
[h,nodes] =  dendrogram(clustTreeEuc,20);
set(gca,'TickDir','out','TickLength',[.002 0],'XTickLabel',[]);  % 尝试用三类K值聚类分析
[cidx3,cmeans3,sumd3,D3] = kmeans(BTBUbikedata,3,'dist','sqEuclidean');
P4 = figure;clf;
[silh3,h3] = silhouette(BTBUbikedata,cidx3,'sqeuclidean');
图1-4 分层聚类的树状图
图1-5 生成的二类的聚类中心点

PART 3 对数据进行可视化操作

然后将中心化的点再现在更加清晰可见的底图上

A=xlsread('BTBUbikedaily0801.xls');  %读取Excel表格数据
x=A(:,2); y=A(:,1);
x1=A(:,4); y1=A(:,3); 
x2=A(:,6); y2=A(:,5); 
x3=A(:,8); y3=A(:,7);
in = imread('图片.tif');   figure,imshow(in);    axis on;  
hold on;  p1=plot([550,550],[0,1100],'r-'); %ylim 用于绘制y轴的取值范围
hold on;  p2=plot([0,1100],[550,550],'r-'); %m-- 用于描述线型,粉色,虚线
hold on;  p3=plot(x,y,'yo','MarkerFaceColor','y');   %绘制x和y的图像
hold on; p4=plot(x1,y1,'ro','MarkerFaceColor','r'); %MarkerFaceColor表示实心点
hold on; p5=plot(x2,y2,'bo','MarkerFaceColor','b');
hold on; p6=plot(x3,y3,'go','MarkerFaceColor','g');
legend([p3,p4,p5,p6],'昨日时间段03','时间段01','时间段02','时间段03','Location','northoutside','Orientation','horizontal');  %图例,注意图例会展示展示出所有前面plot所画的图像,包括所画的直线,因此采用[pi,pn]来限制图例的展示
title(legend,'0801各个时间段');    xlim([0,1100]);  ylim([0,1100]); %对X和Y轴设定显示范围 
set(gca,'YDir','reverse');        %将x轴方向设置为反向(从上到下递增)。
title('显示同一天的三个时间段样本散点分布图');
图1-6 对总共21个时间段的中心点展示
图1-7 最终的再聚类点在地图上的展示

其中使用的单车数据BTBUbikedata.xlsx下载可以访问:https://github.com/ChenQihome9/CSDN-Data-Library

——Written in BTBU

2016-11-01 22:58:19 Serious_Tanx 阅读数 1583

注意:现在用的这个方法,只对灰度值有断续的情况下,处理的结果会很好。因为只考虑一维数据就很好。如果是二维的,我们则还是需要加上权值信息,即每个灰度对应的数量,这就是另一种算法。我们会在后面的文章继续提到。

一、K均值聚类方法概述:

K均值聚类算法是先随机选取K个对象作为初始的聚类中心。然后计算每个对象与各个种子聚类中心之间的距离,把每个对象分配给距离它最近的聚类中心。聚类中心以及分配给它们的对象就代表一个聚类。一旦全部对象都被分配了,每个聚类的聚类中心会根据聚类中现有的对象被重新计算。这个过程将不断重复直到满足某个终止条件。终止条件可以是没有(或最小数目)对象被重新分配给不同的聚类,没有(或最小数目)聚类中心再发生变化,误差平方和局部最小。

即其目的是,各个样本与所在类均值的误差平方和达到最小(这也是评价K-means算法最后聚类效果的评价标准)。

二、算法介绍:
(1) 从 n个数据对象任意选择 k 个对象作为初始聚类中心;

(2 ) 根据每个聚类对象的均值(中心对象),计算每个对象与这些中心对象的距离;并根据最小距离重新对相应对象进行划分;

(3) 重新计算每个(有变化)聚类的均值(中心对象) 三、代码(附说明)

(4)循环(2)到(3)直到每个聚类不再发生变化为止

三、代码(C语言版)

// KmeanImage.cpp : 定义控制台应用程序的入口点。
//作者:Serious_Tanx
//时间:2016.11.01
//题目:根据K均值方法对图像灰度值进行分类
//作用:可以根据聚类后的值,来计算得出后续二值化的阈值

#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <vector>
#include <math.h>

#define k 3  //分成3类

using namespace std;

struct Tuple //结构体的好处不用多说
{
     float gray;
};

void KMeans(vector<Tuple>tuples);
Tuple getMeans(vector<Tuple>cluster);
float getVar(vector<Tuple>clusters[],Tuple means[]);
int clusterOfTuple(Tuple means[],Tuple tuple);
float getDistXY(Tuple t1,Tuple t2);

int _tmain(int argc, _TCHAR* argv[])
{
    char fname[256];
    cout<<"请输入存放数据的文件名:";
    cin>>fname;
    cout<<endl;
    ifstream infile;
    infile.open(fname,ios::in);
    if(!infile)
    {
        cout<<"不能打开输入的文件"<<fname<<endl;
        return 0;
    }

    int count=0;
    vector<Tuple>tuples;//建立储存数据的容器
    Tuple tuple;
    while(!infile.eof())//读入数据
    {
        count++;

        infile>>tuple.gray;
        tuples.push_back(tuple);

    }

    for(vector<Tuple>::size_type ix=0;ix!=tuples.size();++ix)
        cout<<tuples[ix].gray<<" ";
    cout<<endl;

    KMeans(tuples);
    system("PAUSE");
    return 0;
}

void KMeans(vector<Tuple>tuples)
{
    vector<Tuple>clusters[k];

    Tuple means[k];//质心,也就是均值

    //默认一开始将前k个元祖的值作为k个簇的质心(均值)
    for(int i=0;i<k;i++)
    {
        means[i].gray=tuples[i].gray;
    }

    int lable=0;

    //根据默认的质心给簇赋值
    for(int i=0;i!=tuples.size();++i)
    {
        lable=clusterOfTuple(means,tuples[i]);
        clusters[lable].push_back(tuples[i]);
    }

    //输出刚开始的簇
    for(lable=0;lable<k;lable++)
    {
        cout<<"第"<<lable+1<<"个簇:"<<endl;
        vector<Tuple>t=clusters[lable];
        for(int i=0;i<t.size();i++)
        {
            cout<<t[i].gray<<" ";
        }
        cout<<endl;
    }

    float oldVar=-1;
    float newVar=getVar(clusters,means);

    while(abs(newVar - oldVar) >=1)
    {
        for(int i=0;i<k;i++)
        {
            means[i] = getMeans(clusters[i]);
        }
        oldVar = newVar;
        newVar = getVar(clusters,means);//计算新的准则函数值
        for(int i=0;i<k;i++)
        {
            clusters[i].clear();
        }
        //根据新的质心获得新的簇
        for(int i=0;i!=tuples.size();++i)
        {
            lable=clusterOfTuple(means,tuples[i]);
            clusters[lable].push_back(tuples[i]);
        }
        //输出当前的簇
        for(lable=0;lable<k;lable++)
        {
            cout<<"第"<<lable+1<<"个簇:"<<endl;
            vector<Tuple>t=clusters[lable];
            for(int i=0;i<t.size();i++)
            {
                cout<<t[i].gray<<" ";
            }
            cout<<endl;
        }
    }
}

//获得当前簇的均值(质心)
Tuple getMeans(vector<Tuple>cluster)
{
    int num = cluster.size();
    double meansX=0;
    Tuple t;
    for(int i=0;i<num;i++)
    {
        meansX +=cluster[i].gray;
    }
    t.gray = meansX/num;

    return t;
}

//获得给定簇集的平方误差
float getVar(vector<Tuple>clusters[],Tuple means[])
{
    float var=0;
    for(int i=0;i<k;i++)
    {
        vector<Tuple> t=clusters[i];
        for(int j=0;j<t.size();j++)
        {
            var +=getDistXY(t[j],means[i]);
        }
    }
    return var;
}

//根据质心,决定当前元组属于哪个簇
int clusterOfTuple(Tuple means[],Tuple tuple)
{
    float dist=getDistXY(means[0],tuple);
    float tmp;
    int label=0;
    for(int i=1;i<k;i++)
    {
        tmp=getDistXY(means[i],tuple);
        if(tmp<dist)
        {
            dist=tmp;
            label=i;
        }
    }
    return label;
}

//计算两个元组间的欧几里得距离
//注意,这里的是欧几里得距离,对于灰度值来说,就是灰度值之间的差或者差的平方根
float getDistXY(Tuple t1,Tuple t2)
{
    //距离注意要加绝对值,ads是整数绝对值,fabs是浮点数绝对值
    //注意,这里为了数据更加好,可以加平方根
    return abs(t1.gray - t2.gray);  
}

在执行程序时,需要输入存放数据的文件,即txt,输入的是txt所在的目录,注意,这里要是双\的,即输入的是 F:\data.txt

里面的数据可以是如下图,因为没有做全部的结果,大概用了几个测试结果。大家可以读取的是自己的灰度值。

这里写图片描述

经过若干次循环之后,最后得到的是最终的结果。如下图所示,已经把输入的几个灰度值进行了区分。

这里写图片描述

这里写图片描述

可以看得出,已经把输入的几个数据完美的进行了分成了三类。

之后,求每个类的最大的最大最小值也好,或者中值也好,就能求出2个阈值。这里可以用3个聚类后的结果的分别的均值,做差,然后除以2,就可以得到阈值。当然,求阈值的方法有很多,就不再一一赘述。

参考资料:http://blog.csdn.net/cai0538/article/details/7061922
注:这个参考资料写的是,坐标之间的K均值聚类,我们改的是灰度值的K均值聚类。

2018-06-21 20:28:04 zx520113 阅读数 463

        5、图像滤波(平滑)

        图像滤波(平滑),即在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制,是图像预处理中不可缺少的操作,其处理效果的好坏将直接影响到后续图像处理和分析的有效性和可靠性。

        常用的图像滤波算法高斯滤波、均值滤波、中值滤波、双边滤波等。

        卷积的定义:假设被卷积的图像为I,卷积核为K。

        

        I与K的二维离散卷积计算步骤如下:

        首先将K翻转成,然后用K沿着I的每一个位置相乘求和,得到full卷积,,从filter和image刚相交开始做卷积。


        valid卷积:

        当filter全部在image里面的时候,进行卷积运算。

        same卷: 

        当filter的中心(K)与image的边角重合时,开始做卷积运算。

        在OpenCV中通过调用signal.convolve2d()实现卷积。

 

        高斯滤波:高斯滤波(高斯平滑)是一种线性平滑滤波,适用于消除高斯噪声,广泛应用于图像处理的减噪过程。通俗的讲,高斯滤波就是对整幅图像进行加权平均的过程,每一个像素点的值,都由其本身和邻域内的其他像素值经过加权平均后得到。高斯平滑滤波器对于抑制服从正态分布的噪声非常有效。

        高斯卷积算子的构建:

        首先计算高斯矩阵:

        

        再计算高斯矩阵的和:

        最后用高斯矩阵除以本身的和,即归一化,得到高斯卷积算子:

         

        在OpenCV中,通过调用cv2.getGaussianKernel(ksize,sigma,ktype)实现一维水平方向上的高斯平滑,再进行一维垂直方向上的高斯平滑。

 

        均值平滑:均值滤波,是最简单的一种线性滤波操作,输出图像的每一个像素是核窗口内输入图像对应图像像素的平均值。其算法比较简单,计算速度快,但是均值滤波本身存在着固有的缺陷,即它不能很好地保护图像细节,在去除噪声的同时,也破坏了图像的细节部分,从而使图像变得模糊,不能很好地去除噪声点。但均值滤波对周期性的干扰噪声有很好的抑制作用。

        均值平滑:

        利用矩阵积分,计算出矩阵中任意矩形区域内的和,快速均值平滑:

        

        在OpenCV中,通过调用cv2.blur()实现均值滤波。

 

        中值滤波:中值滤波法是一种非线性平滑技术,将图像的每个像素用邻域 (以当前像素为中心的正方形区域)像素的中值代替 ,常用于消除图像中的椒盐噪声。中值滤波对脉冲噪声有良好的滤除作用,特别是在滤除噪声的同时,能够保护信号的边缘,使之不被模糊,但它会洗去均匀介质区域中的纹理。

        再OpenCV中,通过调用cv2.medianBlur()实现中值滤波。

 

        双边滤波:双边滤波(Bilateral filter)是一种非线性的滤波方法,是结合图像的空间邻近度和像素值相似度的一种折衷处理,同时考虑空域信息和灰度相似性,达到保边去噪的目的,具有简单、非迭代、局部的特点,能够对低频信息进行较好的额滤波。双边滤波器的好处是可以做边缘保存,这个特点对于一些图像模糊来说很有用。

        双边滤波根据每个位置的领域,对应该位置构建不同的权重模板,首先,构建winH*winW的空间距离权重模板,与构建高斯卷积核的过程类似,winH*winW均为奇数,0<=h<winH,0<=w<winW。

        

        然后,构建winH*winW的相似权重模板,是通过(r,c)处的值与其领域值得差值的指数衡量。

        

        最后将closenessWeight和similarityWeight的对应位置相乘,然后进行归一化,便可得到改位置的权重模板,将所得到的权重模板和该位置领域的对应位置相乘求和,最后就得到该位置的输出值。

        在OpenCV中,通过调用cv2.bilateralFilter()实现双边滤波。

        联合双边滤波与双边滤波不同之处在于,双边滤波是根据原图对每一个位置,通过该位置和其领域的灰度值的差的指数来估计相似性;而联合双边滤波是首先对原图进行高斯平滑,根据平滑的结果,用当前的位置及其领域的值得差来估计相似性权重模板。

没有更多推荐了,返回首页