擴張(Dilation)和侵蝕(Erosion)、開運算(Opening)、閉運算(Closing)是影像處理中的形態學運算,用於處理影像和物體分割。這些運算可以去除雜訊、修復斷裂、填充空洞,以及幫助識別物體特徵。在影像處理中有廣泛的應用。
擴張的原理基於一個稱為結構元素的核,在影像上做摺積。以下是擴張運算的原理:
侵蝕(Erosion)原理一樣是基於一個結構元素的核,在影像上做摺積。以下是侵蝕運算的原理:
開運算是由侵蝕(Erosion)後接著擴張(Dilation)所組成的運算。運算的步驟如下:
閉運算是由擴張(Dilation)後接著侵蝕(Erosion)所組成的運算。運算的步驟如下:
假設圖片是白底黑字,需要先將圖片轉成負片,將圖片的每一個元素對255相減。透過大津二值化取得一個二進位的圖片,顯示在"Original"視窗上。
cv::threshold(255- image, binary_image, 0, 255, cv::THRESH_OTSU);
onErode
、onDilate
函式:當滑塊"Kernel Size"滑動時。進行侵蝕或擴張運算,首先檢查核的大小是否為偶數,如果是,將大小增加1,以確保使用的結構元素的大小是奇數,才會有中心點的產生。使用 cv::getStructuringElement
建立一個矩形結構元素,並使用 cv::erode
或cv::dilate
函數對二值影像進行侵蝕、擴張運算,最後將結果顯示在"Erode"、"Dilate"視窗上。
void onErode(int kernel_size, void*) {
if (kernel_size % 2 == 0)
kernel_size++;
cv::Mat dst;
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT,cv::Size(kernel_size,kernel_size));
cv::erode(binary_image,dst,kernel);
cv::imshow("Erode", dst);
}
#include <iostream>
#include <opencv2/opencv.hpp>
#include "opencv2/core/utils/logger.hpp"
using namespace std;
void onErode(int kernel_size, void*);
void onDilate(int kernel_size, void*);
void onOpening(int kernel_size, void*);
void onClosing(int kernel_size, void*);
cv::Mat binary_image;
int main()
{
cv::utils::logging::setLogLevel(cv::utils::logging::LOG_LEVEL_ERROR);
cv::Mat image = cv::imread("C:\\Users\\vince\\Downloads\\test_image.jpg",cv::IMREAD_GRAYSCALE);
cv::threshold(255- image, binary_image, 0, 255, cv::THRESH_OTSU);
cv::namedWindow("Original", cv::WindowFlags::WINDOW_NORMAL);
cv::resizeWindow("Original",cv::Size(512*(float)image.cols/image.rows,512));
cv::imshow("Original",binary_image);
cv::namedWindow("Dilate", cv::WindowFlags::WINDOW_NORMAL);
cv::resizeWindow("Dilate",cv::Size(512*(float)image.cols/image.rows,512));
cv::imshow("Dilate",binary_image);
cv::createTrackbar("Kernel Size", "Dilate", NULL, 100,onDilate);
cv::namedWindow("Erode", cv::WindowFlags::WINDOW_NORMAL);
cv::resizeWindow("Erode",cv::Size(512*(float)image.cols/image.rows,512));
cv::imshow("Erode",binary_image);
cv::createTrackbar("Kernel Size", "Erode", NULL, 100,onErode);
cv::namedWindow("Opening", cv::WindowFlags::WINDOW_NORMAL);
cv::resizeWindow("Opening",cv::Size(512*(float)image.cols/image.rows,512));
cv::imshow("Opening",binary_image);
cv::createTrackbar("Kernel Size", "Opening", NULL, 100,onOpening);
cv::namedWindow("Closing", cv::WindowFlags::WINDOW_NORMAL);
cv::resizeWindow("Closing",cv::Size(512*(float)image.cols/image.rows,512));
cv::imshow("Closing",binary_image);
cv::createTrackbar("Kernel Size", "Closing", NULL, 100,onClosing);
cv::waitKey(0);
return 0;
}
void onErode(int kernel_size, void*) {
if (kernel_size % 2 == 0)
kernel_size++;
cv::Mat dst;
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT,cv::Size(kernel_size,kernel_size));
cv::erode(binary_image,dst,kernel);
cv::imshow("Erode", dst);
}
void onDilate(int kernel_size, void*) {
if (kernel_size % 2 == 0)
kernel_size++;
cv::Mat dst;
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT,cv::Size(kernel_size,kernel_size));
cv::dilate(binary_image,dst,kernel);
cv::imshow("Dilate", dst);
}
void onOpening(int kernel_size, void*) {
if (kernel_size % 2 == 0)
kernel_size++;
cv::Mat dst;
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT,cv::Size(kernel_size,kernel_size));
cv::erode(binary_image,dst,kernel);
cv::dilate(dst,dst,kernel);
cv::imshow("Opening", dst);
}
void onClosing(int kernel_size, void*) {
if (kernel_size % 2 == 0)
kernel_size++;
cv::Mat dst;
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT,cv::Size(kernel_size,kernel_size));
cv::dilate(binary_image,dst,kernel);
cv::erode(dst,dst,kernel);
cv::imshow("Closing", dst);
}
這是一張帶有椒鹽雜訊的圖片,雜訊有黑色也有白色,可以看到文字被白色的雜訊侵蝕,在W的字元上甚至出現斷裂。
這張就是經過負運算以後的影像,白色是我們要處理的目標。
經過擴張運算以後,可以看到文字上的空缺被填滿了,但是噪點也被放大了。
經過侵蝕運算以後,可以白色的噪點被侵蝕掉了,但是文字內的空缺也更大了。
進行開運算以後,噪點被消除,但是文字的輪廓發生斷裂的現象。
進行開運算以後,原先文字斷裂或空缺的部分被消除,但是噪點仍存在,有些文字甚至因為過度擴張出現相連的情況。