做淘宝客网站用什么系统吗,网站建设服务器介绍图片,wordpress 插件更新,龙泉市建设局网站什么是数字图像处理#xff1f; 当今时代#xff0c;数字图像无处不在。手机拍照、安防监控、医疗检查、地图导航、工业质检……我们每天都在接收、分析和处理大量图像信息。对于计算机而言#xff0c;图像并不是一张“看得懂”的照片#xff0c;而是由数值组成的矩阵。如何…什么是数字图像处理 当今时代数字图像无处不在。手机拍照、安防监控、医疗检查、地图导航、工业质检……我们每天都在接收、分析和处理大量图像信息。对于计算机而言图像并不是一张“看得懂”的照片而是由数值组成的矩阵。如何让机器也具备“看图”的能力正是数字图像处理的核心目标。 简而言之数字图像处理就是用计算机对图像进行操作和分析让图像更“清晰”、更“有用”、更“可识别”。举例如下
拍完照后用手机“自动美颜”一下,可能用到了滤波、边缘平滑、肤色增强等图像处理算法医生查看 CT 或眼底图时图像可能经过了对比度增强或伪彩色处理使细节更加清晰摄像头识别车辆车牌需要经过颜色识别、轮廓识别、字符识别等操作 什么是OpenCV? OpenCV(Open Source Computer Vision Library)是一个开源、跨平台的计算机视觉库最初由英特尔开发现在已经成为业界和学术界广泛使用的工具之一。OpenCV有如下特性
跨平台支持 Windows、Linux、macOS语言支持丰富C/Python 作为主流语言选择也有部分选择Java、JavaScript等实时性强底层基于 C/C速度快能胜任对性能要求高的实时应用功能强大从图像读取到复杂特征匹配从边缘检测到深度学习支持等 在我们的专栏中我们的示例主要使用C这是工程领域中最合适的使用方式。C提供的卓越的性能可以满足很多实时性的应用需求。同时我们也会适当给出一些Python示例在深度学习训练阶段Python是我们的首选语言一般选择pytorch框架。OpenCV可以对深度学习进行数据预处理支持。 对于OpenCV的安装Python环境下只需要运行以下命令即可 pip install opencv-python 对于C环境我们一般都是从源码直接编译然后再部署到自己的开发环境中。我们这里不讲如何源码编译大家可以在网上自行搜索。我们稍后会提供一个完整的C项目该项目会包含OpenCV所有的依赖库大家可以基于该项目进行自己的开发工作。 数字图像基本结构 上图为一个4行8列的矩阵每个元素的取值范围为[0,256)。我们可以将其看作为一个4*8的灰度图像灰度图像的取值范围为[0,256。在现实生活中我们更多看到的是彩色图像彩色图像相对于灰度图像来说每个元素需要3个值表示分别代表RedGreen和Blue其数据矩阵如下 以上同样为一个4行8列的矩阵但每个元素由一个3*1的向量构成如第0行0列的向量值为[172,47,117]这三个元素具体表示Blue172Green47Red117。特别注意这里的通道排列顺序为BGR而在生活中我们习惯称呼彩色图像为RGB图像。 OpenCV提供了函数cv::imread()该函数可读取多种格式图像如JPG, BMP, PNG等其返回值为cv::Mat对象该对象保存了图像相关的所有信息。不论读取哪种格式图像只要该图像为三通道数据读取后的图像在内存中的排列顺序均为BGR四通道多为BGRAA表示Alpha通道用于记录半透明相关信息。 OpenCV提供了函数cv::imshow()该函数用于显示图像其核心参数为cv::Mat对象。我们通过一个实验来加深通道排列顺序的理解。
import cv2 #导入opencv,可用于读取与显示图像
import matplotlib.pyplot as plt #用于图像显示img_bgr cv2.imread(lena.png) #读取图像, 默认通道为bgrif img_bgr is None:print(图像加载失败请检查路径是否正确。)
else:img_rgb cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) # 转换为rgb顺序cv2.imshow(opencv show, img_bgr) #使用opencv显示图像cv2.waitKey(0) #opencv需要调用该函数已阻止程序继续执行cv2.destroyAllWindows() #用户关闭图像窗口后清除资源#使用matplot显示图像这里需要传入rgb顺序图像plt.imshow(img_rgb)plt.title(matplot show)plt.show() 以上一段python代码首先使用OpenCV读取一张图像然后分别使用OpenCV与matplot库进行显示。需要特别注意在matplot库中默认将通道顺序解读为RGB。因此我们调用了cvtColor函数对其进行通道转换(cv2.COLOR_BGR2RGB)使得matplot可以正确显示图像颜色。以下分别给出正确通道顺序显示结果与错误通道顺序显示结果。 正确通道顺序显示结果 错误通道顺序显示结果 接下来我们给出一段C代码该代码实现了图像读取与显示处理语法上的差异与python代码基本一致。
int main()
{cv::Mat img_bgr cv::imread(lena.png, cv::IMREAD_COLOR);cv::imshow(opencv show, img_bgr);cv::waitKey(0);cv::destroyAllWindows();return 0;
} 数字图像元素读取与修改 到目前为止我们了解了图像数据的基本结构也能正确读取和显示图像。那么我们应该如何读取或修改图像单个元素数据呢 有如下方法可以读取或修改图像像素数据C如下
使用cv::Mat.at()方法该方法适合读取少量数据。由于函数会进行边界检查其速度较慢在图像处理算法实践中我们基本不会使用该函数读取数据。以下给出示例代码
{ // 读取100行150列数据该数据为三通道数据cv::Vec3b val img_bgr.atcv::Vec3b(100, 150); uchar b val[0];uchar g val[1];uchar r val[2];// 读取单通道数据即灰度图uchar gray img_bgr.atuchar(100, 150);
}
在实际项目中我们总是直接访问指针以获得最佳的访问效率以下给出示例代码
{// // 3通道图像bgr访问// 遍历每一行for (int row 0; row img_bgr.rows; row){// 获取每一行的起始指针cv::Vec3b* ptr img_bgr.ptrcv::Vec3b(row);// 遍历每一个元素cv::Vec3bfor (int col 0; col img_bgr.cols; col){// 获取每个通道的值uchar b ptr[col][0];uchar g ptr[col][1];uchar r ptr[col][2];// 每个通道亮度*2// 由于每个通道取值范围为[0,255]因此需要确保不越界b b * 2 255 ? 255 : b * 2;g g * 2 255 ? 255 : g * 2;r r * 2 255 ? 255 : r * 2;// 将修改后值赋给原通道ptr[col][0] b;ptr[col][1] g;ptr[col][2] r;}}// // 单通道灰度图像访问// 遍历每一行for (int row 0; row img_bgr.rows; row){// 获取每一行的起始指针uchar* ptr img_bgr.ptruchar(row);// 遍历每一个元素ucharfor (int col 0; col img_bgr.cols; col){// 获取灰度值uchar gray ptr[col];// 每个通道亮度*2// 由于取值范围为[0,255]因此需要确保不越界gray gray * 2 255 ? 255 : gray * 2;// 将修改灰度值赋给原图像ptr[col] gray;}}
} 通过以上程序我们可以得到一个亮度更高的图像效果如下 每个通道亮度翻倍后图像 虽然直接访问指针可以获得最佳的运行效率然而我们也可能因为访问不当而产生以下不良后果典型错误为内存越界错误这可能导致整个程序崩溃。所以在实际项目中我们需要慎重使用指针确保代码正确性以避免内存越界错误! 另外一些性能优化的常识可以让我们避免一些极端低效的代码如下代码大大降低运行效率
{// 该代码运行效率会非常低由于违背了内存连续性访问原则// 导致频繁的缓存命中失败严重降低数据访问效率// 遍历每一列for (int col 0; col img_bgr.cols; col){// 遍历每一行for (int row 0; row img_bgr.rows; row){cv::Vec3b val img_bgr.ptrcv::Vec3b(row)[col];uchar b val[0];uchar g val[1];uchar r val[2];}}
} 观察以上代码我们for循环顺序发生了改变该代码对图像元素的访问顺序为 0行0列-1行0列-2行0列...-0行1列-1行1列-2行1列.... 也就是说在列方向上遍历而图像元素在行方向上连续存储从而每次访问都可能导致缓存命中失败从而严重影响访问效率 一般情况下C提供了非常灵活的图像数据读取方式有时候我们可能也会使用Python进行少量的数据读取操作以下给出使用Python读取图像数据的方法
(b, g, r) img_bgr[100, 150] #获取第100行第150列的B、G、R通道值
blue_channel img_bgr[:, :, 0] #获取蓝通道数据 cv::Mat关键元素 cv::Mat是 OpenCV中最核心的数据结构之一用于表示图像、视频帧、矩阵等二维数据。理解 cv::Mat的内部结构对于高效图像处理非常关键。早期的C接口使用IplImage结构除了兼容需求我们不再使用IplImage接口了。 以下是cv::Mat的基本数据结构 cv::Mat ├── data → 指向图像数据的指针 ├── rows → 行数即图像高度 ├── cols → 列数即图像宽度 ├── step → 每行占用的字节数stride ├── channels → 通道数通过 type 解析 ├── type → 数据类型和通道数的编码 ├── depth() → 每个通道的数据类型如 CV_8U ├── refcount → 引用计数指针实现共享内存 └── others → flags、allocator 等 data为一个uchar*类型数据指向图像像素数据的首地址可以直接通过指针操作像素如
uchar* p img_bgr.data; p[0] 255; p[1] 255; rows和cols分别代表图像的行数与列数也即图像的高度与宽度。 step表示图像每一行占用的总字节数利用该数据可以准确跳转到每行数据首指针上以下两种写法均可以跳转到第10行首指针处故data1与data2为相等指针。
cv::Vec3b* data1 (cv::Vec3b*)(img_bgr.data img_bgr.step * 10);
cv::Vec3b* data2 img_bgr.ptrcv::Vec3b(10); type()函数返回一个整数该整数编码了通道数与数据类型信息。一般情况下我们可以分别调用channels()与depth()函数来分别获取通道数与数据类型。 在常规数字图像中通道数一般返回为134通道数据分别表示灰度图真彩色带Alpha通道真彩色。当然在其他应用中也可以返回任意通道如2通道可以编码图像梯度信息。 图像数据类型主要定义了数据精度与数据符号如CV_8U为8位无符号整数CV_8S为8位有符号整数CV_16U/CV_16S定义了16位整数CV_32S定义了32位有符号整数注意没有CV_32U!CV_32F/CV_64F分别定义了单精度与双精度浮点类型。
int depth img_bgr.depth();
int channels img_bgr.channels(); elemSize()表示一个像素占用的字节数elemSize1()表示一个通道占用的字节数使用elemSize() / elemSize1()可计算处通道数等价于channnels()函数。 refcount作为内存引用计数在浅拷贝时共享内存数据仅增加引用计数代码如下
cv::Mat img cv::imread(lena.png, cv::IMREAD_COLOR);int* ref img.refcount; // 引用计数为1cv::Mat img2 img; //浅拷贝img与img2公用内存int* ref2 img2.refcount; // 浅拷贝后引用计数增加到2img.release(); // 释放img引用计数减1int* ref3 img2.refcount; // 释放img后引用计数减少到1 除了浅拷贝之外我们在很多时候有深拷贝需求即不共享内存数据函数copyTo()与clone()均可实现该目标代码如下
// 方式 1clone返回新对象
cv::Mat img_clone img.clone();// 方式 2copyTo拷贝到已有对象
cv::Mat img_copy;
img.copyTo(img_copy); 结语 通过介绍数字图像处理与OpenCV的基本知识我们理解了数字图像的基本结构以及如何高效的访问图像中的任意元素。同时对通道顺序以及内存连续性问题进行特别讲解使得我们可以在工程实践中避免一些微妙的错误提升程序的效率。最后我们讲解了OpenCV中最为重要的数据结构cv::Mat通过该数据结构可以实现图像数据的所有基本操作。 在工程应用中为了运行效率我们一般会选择OpenCV的C接口。然而在某些情况下Python接口也发挥了重要的作用。如在深度学习的训练过程中我们一般使用pytorch框架。此时使用OpenCV的Python接口进行数据预处理是非常必要的。因此在博文中我们同步给出了C与Python代码片段以适应不同应用场景需求。