第1章 图像处理系统
1.1 翻转
flip函数实现图像的翻转功能,其语法结构为:
flip(InputArray src, OutputArray dst, int flipCode)
src:要处理的原始图像。
dst:与原始图像同样大小、类型的目标图像。
flipCode:是旋转类型。
flipCode=0,沿x轴方向翻转。
flipCode为任意正数,沿y轴方向翻转。
flipCode为任意负数,x轴、y轴同时翻转。
实现代码如下:
#include <opencv2/opencv.hpp>
#include<opencv2/highgui.hpp>
using namespace std;
using namespace cv;//包含cv命名空间
int main()
{
Mat src_img = imread("01.bmp");//imread()函数载入图像
if (src_img.empty())
{
fprintf(stderr, "Can not load image\n");//如果读入图像失败,返回错误信息
return -1;
}
//显示图像
imshow("origin image and rotate operation", src_img);//imshow()函数显示图像
Mat des_img;
flip(src_img, des_img, 1);//1等正数代表水平方向旋转180度
imshow(" after rotate operation", des_img);//imshow()函数显示图像
waitKey();
return 0;
}
处理结果:
1.2 形态学处理
调用morphologyEx函数来实现形态学的相关操作。该函数的语法结构如下:
morphologyEx(InputArray src, OutputArray dst, int op, InputArray kernel, Point anchor=Point(-1,-1), intiterations=1, int borderType=BORDER_CONSTANT, const Scalar& borderValue=morphologyDefaultBorderValue() )
类型 | 说明 |
---|---|
MORPH_ERODE | 腐蚀 |
MORPH_DILATE | 膨胀 |
MORPH_OPEN | 开运算 |
MORPH_CLOSE | 闭运算 |
MORPH_GRADIENT | 形态学梯度 |
1.2.1 腐蚀
Mat element = getStructuringElement(MORPH_RECT, Size(3, 3));
morphologyEx(src_img, des_img, MORPH_ERODE, element);
1.2.2 膨胀
Mat element = getStructuringElement(MORPH_RECT, Size(3, 3));
morphologyEx(src_img, des_img, MORPH_DILATE, element);
1.3滤波处理
1.3.1 均值滤波
blur(src_img, dst_img, Size(3, 3));
1.3.2 高斯滤波
#include <opencv2/opencv.hpp>
#include<opencv2/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;//包含cv命名空间
int main()
{
Mat src_img = imread("01.bmp");//imread()函数载入图像
double sigmaX = 0;
double sigmaY = 0;
if (src_img.empty())
{
fprintf(stderr, "Can not load image\n");//如果读入图像失败,返回错误信息
return -1;
}
imshow("origin image", src_img);
Mat dst_img;
GaussianBlur(src_img, dst_img,Size(5,5),sigmaX,sigmaY);
imshow(" after operation", dst_img);
waitKey();
return 0;
}
1.3.3 中值滤波
medianBlur(src_img, dst_img, 3);
中值滤波可以去除椒盐噪声。
1.4 缩放
1.4.1 pyrUp函数
该函数实现图像金字塔操作的向上采样,即放大功能。
pyrUp(src_img, dst_img, Size(src_img.cols*2,src_img.rows*2));
使用该函数时,只能固定对图像进行2倍的长宽放大。
1.4.2 pyrDown函数
该函数实现图像金字塔操作的向下采样,即实现缩小功能。
pyrDown(src_img, dst_img, Size(src_img.cols/2,src_img.rows/2));
同样地,使用该函数时,只能固定对图像进行2倍的长宽缩小。
1.4.3 resize函数
resize(src_img, dst_img,Size(), 0.6, 0.6,INTER_LINEAR);
第2章 医学图像处理算法学习系统
2.1 增强
2.1.1 直方图均衡化
该函数只能实现单通道的直方图均衡化,因此在对多通道图像进行直方图均衡化时,要首先将其进行通道分离,逐个进行均衡化,最后再将各个通道的均衡结果进行合并,就可以得到最终的直方图均衡化结果。
vector<Mat> channels;//定义存储的容器
split(src_img, channels);
Mat bluechannel = channels[0];//b通道的图像
equalizeHist(bluechannel, bluechannel);//均衡化
Mat greenchannel = channels[1];//g通道的图像
equalizeHist(greenchannel, greenchannel);
Mat redchannel = channels[2];//r通道的图像
equalizeHist(redchannel, redchannel);
merge(channels, dst_img);//合并通道
imshow(" 均衡后的图像", dst_img);//imshow()函数显示图像
2.1.2 对比度亮度
对比度:
src_img.convertTo(dst_img, -1, 2);调整对比度,仅仅调整alpha的值,此实验alpha=2,beta=0
亮度:
src_img.convertTo(dst_img, -1, 1, 80);此时,aplha=1,beta=80.
2.2 卷积
此算子模板为:
0 -1 0
-1 5 -1
0 -1 0
Mat kernel = (Mat_<float>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
filter2D(src_img, dst_img, src_img.depth(), kernel);
2.3 重映射
把一幅图像内的像素点放置到另外一幅图像内的指定位置,称为重映射。
2.3.1 x轴
Mat map_x, map_y;
dst_img.create(src_img.size(), src_img.type());
map_x.create(src_img.size(), CV_32FC1);
map_y.create(src_img.size(), CV_32FC1);
for (int i = 0; i < src_img.rows; i++)
{
for (int j = 0; j < src_img.cols; j++)
{
map_x.at<float>(i, j) = src_img.cols - j;
map_y.at<float>(i, j) = i;
}
}
remap(src_img, dst_img, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
2.3.2 缩小
#include <opencv2/opencv.hpp>
#include<opencv2/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;//包含cv命名空间
int main()
{
Mat src_img = imread("cat1.jpg");//imread()函数载入图像
if (src_img.empty())
{
fprintf(stderr, "Can not load image\n");//如果读入图像失败,返回错误信息
return -1;
}
//显示图像
imshow("原始图像", src_img);//imshow()函数显示图像
Mat dst_img;
Mat map_x, map_y;
dst_img.create(src_img.size(), src_img.type());
map_x.create(src_img.size(), CV_32FC1);
map_y.create(src_img.size(), CV_32FC1);
for (int i = 0; i < src_img.rows; i++)
{
for (int j = 0; j < src_img.cols; j++)
{
if (i > src_img.rows * 0.25 && i < src_img.rows * 0.75 && j > src_img.cols * 0.25 && j<src_img.cols * 0.75)
{
map_x.at<float>(i, j) = 2 * (j - src_img.cols * 0.25) + 0.5;
map_y.at<float>(i, j) = 2 * (i - src_img.rows * 0.25) + 0.5;
}
else
{
map_x.at<float>(i,j) = 1;//随机选取
map_y.at<float>(i,j) = 1;//随机选取
}
}
}
//调用remap
remap(src_img, dst_img, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
imshow(" 操作后图像", dst_img);//imshow()函数显示图像
waitKey();
return 0;
}
2.4 分割
阈值分割是图像分割过程中常用额一种方法,它利用图像中像素点像素值的大小差异,通过设置阈值的方式将图像中的像素点进行分类,从而达到图像分割的目的。
2.4.1 基本阈值分割
cvtColor(src_img, src_img_gray, COLOR_RGB2GRAY);
int threshold_value = 128;
int max_BINARY_value = 255;
int threshold_type = THRESH_BINARY;
threshold(src_img_gray, dst_img, threshold_value, max_BINARY_value, threshold_type);
threshold函数定义如下:
threshold(InputArray src,OutputArray dst,double thresh,double maxval,int type)
阈值分割的类型如下表所示:
当阈值分割的类型选择THRESH_BINARY时,分割的结果图如下所示:
当阈值分割的类型选择THRESH_BINARY_INV时,分割结果图如下:
当阈值分割的类型选择THRESH_TRUNC时,分割结果图如下:
2.4.2 自适应阈值分割
自适应阈值分割的函数为adaptiveThreshold()
adaptiveThreshold(InputArray src,OutputArray dst,double maxValue,int adaptiveMethod,int thresholdType,int blocksize,double C)
参数adaptiveMethod:自适应阈值分割方法,具体方法如下:
类型 | 定义 |
---|---|
ADAPTIVE_THRESH_MEAN_C | 阈值T(x,y)的值为以当前像素点为中心的bolcksize*blocksize领域内的平均值减去C的值 |
ADAPTIVE_THRESH_GAUSSIAN_C | 阈值T(x,y)的值为以当前像素点为中心的bolcksize*blocksize领域内的加权和减去C的值 |
cvtColor(src_img, src_img_gray, COLOR_RGB2GRAY);
int maxValue = 255;
int adaptiveMethod = ADAPTIVE_THRESH_MEAN_C;
int thresholdType = THRESH_BINARY;
int blocksize = 7;
double C = 1;
adaptiveThreshold(src_img_gray, dst_img, maxValue, adaptiveMethod, thresholdType,blocksize,C);
当adaptiveMethod选择为ADAPTIVE_THRESH_MEAN_C时,结果图如下:
当adaptiveMethod选择为ADAPTIVE_THRESH_GAUSSIAN_C时结果图如下:
第三章 图像边缘检测学习系统
通过边缘检测算子找到图像的边缘,这些边缘通常实在图像在灰度、色彩、纹理等方面的不连续位置。
3.1 Sobel
Sobel边缘检测算子是一种离散的微分算子,该算子结合了高斯平滑和微分求导运算。
#include <opencv2/opencv.hpp>
#include<opencv2/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;//包含cv命名空间
int main()
{
Mat src_img = imread("cat1.jpg");//imread()函数载入图像
if (src_img.empty())
{
fprintf(stderr, "Can not load image\n");//如果读入图像失败,返回错误信息
return -1;
}
//显示图像
imshow("原始图像", src_img);//imshow()函数显示图像
Mat dst_img;
Sobel(dst_img, dst_img, src_img.depth(),0,1);
imshow(" Sobel算子", dst_img);//imshow()函数显示图像
waitKey();
return 0;
}
3.2 Canny
Canny边缘检测算法可分为5个步骤:
1.应用高斯滤波去除图像噪声。
2.计算梯度值的强度信息。
3.应用非最大化抑制去除边缘检测散杂相应。该步骤就是边缘细化的过程。
4.应用双阈值确定潜在的边缘信息。
5.滞后跟踪边缘:通过抑制与强边缘无连接的弱边缘来检测实际的边缘。
Mat dst_img;
cvtColor(src_img, dst_img, COLOR_BGR2GRAY);
Canny(dst_img, dst_img, 30, 100);
imshow(" Canny算子", dst_img);
3.3 Laplacian
Laplacian算子是一种二阶导数算子,具有旋转不变性,可以满足不同走向的图像边缘锐化的要求。
Laplacian(src_img, dst_img, src_img.depth(),ksize=5);
Laplacian(src_img, dst_img, src_img.depth(),ksize=3);
3.4 Scharr
Scharr滤波器是对Sobel算子的改进。
Scharr(src_img, dst_img, src_img.depth(), 0, 1);
第四章 数字图像加密学习系统
4.1 混沌系统
该系统对初始值极为敏感,初始值稍微不同,就会出现完全不同的结果。
1.生成黑白图像:
下图生成的是一个黑白混沌图像:
#include <opencv2/opencv.hpp>
#include<opencv2/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;//包含cv命名空间
int main()
{
int M = 379;
int N = 301;
float chaotic[379 * 301];
//混沌序列赋初值
double t=0.98;
int i;
chaotic[0] = t;
//生成混沌序列
for (i = 1; i < M * N; i++)
chaotic[i] = 1 - 2 * chaotic[i - 1] * chaotic[i - 1];
//将混沌序列二值化,只有0和255两个值
for (i = 0; i < M * N; i++)
if (chaotic[i] > 0)
chaotic[i] = 255;
else
chaotic[i] = 0;
//建立一个混沌图像
Mat chaoticImg(M, N, CV_8UC1);
int flag = 0;
for (int y = 0; y < M; y++)
{
uchar* chaoticImgR = chaoticImg.ptr<uchar>(y);
for (int x = 0; x < N; x++)
{
chaoticImgR[x] = chaotic[flag];
flag++;
}
}
imshow("test1", chaoticImg);
waitKey();
return 0;
}
2.生成二值图像:
3.生成灰度图像:
mod(a,b)是取模函数,返回a除以b的余数。灰度图像GOI中像素点的值区间为0-255。
//处理为256个灰度级
for (i = 0; i < M * N; i++)
{
tmp = chaotic[i] * 1000000;
chaotic[i]=tmp % 256;
}
//将上面混沌序列二值化部分替换成处理256个灰度级就可得到一个灰度混沌图像
4.生成彩色图像:
生成彩色图像时,首先分别生成R,G,B这三个颜色空间,然后将这3个色彩空间组合成彩色图像。
R色彩空间生成的方式为:
G色彩空间生成的方式为:
B色彩空间生成的方式为:
#include <opencv2/opencv.hpp>
#include<opencv2/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;//包含cv命名空间
int main()
{
int M = 200;
int N = 200;
float chaotic[200 * 200 * 3];
double t=0.98;
int i;
chaotic[0] = t;
//给整个混沌序列赋值
for (i = 1; i < M * N * 3; i++)
chaotic[i] = 1 - 2 * chaotic[i - 1] * chaotic[i - 1];
int tmp;
//处理为256个灰度级
for (i = 0; i < M * N * 3; i++)
{
tmp = chaotic[i] * 10000;
chaotic[i]=tmp % 256;
}
//定义一个混沌图像
Mat chaoticImg(M, N, CV_8UC3);
int flag = 0;
//将混沌序列的值赋给混沌图像
for (int y = 0; y < M; y++)
{
uchar* chaoticImgR = chaoticImg.ptr<uchar>(y);
for (int x = 0; x < N; x++)
{
chaoticImgR[3*x] = chaotic[flag];
chaoticImgR[3 * x + 1] = chaotic[flag + M * N];
chaoticImgR[3 * x + 2] = chaotic[flag + 2 * M * N];
flag = flag + 1;
}
}
Mat chaoticImgShow;
chaoticImg.copyTo(chaoticImgShow);
//色彩空间转换
cvtColor(chaoticImg, chaoticImg, COLOR_BGR2RGB);
imshow("test3", chaoticImgShow);
waitKey();
return 0;
}
4.2 异或加密
根据异或运算的规则,假设xor(a,b)=c,则可以得到xor(a,c)=b,xor(b,c)=a。
1.加密过程
原始图像O与选定的加密密钥图像K进行异或运算
#include <opencv2/opencv.hpp>
#include<opencv2/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;//包含cv命名空间
void creatChaoticImage(float init,InputArray src_img,OutputArray chaoticImg)
{
int MM, NN;
MM = src_img.rows();
NN = src_img.cols();
//定义一个数组,赋混沌初始值
float chaotic[200* 200 * 3];
//混沌系统赋初始值
int i;
chaotic[0] = init;
//计算混沌序列的值
for (i = 1; i < MM * NN * 3; i++)
chaotic[i] = 1 - 2 * chaotic[i - 1] * chaotic[i - 1];
int tmp;
//处理为256个灰度级
for (i = 0; i < MM * NN * 3; i++)
{
tmp = chaotic[i] * 10000;
chaotic[i] = tmp % 256;
}
//定义一个混沌图像
Mat chaoticImgT(MM, NN, CV_8UC3);
int flag = 0;
//将混沌序列的值赋给混沌图像
for (int y = 0; y < MM; y++)
{
uchar* chaoticImgR = chaoticImgT.ptr<uchar>(y);
for (int x = 0; x < NN; x++)
{
chaoticImgR[3 * x] = chaotic[flag];
chaoticImgR[3 * x + 1] = chaotic[flag + MM * NN];
chaoticImgR[3 * x + 2] = chaotic[flag + 2 * MM * NN];
flag = flag + 1;
}
}
chaoticImgT.copyTo(chaoticImg);
return;
}
int main()
{
//读入原始图像
Mat src_img=imread("1.bmp");
imshow("原始图像", src_img);
Mat dst_img;
double t=0.98;
Mat chaoticImg;
creatChaoticImage(t,src_img,chaoticImg);
bitwise_xor(src_img, chaoticImg, dst_img);
imshow("加密后的图像",dst_img);
Mat m(src_img.size(), CV_8UC3);
dst_img.copyTo(m);
imwrite("1.0.bmp", m);
waitKey();
return 0;
}
2.解密过程
加密图像OS与密钥图像K进行按位异或运算,得到原图像O。
4.3 置乱加密
置乱加密是将原始图像信息的位置进行重新排列,让图像看起来杂乱无章,从而实现加密的效果。
1.置乱过程:
用混沌系统产生一个长度为n的数列CI,将该数列进行排序,从而得到一个升序数列CIS。通过计算数列CIS中的每个数据在数列CI中的下标,得到一个数列I。置乱时,将待置乱的数列W按照数列I进行置乱排序得到WO。
#include <opencv2/opencv.hpp>
#include<opencv2/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;//包含cv命名空间
int main()
{
//读入原始图像
Mat src_img = imread("1.bmp");
imshow("原始图像", src_img);
double t = 0.98;
int i,j;
int MM, NN;
MM = src_img.rows;
NN = src_img.cols;
double *chaotic = new double[MM * NN * 3];
double *chaoticSort = new double[MM * NN * 3];
int* chaoticIndex = new int[MM * NN * 3];
chaotic[0] = t;
for (i = 1; i < MM * NN * 3; i++)
chaotic[i] = 1 - 2 * chaotic[i - 1] * chaotic[i - 1];
memcpy(chaoticSort, chaotic, MM * NN * 3 * sizeof(double));
//对chaoticSort进行排序
sort(chaoticSort, chaoticSort + MM * NN * 3);
//生成索引序列,计算chaoticSort在chaotic内的位置信息
for (i = 0; i < MM * NN * 3; i++)
{
for (j = 0; j < MM * NN * 3; j++)
if (chaoticSort[i] == chaotic[j])
chaoticIndex[i] = j;
}
int flag = 0;
Mat dst_img;
src_img.copyTo(dst_img);
//对图像进行加密
for (int i = 0; i < MM; i++)
{
uchar *r0 = dst_img.ptr<uchar>(i);
for (int j = 0; j < NN * 3; j++)
{
uchar* p0 = src_img.ptr<uchar>(chaoticIndex[flag] / (NN * 3));
r0[j] = p0[chaoticIndex[flag] % (NN * 3)];
flag++;
}
}
imshow("置乱加密后的图像",dst_img);
Mat m(src_img.size(), CV_8UC3);
dst_img.copyTo(m);
imwrite("2.bmp", m);
waitKey();
return 0;
}
2.逆置乱过程:
将需要置乱的数列标记为WO*,应用数列I实现对WO*进行逆置乱。
由于混沌系统自身对初始值的敏感性、不可预知性等特点,保证了上述置乱过程、逆置乱过程的安全性和可靠性。