在本教程中,您将学习如何:
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main(int argc, char *argv[])
{
//加载源图像,并检查它加载是否没有任何问题,然后显示它:
CommandLineParser parser( argc, argv, "{@input | cards.png | input image}" );
Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ) );
if( src.empty() )
{
cout << "Could not open or find the image!\n" << endl;
cout << "Usage: " << argv[0] << " " << endl;
return -1;
}
// 显示原图像
imshow("Source Image", src);
// 将背景从白色变为黑色,因为这将有助于在使用距离变换时提取更好的结果
Mat mask;
inRange(src, Scalar(255, 255, 255), Scalar(255, 255, 255), mask);
src.setTo(Scalar(0, 0, 0), mask);
// 显示黑色背景的图像
imshow("Black Background Image", src);
// 创建一个我们将用来锐化我们的图像的核
Mat kernel = (Mat_<float>(3,3) <<
1, 1, 1,
1, -8, 1,
1, 1, 1);
// 二阶导数的近似,一个相当强的核可以很好地进行拉普拉斯滤波
// 我们需要float类型来转换所有的东西,因为核有一些负值,我们期望得到一个负的拉普拉斯像
// 但是一个8位无符号整数(我们正在处理的)可以包含0到255的值, 所以可能的负数会被截断
Mat imgLaplacian;
filter2D(src, imgLaplacian, CV_32F, kernel);
Mat sharp;
src.convertTo(sharp, CV_32F);
Mat imgResult = sharp - imgLaplacian;
// 转换回8位
imgResult.convertTo(imgResult, CV_8UC3);
imgLaplacian.convertTo(imgLaplacian, CV_8UC3);
// imshow( "Laplace Filtered Image", imgLaplacian );
imshow( "New Sharped Image", imgResult );
// 现在我们将锐化图像分别转换为灰度和二值图像:
Mat bw;
cvtColor(imgResult, bw, COLOR_BGR2GRAY);
threshold(bw, bw, 40, 255, THRESH_BINARY | THRESH_OTSU);
imshow("Binary Image", bw);
// 现在我们准备对二值图像应用距离变换。此外,我们对输出图像进行了归一化,以便能够对结果进行可视化和阈值操作:
Mat dist;
distanceTransform(bw, dist, DIST_L2, 3);
// 将距离归一化到{0.0, 1.0},这样我们就可以可视化并设定阈值
normalize(dist, dist, 0, 1.0, NORM_MINMAX);
imshow("Distance Transform Image", dist);
// 获取峰值的阈值
// 这将是前景对象的标记
threshold(dist, dist, 0.4, 1.0, THRESH_BINARY);
// 对dist进行膨胀操作
Mat kernel1 = Mat::ones(3, 3, CV_8U);
dilate(dist, dist, kernel1);
imshow("Peaks", dist);
// 创建dist图像的CV_8U版本, findContours()需要它
Mat dist_8u;
dist.convertTo(dist_8u, CV_8U);
// 发现所有标记
vector<vector<Point> > contours;
findContours(dist_8u, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
// 为分水岭算法创建标记图像
Mat markers = Mat::zeros(dist.size(), CV_32S);
// 绘制前景标记
for (size_t i = 0; i < contours.size(); i++)
{
drawContours(markers, contours, static_cast<int>(i), Scalar(static_cast<int>(i)+1), -1);
}
// 绘制背景标记
circle(markers, Point(5,5), 3, Scalar(255), -1);
Mat markers8u;
markers.convertTo(markers8u, CV_8U, 10);
imshow("Markers", markers8u);
// 执行分水岭算法
watershed(imgResult, markers);
Mat mark;
markers.convertTo(mark, CV_8U);
bitwise_not(mark, mark);
// imshow("Markers_v2", mark); // 如果您想查看标记图像此时的样子,请取消注释
// 生成随机颜色
vector<Vec3b> colors;
for (size_t i = 0; i < contours.size(); i++)
{
int b = theRNG().uniform(0, 256);
int g = theRNG().uniform(0, 256);
int r = theRNG().uniform(0, 256);
colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
}
// 创建结果图像
Mat dst = Mat::zeros(markers.size(), CV_8UC3);
// 用随机的颜色填充已标记的物体
for (int i = 0; i < markers.rows; i++)
{
for (int j = 0; j < markers.cols; j++)
{
int index = markers.at<int>(i,j);
if (index > 0 && index <= static_cast<int>(contours.size()))
{
dst.at<Vec3b>(i,j) = colors[index-1];
}
}
}
// 可视化最终结果
imshow("Final Result", dst);
waitKey();
return 0;
}
from __future__ import print_function
import cv2 as cv
import numpy as np
import argparse
import random as rng
rng.seed(12345)
parser = argparse.ArgumentParser(description='Code for Image Segmentation with Distance Transform and Watershed Algorithm.\
Sample code showing how to segment overlapping objects using Laplacian filtering, \
in addition to Watershed and Distance Transformation')
parser.add_argument('--input', help='Path to input image.', default='cards.png')
args = parser.parse_args()
src = cv.imread(cv.samples.findFile(args.input))
if src is None:
print('Could not open or find the image:', args.input)
exit(0)
# 显示原图像
cv.imshow('Source Image', src)
src[np.all(src == 255, axis=2)] = 0
# 显示输出图像
cv.imshow('Black Background Image', src)
kernel = np.array([[1, 1, 1], [1, -8, 1], [1, 1, 1]], dtype=np.float32)
// 二阶导数的近似,一个相当强的核可以很好地进行拉普拉斯滤波
// 我们需要float类型来转换所有的东西,因为核有一些负值,我们期望得到一个负的拉普拉斯像
// 但是一个8位无符号整数(我们正在处理的)可以包含0到255的值, 所以可能的负数会被截断
imgLaplacian = cv.filter2D(src, cv.CV_32F, kernel)
sharp = np.float32(src)
imgResult = sharp - imgLaplacian
#转换回8位灰度
imgResult = np.clip(imgResult, 0, 255)
imgResult = imgResult.astype('uint8')
imgLaplacian = np.clip(imgLaplacian, 0, 255)
imgLaplacian = np.uint8(imgLaplacian)
#cv.imshow('Laplace Filtered Image', imgLaplacian)
cv.imshow('New Sharped Image', imgResult)
# 现在我们将锐化图像分别转换为灰度和二值图像:
bw = cv.cvtColor(imgResult, cv.COLOR_BGR2GRAY)
_, bw = cv.threshold(bw, 40, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
cv.imshow('Binary Image', bw)
# 现在我们准备对二值图像应用距离变换。此外,我们对输出图像进行了归一化,以便能够对结果进行可视化和阈值操作:
dist = cv.distanceTransform(bw, cv.DIST_L2, 3)
# 将距离归一化到{0.0, 1.0},这样我们就可以可视化并设定阈值
cv.normalize(dist, dist, 0, 1.0, cv.NORM_MINMAX)
cv.imshow('Distance Transform Image', dist)
_, dist = cv.threshold(dist, 0.4, 1.0, cv.THRESH_BINARY)
# 对dist图像进行膨胀操作
kernel1 = np.ones((3,3), dtype=np.uint8)
dist = cv.dilate(dist, kernel1)
cv.imshow('Peaks', dist)
dist_8u = dist.astype('uint8')
# 发现所有标记
contours, _ = cv.findContours(dist_8u, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
# 为分水岭算法创建标记图像
markers = np.zeros(dist.shape, dtype=np.int32)
# 绘制前景标记
for i in range(len(contours)):
cv.drawContours(markers, contours, i, (i+1), -1)
# 绘制背景标记
cv.circle(markers, (5,5), 3, (255,255,255), -1)
markers_8u = (markers * 10).astype('uint8')
cv.imshow('Markers', markers_8u)
cv.watershed(imgResult, markers)
#mark = np.zeros(markers.shape, dtype=np.uint8)
mark = markers.astype('uint8')
mark = cv.bitwise_not(mark)
# // 如果您想查看标记图像此时的样子,请取消注释
#cv.imshow('Markers_v2', mark)
# 生成随机颜色
colors = []
for contour in contours:
colors.append((rng.randint(0,256), rng.randint(0,256), rng.randint(0,256)))
# 创建结果图像
dst = np.zeros((markers.shape[0], markers.shape[1], 3), dtype=np.uint8)
# 用随机的颜色填充已标记的物体
for i in range(markers.shape[0]):
for j in range(markers.shape[1]):
index = markers[i,j]
if index > 0 and index <= len(contours):
dst[i,j,:] = colors[index-1]
# 可视化最终结果
cv.imshow('Final Result', dst)
cv.waitKey()
https://docs.opencv.org/4.x/d2/dbd/tutorial_distance_transform.html