我的编程空间,编程开发者的网络收藏夹
学习永远不晚

OpenCV使用邻居访问扫描图像的操作方法

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

OpenCV使用邻居访问扫描图像的操作方法

0. 前言

在图像处理中,有时需要根据某个像素的相邻像素的值计算该像素位置的值。当这个邻域包括上一行和下一行的像素时,就需要同时扫描图像的多行像素,本节中,我们将介绍如何通过邻居访问扫描图像。

1. 图像锐化

为了说明邻域扫描方法,我们将应用一个基于拉普拉斯算子的处理函数来锐化图像。在图像处理,如果从图像中减去它的拉普拉斯算子,图像边缘会被放大,从而得到更清晰的图像。锐化值计算如下:

sharpened_pixel= 5*current-left-right-up-down;

其中,left 是紧接在当前像素左侧的像素,up 是上一行的邻居像素,依此类推。接下来,我们介绍如何实现锐化函数。

2. 邻居访问扫描图像

(1) 我们将创建一个带有输入和输出图像的锐化函数,并不使用原地处理,即函数需要提供输出图像:

void sharpen(const cv::Mat &image, cv::Mat &result)

(2) 分配输出结果图像,通过 channels() 函数获取输入图像的通道数:

result.create(image.size(), image.type());
int nchannels= image.channels();

(3) 接下来,我们循环处理图像中的每一行。图像扫描使用三个指针完成,一个指向当前行,一个指向前一行,另一个指向下一行。此外,由于每个像素计算都需要访问其邻居,因此无法计算图像第一行和最后一行的像素以及第一列和最后一列的像素的值:

for (int j=1; j<image.rows-1; j++) {
    const uchar* previous = image.ptr<const uchar>(j-1);
    const uchar* current = image.ptr<const uchar>(j);
    const uchar* next = image.ptr<const uchar>(j+1);
    uchar* output = result.ptr<uchar>(j);
    for (int i=channels; i<(image.cols-1)*nchannels; i++){
        *output++ = cv::saturate_cast<uchar>(
                    5*current[i]-current[i-nchannels]-current[i+nchannels]-previous[i]-next[i]);
    }
}

以上代码可以在灰度和彩色图像上工作。如果我们将此函数应用于测试彩色图像,可以得到以下结果:

为了访问前一行和下一行的相邻像素,必须定义附加指针,然后在扫描循环内访问这些行中的像素。
在计算输出像素值时,会根据运算结果调用 cv::saturate_cast 模板函数,这是因为应用于像素的数学表达式可能会导致超出允许像素值范围的结果(即低于 0 或高于 255)。解决方案是将像素值重置到 [0, 255] 范围内,将负值改为 0 并将超过 255 的值改为 255,这正是 cv::saturate_cast<uchar> 函数的作用。此外,如果输入参数是浮点数,则结果将四舍五入为最接近的整数。我们也可以将此函数与其他类型一起使用,以确保结果保持在此类型定义的范围内。
由于其邻域未完全定义而无法处理的边界像素需要单独处理。在这里,我们简单的将它们设为 0;在复杂情况下,可以对这些像素执行特殊计算,但在大多数情况下,花时间处理这些极少数像素是没有意义的。我们可以使用两种特殊的方法将这些边缘像素设置为 0,可以使用 rowcol,它们返回一个特殊的 cv::Mat 实例,该实例由参数中指定的单行感兴趣区域 (region of interest, ROI) (或单列 ROI) 组成。这里不需要进行复制,因为如果修改这个一维矩阵的元素,它们在原始图像中也会被修改,我们可以通过调用 setTo() 方法实现,setTo() 方法可以为矩阵的所有元素分配值:

result.row(0).setTo(cv::Scalar(0));

以上代码可以将值 0 分配给结果图像第一行的所有像素。在三通道彩色图像的情况下,需要使用 cv::Scalar(a,b,c) 指定要分配给像素的每个通道的三个值。

3. 锐化滤波器

当对像素邻域进行计算时,通常用核矩阵表示它,核描述了如何组合计算中涉及的像素以获得所需的结果。本节中使用的锐化滤波器核如下:

通常,当前像素对应于核的中心,核的每个单元格中的值表示乘以相应像素的因子。然后将计算所有乘法的总和得到核应用于像素的结果。核的大小对应于邻域的大小(此处为 3 x 3)。使用这种表示,可以看出,锐化滤波器的计算方法:当前像素的水平和垂直邻居乘以 -1,而当前像素乘以 5。将核应用于图像不仅仅是一种方便的表示,同时也是信号处理中卷积概念的基础,核定义了一个应用于图像的滤波器。
由于滤波是图像处理中的一个常见操作,OpenCV 定义了一个特殊的函数来执行这个任务——cv::filter2D 函数。要使用此函数,只需要使用矩阵的形式定义一个核,然后使用图像和核调用该函数,并返回滤波后的图像。因此,使用 cv::filter2D 函数,可以很容易重新定义锐化函数:

void sharpen2D(const cv::Mat &image, cv::Mat &result) {
    // 创建3x3核,所有元素初始化为0
    cv::Mat kernel(3, 3, CV_32F, cv::Scalar(0));
    // 为核赋值
    kernel.at<float>(1,1) = 5.0;
    kernel.at<float>(0,1) = -1.0;
    kernel.at<float>(2,1) = -1.0;
    kernel.at<float>(1,0) = -1.0;
    kernel.at<float>(1,2) = -1.0;
    // 图像滤波
    cv::filter2D(image, result, image.depth(), kernel);
}

使用此函数可以得到与上一小节中代码完全相同的结果(并且具有相同的效率),如果输入彩色图像,则相同的内核将应用于所有三个通道。在使用较大尺寸的核时,cv::filter2D 函数更加高效。

4. 完整代码

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

void sharpen(const cv::Mat &image, cv::Mat &result) {
    result.create(image.size(), image.type());
    int nchannels = image.channels();
    for (int j=1; j<image.rows-1; j++) { // 循环除第一行和最后一行外的所有行
        const uchar* previous = image.ptr<const uchar>(j-1);
        const uchar* current = image.ptr<const uchar>(j);
        const uchar* next = image.ptr<const uchar>(j+1);
        uchar* output = result.ptr<uchar>(j);
        for (int i=nchannels; i<(image.cols-1)*nchannels; i++) {
            *output++ = cv::saturate_cast<uchar>(5*current[i]-current[i-nchannels]-current[i+nchannels]-previous[i]-next[i]);
        }
    }
    // 将未处理的像素置0
    result.row(0).setTo(cv::Scalar(0));
    result.row(result.rows-1).setTo(cv::Scalar(0));
    result.col(0).setTo(cv::Scalar(0));
    result.col(result.cols-1).setTo(cv::Scalar(0));
}

// 使用迭代器,该函数的输入图像必须为灰度图像
void sharpenIterator(const cv::Mat &image, cv::Mat &result) {
    // 输入图像必须为灰度图像
    CV_Assert(image.type()==CV_8UC1);
    // 初始化迭代器
    cv::Mat_<uchar>::const_iterator it = image.begin<uchar>() + image.cols;
    cv::Mat_<uchar>::const_iterator itend = image.end<uchar>() - image.cols;
    cv::Mat_<uchar>::const_iterator itup = image.begin<uchar>();
    cv::Mat_<uchar>::const_iterator itdown = image.begin<uchar>() + 2*image.cols;
    // 设置输出图像和迭代器
    result.create(image.size(), image.type());
    cv::Mat_<uchar>::iterator itout = result.begin<uchar>() + result.cols;
    for (; it!=itend; ++it,++itout,++itup,++itdown) {
        *itout = cv::saturate_cast<uchar>(*it * 5 - *(it-1) - *(it+1) - *itup - *itdown);
    }
    // 将未处理的像素置0
    result.row(0).setTo(cv::Scalar(0));
    result.row(result.rows-1).setTo(cv::Scalar(0));
    result.col(0).setTo(cv::Scalar(0));
    result.col(result.cols-1).setTo(cv::Scalar(0));
}

// 使用核
void sharpen2D(const cv::Mat &image, cv::Mat &result) {
    // 构造3x3核,并将所有元素初始化为0
    cv::Mat kernel(3, 3, CV_32F, cv::Scalar(0));
    // 为核元素赋值
    kernel.at<float>(1, 1) = 5.0;
    kernel.at<float>(0, 1) = -1.0;
    kernel.at<float>(2, 1) = -1.0;
    kernel.at<float>(1, 0) = -1.0;
    kernel.at<float>(1, 2) = -1.0;
    // 图像滤波
    cv::filter2D(image, result, image.depth(), kernel);
}

int main() {
    cv::Mat image = cv::imread("1.png");
    if (!image.data) return 0;
    cv::Mat result;
    double time = static_cast<double>(cv::getTickCount());
    sharpen(image, result);
    time = (static_cast<double>(cv::getTickCount())-time) / cv::getTickFrequency();
    std::cout << "time = " << time << "s" << std::endl;
    cv::namedWindow("Image");
    cv::imshow("Image", result);
    // 使用灰度模式打开图像
    image = cv::imread("1.png", 0);
    time = static_cast<double>(cv::getTickCount());
    sharpenIterator(image, result);
    time = (static_cast<double>(cv::getTickCount())-time) / cv::getTickFrequency();
    std::cout << "time gray level = " << time << "s" << std::endl;
    cv::namedWindow("Sharpened Image");
    cv::imshow("Sharpened Image", result);
    // 测试sharpen2D
    image = cv::imread("1.png");
    time = static_cast<double>(cv::getTickCount());
    sharpen2D(image, result);
    time = (static_cast<double>(cv::getTickCount())-time) / cv::getTickFrequency();
    std::cout << "time sharpen 2D = " << time << "s" << std::endl;
    cv::namedWindow("Image Filter 2D");
    cv::imshow("Image Filter 2D", result);
    cv::waitKey();
    return 0;
}

到此这篇关于OpenCV使用邻居访问扫描图像的文章就介绍到这了,更多相关OpenCV邻居访问扫描图像内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

OpenCV使用邻居访问扫描图像的操作方法

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

OpenCV使用邻居访问扫描图像的操作方法

在图像处理中,有时需要根据某个像素的相邻像素的值计算该像素位置的值,当这个邻域包括上一行和下一行的像素时,就需要同时扫描图像的多行像素,本节中我们将介绍如何通过邻居访问扫描图像,感兴趣的朋友一起看看吧
2023-01-05

使用路由器功能实现主机跨网访问的操作方法

  路由器是互联网络中必不可少的网络设备之一,路由器是一种连接多个网络或网段的网络设备,它能将不同网络或网段之间的数据信息进行"翻译",以使它们能够相互"读"懂对方的数据,从而构成一个更大的网络,使整个网络互通数据,提高数据传输效率。在这里,小编为大家分享的教程是:使用路由器功能实现主机跨网访问的操作方法。  我们应该
使用路由器功能实现主机跨网访问的操作方法
2024-04-18

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录