欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

OpenCV使用鄰居訪問(wèn)掃描圖像的操作方法

 更新時(shí)間:2023年01月05日 09:54:33   作者:盼小輝丶  
在圖像處理中,有時(shí)需要根據(jù)某個(gè)像素的相鄰像素的值計(jì)算該像素位置的值,當(dāng)這個(gè)鄰域包括上一行和下一行的像素時(shí),就需要同時(shí)掃描圖像的多行像素,本節(jié)中我們將介紹如何通過(guò)鄰居訪問(wèn)掃描圖像,感興趣的朋友一起看看吧

0. 前言

在圖像處理中,有時(shí)需要根據(jù)某個(gè)像素的相鄰像素的值計(jì)算該像素位置的值。當(dāng)這個(gè)鄰域包括上一行和下一行的像素時(shí),就需要同時(shí)掃描圖像的多行像素,本節(jié)中,我們將介紹如何通過(guò)鄰居訪問(wèn)掃描圖像。

1. 圖像銳化

為了說(shuō)明鄰域掃描方法,我們將應(yīng)用一個(gè)基于拉普拉斯算子的處理函數(shù)來(lái)銳化圖像。在圖像處理,如果從圖像中減去它的拉普拉斯算子,圖像邊緣會(huì)被放大,從而得到更清晰的圖像。銳化值計(jì)算如下:

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

其中,left 是緊接在當(dāng)前像素左側(cè)的像素,up 是上一行的鄰居像素,依此類推。接下來(lái),我們介紹如何實(shí)現(xiàn)銳化函數(shù)。

2. 鄰居訪問(wèn)掃描圖像

(1) 我們將創(chuàng)建一個(gè)帶有輸入和輸出圖像的銳化函數(shù),并不使用原地處理,即函數(shù)需要提供輸出圖像:

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

(2) 分配輸出結(jié)果圖像,通過(guò) channels() 函數(shù)獲取輸入圖像的通道數(shù):

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

(3) 接下來(lái),我們循環(huán)處理圖像中的每一行。圖像掃描使用三個(gè)指針完成,一個(gè)指向當(dāng)前行,一個(gè)指向前一行,另一個(gè)指向下一行。此外,由于每個(gè)像素計(jì)算都需要訪問(wèn)其鄰居,因此無(wú)法計(jì)算圖像第一行和最后一行的像素以及第一列和最后一列的像素的值:

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]);
    }
}

以上代碼可以在灰度和彩色圖像上工作。如果我們將此函數(shù)應(yīng)用于測(cè)試彩色圖像,可以得到以下結(jié)果:

為了訪問(wèn)前一行和下一行的相鄰像素,必須定義附加指針,然后在掃描循環(huán)內(nèi)訪問(wèn)這些行中的像素。
在計(jì)算輸出像素值時(shí),會(huì)根據(jù)運(yùn)算結(jié)果調(diào)用 cv::saturate_cast 模板函數(shù),這是因?yàn)閼?yīng)用于像素的數(shù)學(xué)表達(dá)式可能會(huì)導(dǎo)致超出允許像素值范圍的結(jié)果(即低于 0 或高于 255)。解決方案是將像素值重置到 [0, 255] 范圍內(nèi),將負(fù)值改為 0 并將超過(guò) 255 的值改為 255,這正是 cv::saturate_cast<uchar> 函數(shù)的作用。此外,如果輸入?yún)?shù)是浮點(diǎn)數(shù),則結(jié)果將四舍五入為最接近的整數(shù)。我們也可以將此函數(shù)與其他類型一起使用,以確保結(jié)果保持在此類型定義的范圍內(nèi)。
由于其鄰域未完全定義而無(wú)法處理的邊界像素需要單獨(dú)處理。在這里,我們簡(jiǎn)單的將它們?cè)O(shè)為 0;在復(fù)雜情況下,可以對(duì)這些像素執(zhí)行特殊計(jì)算,但在大多數(shù)情況下,花時(shí)間處理這些極少數(shù)像素是沒(méi)有意義的。我們可以使用兩種特殊的方法將這些邊緣像素設(shè)置為 0,可以使用 rowcol,它們返回一個(gè)特殊的 cv::Mat 實(shí)例,該實(shí)例由參數(shù)中指定的單行感興趣區(qū)域 (region of interest, ROI) (或單列 ROI) 組成。這里不需要進(jìn)行復(fù)制,因?yàn)槿绻薷倪@個(gè)一維矩陣的元素,它們?cè)谠紙D像中也會(huì)被修改,我們可以通過(guò)調(diào)用 setTo() 方法實(shí)現(xiàn),setTo() 方法可以為矩陣的所有元素分配值:

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

以上代碼可以將值 0 分配給結(jié)果圖像第一行的所有像素。在三通道彩色圖像的情況下,需要使用 cv::Scalar(a,b,c) 指定要分配給像素的每個(gè)通道的三個(gè)值。

3. 銳化濾波器

當(dāng)對(duì)像素鄰域進(jìn)行計(jì)算時(shí),通常用核矩陣表示它,核描述了如何組合計(jì)算中涉及的像素以獲得所需的結(jié)果。本節(jié)中使用的銳化濾波器核如下:

通常,當(dāng)前像素對(duì)應(yīng)于核的中心,核的每個(gè)單元格中的值表示乘以相應(yīng)像素的因子。然后將計(jì)算所有乘法的總和得到核應(yīng)用于像素的結(jié)果。核的大小對(duì)應(yīng)于鄰域的大小(此處為 3 x 3)。使用這種表示,可以看出,銳化濾波器的計(jì)算方法:當(dāng)前像素的水平和垂直鄰居乘以 -1,而當(dāng)前像素乘以 5。將核應(yīng)用于圖像不僅僅是一種方便的表示,同時(shí)也是信號(hào)處理中卷積概念的基礎(chǔ),核定義了一個(gè)應(yīng)用于圖像的濾波器。
由于濾波是圖像處理中的一個(gè)常見(jiàn)操作,OpenCV 定義了一個(gè)特殊的函數(shù)來(lái)執(zhí)行這個(gè)任務(wù)——cv::filter2D 函數(shù)。要使用此函數(shù),只需要使用矩陣的形式定義一個(gè)核,然后使用圖像和核調(diào)用該函數(shù),并返回濾波后的圖像。因此,使用 cv::filter2D 函數(shù),可以很容易重新定義銳化函數(shù):

void sharpen2D(const cv::Mat &image, cv::Mat &result) {
    // 創(chuàng)建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);
}

使用此函數(shù)可以得到與上一小節(jié)中代碼完全相同的結(jié)果(并且具有相同的效率),如果輸入彩色圖像,則相同的內(nèi)核將應(yīng)用于所有三個(gè)通道。在使用較大尺寸的核時(shí),cv::filter2D 函數(shù)更加高效。

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++) { // 循環(huán)除第一行和最后一行外的所有行
        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));
}

// 使用迭代器,該函數(shù)的輸入圖像必須為灰度圖像
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;
    // 設(shè)置輸出圖像和迭代器
    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) {
    // 構(gòu)造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);
    // 使用灰度模式打開(kāi)圖像
    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);
    // 測(cè)試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;
}

到此這篇關(guān)于OpenCV使用鄰居訪問(wèn)掃描圖像的文章就介紹到這了,更多相關(guān)OpenCV鄰居訪問(wèn)掃描圖像內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • VS2022調(diào)試通過(guò)??禂z像頭煙火識(shí)別SDK的實(shí)現(xiàn)

    VS2022調(diào)試通過(guò)??禂z像頭煙火識(shí)別SDK的實(shí)現(xiàn)

    本文主要介紹了VS2022調(diào)試通過(guò)??禂z像頭煙火識(shí)別SDK的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • C++遞歸與分治算法原理示例詳解

    C++遞歸與分治算法原理示例詳解

    這篇文章主要為大家介紹了C++遞歸與分治算法的策略原理示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2021-11-11
  • C++?如何將Lambda轉(zhuǎn)換成函數(shù)指針

    C++?如何將Lambda轉(zhuǎn)換成函數(shù)指針

    這篇文章主要介紹了C++?如何將Lambda轉(zhuǎn)換成函數(shù)指針,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • 基于C++ bitset常用函數(shù)及運(yùn)算符(詳解)

    基于C++ bitset常用函數(shù)及運(yùn)算符(詳解)

    下面小編就為大家?guī)?lái)一篇基于C++ bitset常用函數(shù)及運(yùn)算符(詳解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-11-11
  • VC WinExec打開(kāi)指定程序或者文件的方法

    VC WinExec打開(kāi)指定程序或者文件的方法

    使用WinExec命令打開(kāi)指定程序或者文件的參數(shù)說(shuō)明
    2008-11-11
  • C語(yǔ)言編程C++自定義個(gè)性化類型

    C語(yǔ)言編程C++自定義個(gè)性化類型

    這篇文章主要介紹了C語(yǔ)言編程中如何來(lái)自定義C++個(gè)性化類型,文中附含詳細(xì)的示例代碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2021-09-09
  • Unity3D實(shí)現(xiàn)經(jīng)典小游戲Pacman

    Unity3D實(shí)現(xiàn)經(jīng)典小游戲Pacman

    這篇文章主要介紹了基于Unity3D制作一做個(gè)經(jīng)典小游戲Pacman,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Unity3D有一定的幫助,感興趣的小伙伴可以了解一下
    2021-12-12
  • C語(yǔ)言詳細(xì)分析貪心策略中最小生成樹(shù)的Prime算法設(shè)計(jì)與實(shí)現(xiàn)

    C語(yǔ)言詳細(xì)分析貪心策略中最小生成樹(shù)的Prime算法設(shè)計(jì)與實(shí)現(xiàn)

    最小生成樹(shù)的問(wèn)題還是比較熱門(mén)的,最經(jīng)典的莫過(guò)于Prime算法和Kruskal算法了,這篇博文我會(huì)詳細(xì)講解Prime算法的設(shè)計(jì)思想與具體代碼的實(shí)現(xiàn),不要求數(shù)據(jù)結(jié)構(gòu)學(xué)的有多好,只要跟著我的思路來(lái),一步一步的分析,調(diào)試,終能成就自己,那就讓我們開(kāi)始吧
    2022-05-05
  • C++ GDI實(shí)現(xiàn)圖片格式轉(zhuǎn)換

    C++ GDI實(shí)現(xiàn)圖片格式轉(zhuǎn)換

    GDI+(Graphics Device Interface Plus)是一種用于圖形繪制和圖像處理的應(yīng)用程序編程接口(API),在Windows平臺(tái)上廣泛使用,本文就來(lái)介紹一下如何使用GDI實(shí)現(xiàn)圖片格式轉(zhuǎn)換吧
    2023-12-12
  • C++中的類與對(duì)象深度解析

    C++中的類與對(duì)象深度解析

    這篇文章主要為大家詳細(xì)介紹了C++中的類與對(duì)象,使用數(shù)據(jù)庫(kù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02

最新評(píng)論