https://mp.weixin.qq.com/s?__biz=MzA4MTA1NjM5NQ==&mid=2247485202&idx=1&sn=05d0b4cd25675a99357910a5f2694508&chksm=9f9b80f6a8ec09e03ab2bb518ea6aad83db007c9cdd602c7459ed75c737e380ac9c34bc0d614&scene=178&cur_album_id=2073041855356764164#rd
https://mp.weixin.qq.com/s?__biz=MzA4MTA1NjM5NQ==&mid=2247485234&idx=1&sn=64c4a7235bd02a49d844089a84e6a01d&chksm=9f9b80d6a8ec09c029a387bbdeea3f8f7ecbc31e9032da047b6e77cf0394f272ada897c10f3b&scene=178&cur_album_id=2073041855356764164#rd
data:表示 Mat 对象中的指针(uchar 类型的指针),指向内存中存放矩阵数据的一块内存 (uchar* data)。
step: 字面意思是“步长”,实际上它描述了矩阵的形状。 step[] 为一个数组,矩阵有几维,step[] 数组就有几个元素。以一个三维矩阵为例,step[0] 表示一个平面的字节总数,step[1] 表示一行元素的字节总数,step[2] 表示每一个元素的字节总数。
上面两个数据可以配合使用来获得Mat中任意位置的数据
https://blog.csdn.net/u012566751/article/details/77046445
图像的二值化就是将图像上的像素点的灰度值设置为0或255,这样将使整个图像呈现出明显的黑白效果。在数字图像处理中,二值图像占有非常重要的地位,图像的二值化使图像中数据量大为减少,从而能凸显出目标的轮廓。OpenCV中提供了函数cv::threshold();
https://mp.weixin.qq.com/s?__biz=Mzg3NjY3NjI0Mw==&mid=2247484975&idx=1&sn=5ea64c7c4dd3c017eac0551f12c45137&chksm=cf2fd3d3f8585ac5b6e59f313a42d8981b8d09721f88eea7ee4a71ecfae7baeacf69fb0e1ec2&mpshare=1&scene=1&srcid=1030Tal3VDLygpiUNPLo45zv&sharer_shareinfo=84ef8c16db4fd26fa08ad0259bcc4dce&sharer_shareinfo_first=84ef8c16db4fd26fa08ad0259bcc4dce#rd
1.连通域概念:指图像中具有相同像素值且位置相邻的像素组成的区域。
2.相关函数:
int connectedComponents(InputArray image, OutputArray labels, int connectivity = 8, int ltype = CV_32S);
image:输入图像,待标记8位单通道图像。
labels:输出图像,目标标记图像。
connectivity:连通域大小,四连通域还是八连通域。
输出类型:CV32S 和 CV_16U ,默认是 CV_32S 。
返回连通域个数。
例子:
#include
#include
int main()
{
cv::Mat input = cv::imread("src/rice.jpg");
cv::Mat img_bw, connectimg;
cv::cvtColor(input, img_bw, cv::COLOR_BGR2GRAY);
cv::threshold(img_bw, img_bw, 100, 255, cv::THRESH_BINARY_INV);
// 做一些膨胀和腐蚀使得图像连通域明显
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5));
cv::erode(img_bw, img_bw, element, cv::Point(-1, -1), 2);
cv::dilate(img_bw, img_bw, element, cv::Point(-1, -1), 2);
int num = cv::connectedComponents(img_bw, connectimg, 8, CV_16U);
std::cout << "连通域个数:" << num << std::endl;
cv::namedWindow("原图", cv::WINDOW_NORMAL);
cv::imshow("原图", img_bw);
//以不同颜色标记出不同的连通域
cv::Mat result = cv::Mat::zeros(img_bw.size(), CV_8UC3); //定义标记结果图像
//定义五种颜色
std::vector<cv::Vec3b> color;
color.push_back(cv::Vec3b(0, 0, 255));
color.push_back(cv::Vec3b(0, 255, 0));
color.push_back(cv::Vec3b(255, 0, 0));
color.push_back(cv::Vec3b(0, 255, 255));
color.push_back(cv::Vec3b(255, 255, 0));
for (int i = 0; i < result.cols; i ++)
{
for (int j = 0; j < result.rows; j ++)
{
int label = connectimg.at<uint16_t>(i, j);
if (label == 0)
{
continue; //背景的黑色不改变
}
result.at<cv::Vec3b>(i, j) = color[label % 5];
}
}
cv::namedWindow("标记连通域后", cv::WINDOW_NORMAL);
cv::imshow("标记连通域后", result);
cv::waitKey(0);
return 0;
}
https://www.cnblogs.com/mtcnn/p/9411967.html
distanceTransform方法的功能:
用于计算图像中每一个非零点距离离自己最近的零点的距离,distanceTransform的第二个Mat矩阵参数dst保存了每一个点与最近的零点的距离信息,图像上越亮的点,代表了离零点的距离越远。
C++
#include "core/core.hpp"
#include "imgproc/imgproc.hpp"
#include "highgui/highgui.hpp"
using namespace cv;
int main(int argc,char *argv[])
{
float maxValue=0; //定义距离变换矩阵中的最大值
Point Pt(0,0);
Mat image=imread(argv[1]);
Mat imageGray;
cvtColor(image,imageGray,CV_RGB2GRAY);
imageGray=~imageGray; //取反
GaussianBlur(imageGray,imageGray,Size(5,5),2); //滤波
threshold(imageGray,imageGray,20,200,CV_THRESH_BINARY); //阈值化
Mat imageThin(imageGray.size(),CV_32FC1); //定义保存距离变换结果的Mat矩阵
distanceTransform(imageGray,imageThin,CV_DIST_L2,3); //距离变换
Mat distShow;
distShow=Mat::zeros(imageGray.size(),CV_8UC1); //定义细化后的字符轮廓
for(int i=0;i<imageThin.rows;i++)
{
for(int j=0;j<imageThin.cols;j++)
{
distShow.at<uchar>(i,j)=imageThin.at<float>(i,j);
if(imageThin.at<float>(i,j)>maxValue)
{
maxValue=imageThin.at<float>(i,j); //获取距离变换的极大值
Pt=Point(j,i); //坐标
}
}
}
normalize(distShow,distShow,0,255,CV_MINMAX); //为了显示清晰,做了0~255归一化
circle(image,Pt,maxValue,Scalar(0,0,255),3);
circle(image,Pt,3,Scalar(0,255,0),3);
imshow("Source Image",image);
imshow("Thin Image",distShow);
waitKey();
return 0;
}
1).原始图片:
2).经过距离变换后距离Mat矩阵dst:
为了显示清晰,做了0~255的归一化。可以看到,中心处最亮,说明了中心点距离零点的距离最远,而最远处就可以作为物体的质心。
3).标记质心(绿色点):
C++:
#include "core/core.hpp"
#include "imgproc/imgproc.hpp"
#include "highgui/highgui.hpp"
using namespace cv;
int main(int argc,char *argv[])
{
float maxValue=0; //定义距离变换矩阵中的最大值
Mat image=imread(argv[1]);
Mat imageGray;
cvtColor(image,imageGray,CV_RGB2GRAY);
imageGray=~imageGray; //取反
GaussianBlur(imageGray,imageGray,Size(5,5),2); //滤波
threshold(imageGray,imageGray,20,200,CV_THRESH_BINARY); //阈值
imshow("s",imageGray);
Mat imageThin(imageGray.size(),CV_32FC1); //定义保存距离变换结果的Mat矩阵
distanceTransform(imageGray,imageThin,CV_DIST_L2,3); //距离变换
Mat distShow;
distShow=Mat::zeros(imageGray.size(),CV_8UC1); //定义细化后的字符轮廓
for(int i=0;i<imageThin.rows;i++)
{
for(int j=0;j<imageThin.cols;j++)
{
if(imageThin.at<float>(i,j)>maxValue)
{
maxValue=imageThin.at<float>(i,j); //获取距离变换的极大值
}
}
}
for(int i=0;i<imageThin.rows;i++)
{
for(int j=0;j<imageThin.cols;j++)
{
if(imageThin.at<float>(i,j)>maxValue/1.9)
{
distShow.at<uchar>(i,j)=255; //符合距离大于最大值一定比例条件的点设为255
}
}
}
imshow("Source Image",image);
imshow("Thin Image",distShow);
waitKey();
return 0;
}
https://blog.csdn.net/qq_42856191/article/details/123663533
1.1 什么是腐蚀
简单来说,腐蚀就是把图像中的物体变小了!(用背景去侵蚀前景)
腐蚀的工作过程如下图所示,A是一个集合,B是一个结构单元,我们使用B去对集合A进行腐蚀,腐蚀过程如C所示,在腐蚀过程中,结构单元必须全部位于集合A中才能进行腐蚀操作,最终得到的结果即为D所示。
Python:
import cv2
import numpy as np
ori = cv2.imread(r"C:\Users\Lenovo\Desktop\original.jpg") # 读取图像
kernel1 = np.ones((3, 3), np.uint8) # 3个不同尺度的腐蚀单元
kernel2 = np.ones((5, 5), np.uint8)
kernel3 = np.ones((7, 7), np.uint8)
erosion1 = cv2.erode(ori, kernel1) # 腐蚀函数
erosion2 = cv2.erode(ori, kernel2)
erosion3 = cv2.erode(ori, kernel3)
cv2.imshow("original", ori)
cv2.imshow("erosion1", erosion1)
cv2.imshow("erosion2", erosion2)
cv2.imshow("erosion3", erosion3)
cv2.imwrite(r'C:\Users\Lenovo\Desktop\erosion1.jpg', erosion1)
cv2.imwrite(r'C:\Users\Lenovo\Desktop\erosion2.jpg', erosion2)
cv2.imwrite(r'C:\Users\Lenovo\Desktop\erosion3.jpg', erosion3)
cv2.waitKey()
下面展示的是程序得到的结果图,由图可知,腐蚀能对图像中的毛刺进行去除,但这也与腐蚀单元的大小有关,3 × 3 3×33×3大小的核去除效果不如5 × 5 5×55×5与7 × 7 7×77×7,此外7 × 7 7×77×7的核使得图像变得更加细小。
2.1 什么是膨胀
简单来说,膨胀就是把图像中的物体变大了!(对前景进行膨胀)
膨胀的工作过程如下图所示,A是一个集合,B是一个结构单元,我们使用B去对集合A进行膨胀,膨胀过程如C所示,在膨胀过程中,结构单元只要有一个位于集合A中就能进行膨胀操作,最终得到的结果即为D所示。
Python:
import cv2
import numpy as np
ori = cv2.imread(r"C:\Users\Lenovo\Desktop\original.jpg") # 读取图像
kernel1 = np.ones((3, 3), np.uint8) # 3个不同尺度的腐蚀单元
kernel2 = np.ones((5, 5), np.uint8)
kernel3 = np.ones((9, 9), np.uint8)
dilation1 = cv2.dilate(ori, kernel1) # 膨胀函数
dilation2 = cv2.dilate(ori, kernel2)
dilation3 = cv2.dilate(ori, kernel3)
cv2.imshow("original", ori)
cv2.imshow("dilation1", dilation1)
cv2.imshow("dilation2", dilation2)
cv2.imshow("dilation3", dilation3)
cv2.imwrite(r'C:\Users\Lenovo\Desktop\dilation1.jpg', dilation1)
cv2.imwrite(r'C:\Users\Lenovo\Desktop\dilation2.jpg', dilation2)
cv2.imwrite(r'C:\Users\Lenovo\Desktop\dilation3.jpg', dilation3)
cv2.waitKey()
下面展示的是程序得到的结果图,由图可知,膨胀能对图像中的物体进行扩张,但这也与膨胀单元的大小有关,越大的膨胀核使得图像扩张区域更大。
https://blog.csdn.net/qq_42856191/article/details/123670455
1.1 什么是开运算:先腐蚀后膨胀的操作称为开运算。
1.2作用:消除细小物体、在窄区域分离物体、平滑大物体边界等。
2.1 什么是闭运算:先膨胀后腐蚀的操作称为闭运算。
2.2作用:填充物体空洞、消除噪声、连接邻近物体、平滑边界等。
https://blog.csdn.net/great_yzl/article/details/119831771
以上述引用中的硬币检测为例子的分割过程:
1.将RGB图片转换为灰度图
2.去噪
开运算:先侵蚀后膨胀。会将两个物体之间可能存在的粘连噪点过滤,并将前景物体扩大。
3.确定背景区域
对图片进行膨胀操作,将前景扩大,背景也会随之减小。那么此时对象>原对象,此时背景 < 原背景,那么此时的背景自然可以确定为原背景的一部分。(离对象中心很远的是背景)
4.确定前景区域
原理:距离变换,在二值图中把对象缩小,得到的就是原图的一部分,可以确定为前景。
这种距离变换可以将前景中的不同实例分离,距离变换后得到前景物体不同灰度值的像素,通过threshold函数选定一个阈值来缩小前景物体中每个的大小,从而达到将不同实例分离的效果。(不分离的话,可以不用距离变换,只用腐蚀就够了)
下图分别为img原始图片、opening膨胀后的图片、sure_fg距离变换后经过threshold阈值操作获得的确定前景实例