为了提升自己对Opencv中Mat数据类型的熟悉和掌握程度,自己尝试着写了一下Laplace图像锐化函数,一路坎坷,踩坑不断。现将代码分享如下:

#include <opencv2/opencv.hpp>
#include <iostream> using namespace std;
using namespace cv; //Laplace滤波锐化图像
void myLaplace(Mat Src, Mat Tem, Mat Dst)
{
int SrcH = Src.rows;
int SrcW = Src.cols;
int TemH = Tem.rows;
int TemW = Tem.cols; //检测模板行列是否为奇数
if (TemH * TemW % 2 == 0)
{
cerr << "模板规格错误" << endl;
return;
} //用于存储中间过程的计算结果。在进行滤波变换时,会有少量的行列遍历不到,为避免未遍历到的行列对结果的影响,因此将整个矩阵初始化为0,
Mat IntDst(SrcH, SrcW, CV_32SC1, Scalar(0)); //计算锐化后的掩模
char* pTem = (char*)Tem.data;//Mat.data默认指针类型为 uchar*,在不同应用场合下要进行相应的类型转换
for (int i = 0; i < SrcH - 2; i++)
{ //Mat的各行都是连续存储的,但行与行之间不一定定是连续的,最好用哪行就取出对应行的首地址
int* pSrc1 = Src.ptr<int>(i);
int* pSrc2 = Src.ptr<int>(i +1);
int* pSrc3 = Src.ptr<int>(i + 2);
int* pIntDst = IntDst.ptr<int>(i + 1);
for (int j = 0; j < SrcW - 2; j++)
{
//pSrc1[ j ]为当前模板作用邻域左上角地址
pIntDst[ j + 1 ] = pSrc1[ j ] * pTem[ 0 ] + pSrc1[ j + 1 ] * pTem[ 1 ] + pSrc1[ j + 2 ] * pTem[ 2 ]\
+ pSrc2[ j ] * pTem[ 3 ] + pSrc2[ j + 1 ] * pTem[ 4 ] + pSrc2[ j + 2 ] * pTem[ 5 ]\
+ pSrc3[ j ] * pTem[ 6 ] + pSrc3[ j + 1 ] * pTem[ 7 ] + pSrc3[ j + 2 ] * pTem[ 8 ];
}
} //将滤波处理后的信息加到原图上
addWeighted(IntDst, 1, Src, 1, 0.0, IntDst); //求最小值,将基准拉到0
double minNum, maxNum;
Point minLoc, maxLoc;
minMaxLoc(IntDst, &minNum, &maxNum, &minLoc, &maxLoc);
minNum = (int)minNum; for (int i = 0; i < SrcH; i++)
{
int* pIntDst = IntDst.ptr<int>(i);
for (int j = 0; j < SrcW; j++)
{
pIntDst[ j ] -= minNum;
}
} //求最大值,将整体范围标定至0--255
double newMinNum, newMaxNum;
Point newMinLoc, newMaxLoc;
minMaxLoc(IntDst, &newMinNum, &newMaxNum, &newMinLoc, &newMaxLoc);
newMaxNum = (int)newMaxNum; for (int i = 0; i < SrcH; i++)
{
int* pIntDst = IntDst.ptr<int>(i);
uchar* pDst = Dst.ptr<uchar>(i);
for (int j = 0; j < SrcW; j++)
{
pIntDst[ j ] = pIntDst[ j ] * 255 / newMaxNum;
pDst[ j ] = (uchar) pIntDst[ j ];
}
}
} //将uchar型Mat矩阵写入int型Mat矩阵(后续计算像素值会超过0--255范围)
void UChar2Int(Mat inputMat, Mat outputMat)
{
for (int i = 0; i < inputMat.rows; i++)
{
uchar* pInputMat = inputMat.ptr<uchar>(i);
int* pOutputMat = outputMat.ptr<int>(i);
for (int j = 0; j < inputMat.cols; j++)
{
pOutputMat[j] = (int)pInputMat[j];
}
}
} int main()
{
Mat mColorImage = imread("color.jpg");
Mat mImage = imread("color.jpg", 0);//读取灰度图
if (mColorImage.data == 0)
{
cerr << "彩图读取错误" << endl;
return -1;
} if (mImage.data == 0)
{
cerr << "灰图读取错误" << endl;
return -1;
} //创建3X3 Laplace算子
char templateArray[3][3] = { {-1, -1, -1}, {-1, 8, -1}, {-1, -1, -1} };
Mat mTemplate(3, 3, CV_8SC1, templateArray); //创建盛放输入信息的Mat矩阵
Mat mIntImage(mImage.rows, mImage.cols, CV_32SC1, Scalar(0));
UChar2Int(mImage, mIntImage); //创建盛放输出信息的Mat矩阵(输出灰度范围在0--255间,一定要存储在uchar中。若存放在int中,显示时默认共有2的32次方个灰度级,0--255范围过窄且靠近0,显示黑屏)
Mat mOutputImage(mImage.rows, mImage.cols, CV_8UC1, Scalar(0)); //进行Laplace锐化并显示
myLaplace(mIntImage, mTemplate, mOutputImage);
namedWindow("彩图", WINDOW_NORMAL);
imshow("彩图", mColorImage);
namedWindow("灰图", WINDOW_NORMAL);
imshow("灰图", mImage);
namedWindow("Laplace锐化", WINDOW_NORMAL);
imshow("Laplace锐化", mOutputImage);
waitKey();
destroyAllWindows(); return 0;
}

对图像进行Laplace锐化时,最令人头痛的就是数据类型的转换了。众所周知,一般的灰度图256个灰度级,在Mat中存储的数据类型都是uchar,即CV_8UC1,但进行线性运算后,矩阵中的部分数值会小于0,也有部分数值会大于255,就超出了uchar能表示的极限范围。此时就要用int,即CV_32SC1, Mat矩阵数据在两种类型之间转换时麻烦且容易出错。现将本次踩的坑与收获经验分享如下,若能助人,不胜荣幸:

1.在创建并初始化Mat时,发现了一种直接用数组初始化Mat矩阵的方法,前提是数组和矩阵大小相同且元素数据类型保持一致。

	char templateArray[3][3] = { {-1, -1, -1}, {-1, 8, -1}, {-1, -1, -1} };
Mat mTemplate(3, 3, CV_8SC1, templateArray);

2.用Mat.data获取到的指针类型默认为uchar*型的,而与矩阵中元素的数据类型无关。使用时要注意指针类型的转化。

3.灰度图Mat矩阵中的元素多数是uchar(CV_8UC1)型的,有时需要访问其中的单个元素(像素值)并用"cout<<"输出。需要注意的是,用"cout<<"输出char/uchar型数据时,输出的并不是数字数据,而是数字对应的ASCII码字符,若对应的字符不可打印,则显示输出为空。若要求输出数字数据,可使用类型强制转换后输出(如:cout<<(int)num<<endl;)。

4.Mat的各行数据在内存中都是连续存储的,但行与行之间的地址不一定连续。因此需要用哪行的数据,就最好先获得对应行的首地址(uchar* p = image.ptr<uchar>(i),获取第i行首地址)。(在一篇博客上看到的,真伪待考证,不过谨慎点总是好的)。

5.用imshow()显示Mat矩阵存储的图像信息时,若元素的数据类型是uchar(CV_8UC1)的,就默认有256(2的8次方)个灰度级;若元素的数据类型是用int(CV_32SC1)的,就默认有2147483647 (2的32次方)个灰度级。普通灰度图的灰度值都在0-255之间,在CV_8UC1下能够正常显示。要是将其数据类型转化为CV_32SC1的,0-255的灰度值在2147483647的尺度下就显得范围过窄且无限靠近于0,用imshow()显示时显示窗口就会一片黑暗。

注:错误之处,敬请雅正!

C++ Opencv Mat类型使用的几个注意事项及自写函数实现Laplace图像锐化的更多相关文章

  1. OpenCV Mat 类型定义和赋值

    1.一般的Mat定义方法:cv::Mat M(height,width,<Type>),例: cv::Mat M(480,640,CV_8UC3); 表示定义了一个480行640列的矩阵, ...

  2. 在MFC中显示图片(opencv Mat类型)

    1,在MFC窗体中添加picture control控件,并添加对应的变量名 2,在窗体的初始化窗口中添加: namedWindow(); HWND hWnd = (HWND)cvGetWindowH ...

  3. opencv中的Mat类型

    Mat类型主要是跟matlab中的数据类型一样.故用起来很方便. Mat最大的优势跟STL很相似,都是对内存进行动态的管理,不需要之前用户手动的管理内存,对于一些大型的开发,有时候投入的lpImage ...

  4. opencv中Mat类型数据操作与遍历

    Mat作为opencv中一种数据类型常常用来存储图像,相对与以前的IplImgae类型来说,Mat类型省去了人工的对内存的分配与释放,转而自动分配释放.Mat Class主要包括两部个数据部分:一个是 ...

  5. OpenCV不同类型Mat的at方法访问元素时该如何确定模板函数的typename(转)

    自从OpenCV推出了Mat后越来越像是Matlab了,使用起来方便了很多,但是,在用at方法访问Mat时,如何选用合适的typename类型来访问相应的Mat元素是个头疼的问题. 比如: int H ...

  6. OpenCV——Mat、CvMat、IplImage类型浅析【转】

    OpenCV中常见的与图像操作有关的数据容器有Mat,cvMat和IplImage. 一.Mat类型:矩阵类型,Matrix. 在openCV中,Mat是一个多维的密集数据数组.可以用来处理向量和矩阵 ...

  7. Opencv Mat的三种常用类型简介

    本系列文章由 @YhL_Leo 出品,转载请注明出处. 文章链接: http://blog.csdn.net/yhl_leo/article/details/47683127 本文主要介绍Opencv ...

  8. OpenCV中图像以Mat类型保存时各通道数据在内存中的组织形式及python代码访问各通道数据的简要方式

    以最简单的4 x 5三通道图像为例,其在内存中Mat类型的数据组织形式如下: 每一行的每一列像素的三个通道数据组成一个一维数组,一行像素组成一个二维数组,整幅图像组成一个三维数组,即: Mat.dat ...

  9. 网络流中的图像转化为OpenCV中的Mat类型

    1,从网络中读取到的图像流,不支持查找,不能直接转化为Mat类型 2,例子如下: string Url = "http://192.168.0.110/cgi-bin/camera?reso ...

随机推荐

  1. C运算符

    运算符是一种告诉编译器执行特定的数学或逻辑操作的符号. C 语言内置了丰富的运算符,并提供了以下类型的运算符: 算术运算符.关系运算符.逻辑运算符.位运算符.赋值运算符.杂项运算符 C 中的运算符优先 ...

  2. Scania SDP3 2.38.2.37.0 Download, Install, Activate: Confirmed

    Download: Scania Diagnos & Programmer SDP3 2.38.2.37.0 free version and tested version SDP3 2.38 ...

  3. Pyhon入门基础(1)---Pycharm安装及破解

    一.下载安装 1.首先我们可以对比一下社区版和专业版的区别: 2.下载地址:https://www.jetbrains.com/pycharm/download/ 当我们开发的项目比较大的时候通常会涉 ...

  4. java 日志体系(三)log4j从入门到详解

    java 日志体系(三)log4j从入门到详解 一.Log4j 简介 在应用程序中添加日志记录总的来说基于三个目的: 监视代码中变量的变化情况,周期性的记录到文件中供其他应用进行统计分析工作: 跟踪代 ...

  5. laravel-更换语言包

    第一步:找语言包 找到比较靠谱的语言包(根据下载量与收藏量综合判断),而且要是laravel的 扩展的链接:https://packagist.org/packages/caouecs/laravel ...

  6. django2.0 + python3.6 在centos7 下部署生产环境的一些注意事项

    一:mysql 与环境选用的坑 目前, 在生产环境部署django有三种方式: 1. apache + mod_wsgi 2. nginx + uwsigi 3. nginx + supervisor ...

  7. 防止用户重发发生ajax请求

    1.前端限制 点击提交后,将该元素禁用,等待请求结束后再次释放(解除禁用). 可以使用ajax中的 success 请求成功后的回调函数进行按钮释放. 2.防抖动 暴力连续点击按钮,可以通过闭包里的  ...

  8. linux下部署tomcat 上线jpress博客系统

    tomcat Tomcat服务器是一个免费的开放源代码的Web应用服务器,属于轻量级应用服务器. tomcat有三个端口 开始部署 安装tomcat 第一步:下载tomcat 安装包 tomcat官网 ...

  9. 利用insertd查出更新记录

    DROP TABLE #tableCREATE TABLE #table(CompanyID VARCHAR(20),BillNo VARCHAR(40))UPDATE dbo.SD_Inv_Move ...

  10. mac 清理

    1.iOS DeviceSupport   -- ~/Library/Developer/Xcode/iOS DeviceSupport 这个可重新生成!在连接旧设备调试时,会重新自动生成. 2.iP ...