Morphological Operations: A set of operations that process images based on shapes.Morphological operations apply a structuring element to an input image and generate an output image.
The most basic morphological operations are two: Erosion and Dilation. They have a wide array of uses, i.e.:
(1)、Removing noise.
(2)、Isolation of individual elements and joining disparate elements in an image.
(3)、Finding of intensity bumps or holes in an image.
数学形态学可以理解为一种滤波行为,因此也称为形态学滤波。滤波中用到的滤波器(kernal),在形态学中称为结构元素。结构元素往往是由一个特殊的形状构成,如线条、矩形、圆等。
开运算(open):先腐蚀后膨胀的过程。开运算可以用来消除小黑点,在纤细点处分离物体、平滑较大物体的边界的同时并不明显改变其面积。
闭运算(close):先膨胀后腐蚀的过程。闭运算可以用来排除小黑洞。
形态学梯度(morph-grad):可以突出团块(blob)的边缘,保留物体的边缘轮廓。
顶帽(top-hat):将突出比原轮廓亮的部分。
黑帽(black-hat):将突出比原轮廓暗的部分。
目前fbc_cv库中支持uchar和float两种数据类型,经测试,与OpenCV3.1结果完全一致。
实现代码morphologyEx.hpp:
// fbc_cv is free software and uses the same licence as OpenCV
// Email: fengbingchun@163.com
#ifndef FBC_CV_MORPHOLOGYEX_HPP_
#define FBC_CV_MORPHOLOGYEX_HPP_
/* reference: include/opencv2/imgproc.hpp
modules/imgproc/src/morph.cpp
*/
#include
#include "erode.hpp"
#include "dilate.hpp"
namespace fbc {
// perform advanced morphological transformations using an erosion and dilation as basic operations
// In case of multi - channel images, each channel is processed independently.
// morphologyEx can be applied several ( iterations ) times.
// op ==> enum MorphTypes
// support type: uchar/float, multi-channels
template
int morphologyEx(const Mat_<_Tp, chs>& src, Mat_<_Tp, chs>& dst, int op, const Mat_& kernel,
Point anchor = Point(-1, -1), int iterations = 1, int borderType = BORDER_CONSTANT, const Scalar& borderValue = Scalar::all(DBL_MAX))
{
FBC_Assert(typeid(uchar).name() == typeid(_Tp).name() || typeid(float).name() == typeid(_Tp).name()); // uchar || float
if (dst.empty()) {
dst = Mat_<_Tp, chs>(src.rows, src.cols);
} else {
FBC_Assert(src.rows == dst.rows && src.cols == dst.cols);
}
Mat_ kernel_ = kernel;
if (kernel_.empty()) {
kernel_ = Mat_(3, 3);
getStructuringElement(kernel_, MORPH_RECT, Size(3, 3), Point(1, 1));
}
switch (op) {
case MORPH_ERODE: {
erode(src, dst, kernel_, anchor, iterations, borderType, borderValue);
break;
}
case MORPH_DILATE: {
dilate(src, dst, kernel_, anchor, iterations, borderType, borderValue);
break;
}
case MORPH_OPEN: {
erode(src, dst, kernel_, anchor, iterations, borderType, borderValue);
dilate(dst, dst, kernel_, anchor, iterations, borderType, borderValue);
break;
}
case CV_MOP_CLOSE: {
dilate(src, dst, kernel_, anchor, iterations, borderType, borderValue);
erode(dst, dst, kernel_, anchor, iterations, borderType, borderValue);
break;
}
case CV_MOP_GRADIENT: {
Mat_<_Tp, chs> temp(src.rows, src.cols);
erode(src, temp, kernel_, anchor, iterations, borderType, borderValue);
dilate(src, dst, kernel_, anchor, iterations, borderType, borderValue);
dst -= temp;
break;
}
case CV_MOP_TOPHAT: {
Mat_<_Tp, chs> temp(src.rows, src.cols);
if (src.data != dst.data)
temp = dst;
erode(src, temp, kernel_, anchor, iterations, borderType, borderValue);
dilate(temp, temp, kernel_, anchor, iterations, borderType, borderValue);
dst = src - temp;
break;
}
case CV_MOP_BLACKHAT: {
Mat_<_Tp, chs> temp(src.rows, src.cols);
if (src.data != dst.data)
temp = dst;
dilate(src, temp, kernel_, anchor, iterations, borderType, borderValue);
erode(temp, temp, kernel_, anchor, iterations, borderType, borderValue);
dst = temp - src;
break;
}
case MORPH_HITMISS: {
FBC_Assert(typeid(uchar).name() == typeid(_Tp).name() && chs == 1);
Mat_ k1 = (kernel_ == Mat_(kernel_.rows, kernel_.cols, Scalar::all(1)));
Mat_ k2 = (kernel_ == Mat_(kernel_.rows, kernel_.cols, Scalar::all(-1)));
Mat_<_Tp, chs> e1, e2;
if (countNonZero(k1) <= 0)
e1 = src;
else
erode(src, e1, k1, anchor, iterations, borderType, borderValue);
if (countNonZero(k2) <= 0) {
e2 = src;
} else {
Mat_<_Tp, chs> src_complement;
bitwise_not(src, src_complement);
erode(src_complement, e2, k2, anchor, iterations, borderType, borderValue);
}
bitwise_and(e1, e2, dst);
break;
}
default:
FBC_Assert("unknown morphological operation");
}
return 0;
}
} // namespace fbc
#endif // FBC_CV_MORPHOLOGYEX_HPP_
测试代码test_morphologyEx.cpp:
#include "test_erode.hpp"
#include
#include
#include
int test_morphologyEx_uchar()
{
cv::Mat matSrc = cv::imread("E:/GitCode/OpenCV_Test/test_images/lena.png", 1);
if (!matSrc.data) {
std::cout << "read image fail" << std::endl;
return -1;
}
int width = matSrc.cols;
int height = matSrc.rows;
for (int elem = 0; elem < 3; elem++) {
for (int size = 0; size < 5; size++) {
for (int iterations = 1; iterations < 3; iterations++) {
for (int operation = 0; operation < 7; operation++) {
int type;
if (elem == 0){ type = fbc::MORPH_RECT; }
else if (elem == 1){ type = fbc::MORPH_CROSS; }
else if (elem == 2) { type = fbc::MORPH_ELLIPSE; }
fbc::Mat_ element(2 * size + 1, 2 * size + 1);
fbc::getStructuringElement(element, type, fbc::Size(2 * size + 1, 2 * size + 1), fbc::Point(size, size));
int type_;
if (elem == 0){ type_ = cv::MORPH_RECT; }
else if (elem == 1){ type_ = cv::MORPH_CROSS; }
else if (elem == 2) { type_ = cv::MORPH_ELLIPSE; }
cv::Mat element_ = cv::getStructuringElement(type_, cv::Size(2 * size + 1, 2 * size + 1), cv::Point(size, size));
assert(element.rows == element_.rows && element.cols == element.cols && element.step == element_.step);
for (int y = 0; y < element.rows; y++) {
const fbc::uchar* p1 = element.ptr(y);
const uchar* p2 = element_.ptr(y);
for (int x = 0; x < element.step; x++) {
assert(p1[x] == p2[x]);
}
}
fbc::Mat3BGR mat1(height, width, matSrc.data);
fbc::Mat3BGR mat2(height, width);
fbc::morphologyEx(mat1, mat2, operation, element, fbc::Point(-1, -1), iterations, 0, fbc::Scalar::all(128));
cv::Mat mat1_(height, width, CV_8UC3, matSrc.data);
cv::Mat mat2_;
cv::morphologyEx(mat1_, mat2_, operation, element_, cv::Point(-1, -1), iterations, 0, cv::Scalar::all(128));
assert(mat2.rows == mat2_.rows && mat2.cols == mat2_.cols && mat2.step == mat2_.step);
for (int y = 0; y < mat2.rows; y++) {
const fbc::uchar* p1 = mat2.ptr(y);
const uchar* p2 = mat2_.ptr(y);
for (int x = 0; x < mat2.step; x++) {
assert(p1[x] == p2[x]);
}
}
}
}
}
}
return 0;
}
int test_morphologyEx_float()
{
cv::Mat matSrc = cv::imread("E:/GitCode/OpenCV_Test/test_images/lena.png", 1);
if (!matSrc.data) {
std::cout << "read image fail" << std::endl;
return -1;
}
cv::cvtColor(matSrc, matSrc, CV_BGR2GRAY);
matSrc.convertTo(matSrc, CV_32FC1);
int width = matSrc.cols;
int height = matSrc.rows;
for (int elem = 0; elem < 3; elem++) {
for (int size = 0; size < 5; size++) {
for (int iterations = 1; iterations < 3; iterations++) {
for (int operation = 0; operation < 7; operation++) {
int type;
if (elem == 0){ type = fbc::MORPH_RECT; }
else if (elem == 1){ type = fbc::MORPH_CROSS; }
else if (elem == 2) { type = fbc::MORPH_ELLIPSE; }
fbc::Mat_ element(2 * size + 1, 2 * size + 1);
fbc::getStructuringElement(element, type, fbc::Size(2 * size + 1, 2 * size + 1), fbc::Point(size, size));
int type_;
if (elem == 0){ type_ = cv::MORPH_RECT; }
else if (elem == 1){ type_ = cv::MORPH_CROSS; }
else if (elem == 2) { type_ = cv::MORPH_ELLIPSE; }
cv::Mat element_ = cv::getStructuringElement(type_, cv::Size(2 * size + 1, 2 * size + 1), cv::Point(size, size));
assert(element.rows == element_.rows && element.cols == element.cols && element.step == element_.step);
for (int y = 0; y < element.rows; y++) {
const fbc::uchar* p1 = element.ptr(y);
const uchar* p2 = element_.ptr(y);
for (int x = 0; x < element.step; x++) {
assert(p1[x] == p2[x]);
}
}
fbc::Mat_ mat1(height, width, matSrc.data);
fbc::Mat_ mat2(height, width);
fbc::morphologyEx(mat1, mat2, operation, element, fbc::Point(-1, -1), iterations, 0, fbc::Scalar::all(128));
cv::Mat mat1_(height, width, CV_32FC1, matSrc.data);
cv::Mat mat2_;
cv::morphologyEx(mat1_, mat2_, operation, element_, cv::Point(-1, -1), iterations, 0, cv::Scalar::all(128));
assert(mat2.rows == mat2_.rows && mat2.cols == mat2_.cols && mat2.step == mat2_.step);
for (int y = 0; y < mat2.rows; y++) {
const fbc::uchar* p1 = mat2.ptr(y);
const uchar* p2 = mat2_.ptr(y);
for (int x = 0; x < mat2.step; x++) {
assert(p1[x] == p2[x]);
}
}
}
}
}
}
return 0;
}
int test_morphologyEx_hitmiss()
{
cv::Mat matSrc = cv::imread("E:/GitCode/OpenCV_Test/test_images/lena.png", 1);
if (!matSrc.data) {
std::cout << "read image fail" << std::endl;
return -1;
}
cv::cvtColor(matSrc, matSrc, CV_BGR2GRAY);
int width = matSrc.cols;
int height = matSrc.rows;
for (int elem = 0; elem < 3; elem++) {
for (int size = 0; size < 5; size++) {
for (int iterations = 1; iterations < 3; iterations++) {
int operation = 7;
int type;
if (elem == 0){ type = fbc::MORPH_RECT; }
else if (elem == 1){ type = fbc::MORPH_CROSS; }
else if (elem == 2) { type = fbc::MORPH_ELLIPSE; }
fbc::Mat_ element(2 * size + 1, 2 * size + 1);
fbc::getStructuringElement(element, type, fbc::Size(2 * size + 1, 2 * size + 1), fbc::Point(size, size));
int type_;
if (elem == 0){ type_ = cv::MORPH_RECT; }
else if (elem == 1){ type_ = cv::MORPH_CROSS; }
else if (elem == 2) { type_ = cv::MORPH_ELLIPSE; }
cv::Mat element_ = cv::getStructuringElement(type_, cv::Size(2 * size + 1, 2 * size + 1), cv::Point(size, size));
assert(element.rows == element_.rows && element.cols == element.cols && element.step == element_.step);
for (int y = 0; y < element.rows; y++) {
const fbc::uchar* p1 = element.ptr(y);
const uchar* p2 = element_.ptr(y);
for (int x = 0; x < element.step; x++) {
assert(p1[x] == p2[x]);
}
}
fbc::Mat_ mat1(height, width, matSrc.data);
fbc::Mat_ mat2(height, width);
fbc::morphologyEx(mat1, mat2, operation, element, fbc::Point(-1, -1), iterations, 0, fbc::Scalar::all(128));
cv::Mat mat1_(height, width, CV_8UC1, matSrc.data);
cv::Mat mat2_;
cv::morphologyEx(mat1_, mat2_, operation, element_, cv::Point(-1, -1), iterations, 0, cv::Scalar::all(128));
assert(mat2.rows == mat2_.rows && mat2.cols == mat2_.cols && mat2.step == mat2_.step);
for (int y = 0; y < mat2.rows; y++) {
const fbc::uchar* p1 = mat2.ptr(y);
const uchar* p2 = mat2_.ptr(y);
for (int x = 0; x < mat2.step; x++) {
assert(p1[x] == p2[x]);
}
}
}
}
}
return 0;
}