前景与背景:
在几何关系方面,前景和背景之间存在多种关系,如:
路径与连通:
距离:对于像素 p p p、 q q q和 z z z,如果满足以下三个条件,则称 d d d是距离函数或度量
其中,欧式距离是指
D e ( p , q ) = ( x − s ) 2 + ( y − t ) 2 D_{e}(p, q)=\sqrt{(x-s)^{2}+(y-t)^{2}} De(p,q)=(x−s)2+(y−t)2
城市距离: D 4 ( p , q ) = ∣ x − s ∣ + ∣ y − t ∣ D_{4}(p,q)=|x-s|+|y-t| D4(p,q)=∣x−s∣+∣y−t∣
棋盘距离: D 8 ( p , q ) = m a x ( ∣ x − s ∣ , ∣ y − t ∣ ) D_{8}(p,q)=max(|x-s|,|y-t|) D8(p,q)=max(∣x−s∣,∣y−t∣)
位置:物体在图像中的位置,用物体面积的中心点来表示。二值图像质量分布是均匀的,质心和形心重合。若图像中的物体对应的像素位置坐标为 ( x i , y i ) ( i = 0 , 1 , … , n - 1 ; j = 0 , 1 , … , m - 1 ) (x_{i},y_{i})(i=0, 1, …, n-1;j=0, 1, …, m-1) (xi,yi)(i=0,1,…,n-1;j=0,1,…,m-1),则质心位置坐标为
x ˉ = 1 m n ∑ i = 0 n − 1 ∑ j = 0 m − 1 x i ; y ˉ = 1 m n ∑ i = 0 n − 1 ∑ j = 0 m − 1 y j \bar{x}=\frac{1}{m n} \sum_{i=0}^{n-1} \sum_{j=0}^{m-1} x_{i} ; \bar{y}=\frac{1}{m n} \sum_{i=0}^{n-1} \sum_{j=0}^{m-1} y_{j} xˉ=mn1i=0∑n−1j=0∑m−1xi;yˉ=mn1i=0∑n−1j=0∑m−1yj
方向:如果物体是细长的,则可以把较长方向的轴定为物体的方向。将最小二阶矩轴(最小惯量轴在二维平面上的等效轴)定义为较长物体的方向。也就是说,要找出一条直线,使下式定义的 E E E值最小
E = ∬ r 2 f ( x , y ) d x d y E=\iint r^{2} f(x, y) d x d y E=∬r2f(x,y)dxdy
长宽:当物体的边界已知时,用其外接矩形的尺寸来刻画它的基本形状是最简单的方法求物体在坐标系方向上的外接矩形,只需计算物体边界点的最大和最小坐标值,就可得到物体的水平和垂直跨度
周长:区域的边界长度,用于区别具有简单或复杂形状的物体;表示方法不同,计算方法也不同
面积:度量物体的总尺寸,只与该物体的边界有关,与其内部灰度级的变化无关。像素计数面积是指
如下:对图像进行阈值分割,并统计区域的几何特征
matlab:
clear,clc,close all;
image=imread('plane.jpg');
BW=im2bw(rgb2gray(image));
figure,imshow(BW),title('二值化图像');
% imwrite(BW,'biplane.jpg');
SE=strel('square',3);
Morph=imopen(BW,SE);
Morph=imclose(Morph,SE);
figure,imshow(Morph),title('形态学滤波');
% imwrite(Morph,'morphplane.jpg');
[B,L]=bwboundaries(1-Morph);
figure,imshow(L),title('划分的区域');
% imwrite(L,'Lplane.jpg');
STATS = regionprops(L,'Area', 'Centroid','Orientation','BoundingBox');
figure,imshow(image),title('检测的区域');
hold on;
for i=1:length(B)
boundary=B{i};
plot(boundary(:,2),boundary(:,1),'r','LineWidth',2);
end
rectangle('Position',STATS.BoundingBox,'edgecolor','g');
hold off;
% STATS
python:
import numpy as np
import cv2
import matplotlib.pyplot as plt
image = cv2.imread('plane.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
_, bw = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
plt.imshow(bw, cmap='gray')
plt.title('二值化图像')
plt.show()
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
morph = cv2.morphologyEx(bw, cv2.MORPH_OPEN, kernel)
morph = cv2.morphologyEx(morph, cv2.MORPH_CLOSE, kernel)
plt.imshow(morph, cmap='gray')
plt.title('形态学滤波')
plt.show()
contours, _ = cv2.findContours(255 - morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
boundary_image = np.zeros_like(morph)
cv2.drawContours(boundary_image, contours, -1, 255, 1)
plt.imshow(boundary_image, cmap='gray')
plt.title('划分的区域')
plt.show()
stats = []
for contour in contours:
area = cv2.contourArea(contour)
centroid = np.mean(contour, axis=0)[0]
rect = cv2.boundingRect(contour)
stats.append({
'Area': area,
'Centroid': centroid,
'BoundingBox': rect
})
image_with_boundary = image.copy()
for boundary in contours:
cv2.drawContours(image_with_boundary, [boundary], -1, (0, 0, 255), 2)
for stat in stats:
cv2.rectangle(image_with_boundary, stat['BoundingBox'], (0, 255, 0), 2)
plt.imshow(cv2.cvtColor(image_with_boundary, cv2.COLOR_BGR2RGB))
plt.title('检测的区域')
plt.show()
矩形度:用于描述实体或区域与矩形接近程度的量化指标。它衡量了一个对象或区域在形状上与矩形的相似程度,即其紧凑性和规则性。矩形度是通过比较对象的实际面积和最小外接矩形(Bounding Rectangle)的面积来计算的。最小外接矩形是能够完全包围对象的最小面积的矩形,它的长宽与对象的主要方向一致。矩形度的计算公式如下
R = A o A M E R R=\frac{A_{o}}{A_{MER}} R=AMERAo
MER宽与长的比值为
r = W M E R L M E R r=\frac{W_{MER}}{L_{MER}} r=LMERWMER
圆度:用于描述实体或区域与圆形接近程度的量化指标。它衡量了一个对象或区域在形状上与圆形的相似程度,即其圆形度。圆度是通过比较对象的实际面积和等效圆形的面积来计算的。等效圆形是具有与对象相同面积的圆形,其半径可以通过将对象的面积除以π然后开方来计算。圆度的计算公式如下
边界能量:边界上的点的曲率函数
K ( p ) = 1 r ( p ) K(p)=\frac{1}{r(p)} K(p)=r(p)1
圆形性:是用于描述实体或物体形状接近球体的度量标准。它衡量了一个对象或区域在形状上与球体的相似程度。圆形性是通过比较对象的体积和等效球体的体积来计算的。等效球体是具有与对象相同体积的球体,其半径可以通过将对象的体积除以(4/3π)然后开立方根来计算
C = μ R σ R 2 μ R = 1 K ∑ k = 0 K − 1 ∥ ( x k , y k ) − ( x ˉ , y ˉ ) ∥ σ R 2 = 1 K ∑ k = 0 K − 1 [ ∥ ( x k , y k ) − ( x ˉ , y ˉ ) ∥ − μ R ] 2 \begin{array}{l}C=\frac{\mu_{R}}{\sigma_{R}^{2}} \\\mu_{R}=\frac{1}{K} \sum_{k=0}^{K-1}\left\|\left(x_{k}, y_{k}\right)-(\bar{x}, \bar{y})\right\| \\\sigma_{R}^{2}=\frac{1}{K} \sum_{k=0}^{K-1}\left[\left\|\left(x_{k}, y_{k}\right)-(\bar{x}, \bar{y})\right\|-\mu_{R}\right]^{2}\end{array} C=σR2μRμR=K1∑k=0K−1∥(xk,yk)−(xˉ,yˉ)∥σR2=K1∑k=0K−1[∥(xk,yk)−(xˉ,yˉ)∥−μR]2
内切圆与外接圆半径比:刻画物体边界的复杂程度
S = r i r c S=\frac{r_{i}}{r_{c}} S=rcri
两个圆的圆心都在区域的重心上
如下,对原图进行分割,并检测圆和矩形
matlab:
clear,clc,close all;
image=rgb2gray(imread('shape.png'));
figure,imshow(image),title('Ôͼ');
BW=edge(image,'canny');
figure,imshow(BW),title('±ß½çͼÏñ');
% imwrite(BW,'shapeedge.jpg');
SE=strel('disk',5);
Morph=imclose(BW,SE);
figure,imshow(Morph),title('ÐÎ̬ѧÂ˲¨');
% imwrite(Morph,'shapemorph.jpg');
Morph=imfill(Morph,'holes');
figure,imshow(Morph),title('ÇøÓòÌî³ä');
imwrite(Morph,'shapefill.jpg');
[B,L]=bwboundaries(Morph);
figure,imshow(L),title('¼ì²âÔ²ºÍ¾ØÐÎ');
% imwrite(L,'Lplane.jpg');
STATS = regionprops(L,'Area', 'Centroid','BoundingBox');
len=length(STATS);
hold on
for i=1:len
R=STATS(i).Area/(STATS(i).BoundingBox(3)*STATS(i).BoundingBox(4));
boundary=fliplr(B{i});
everylen=length(boundary);
F=4*pi*STATS(i).Area/(everylen^2);
dis=pdist2(STATS(i).Centroid,boundary,'euclidean');
miu=sum(dis)/everylen;
sigma=sum((dis-miu).^2)/everylen;
C=miu/sigma;
if R>0.9 && F<1
rectangle('Position',STATS(i).BoundingBox,'edgecolor','g','linewidth',2);
plot(STATS(i).Centroid(1),STATS(i).Centroid(2),'g*');
end
if R>pi/4-0.1 && R<pi/4+0.1 && F>0.9 && C>10
rectangle('Position',[STATS(i).Centroid(1)-miu,STATS(i).Centroid(2)-miu,2*miu,2*miu],...
'Curvature',[1,1],'edgecolor','r','linewidth',2);
plot(STATS(i).Centroid(1),STATS(i).Centroid(2),'r*');
end
end
hold off
python:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图像并将其转换为灰度图像
image = cv2.imread('shape.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 显示原始图片
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.title('原始图像')
plt.show()
# 边缘检测
edges = cv2.Canny(gray, 30, 100)
# 显示边缘图片
plt.imshow(edges, cmap='gray')
plt.title('边缘图像')
plt.show()
# 闭运算
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
closed = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel)
# 显示闭运算结果
plt.imshow(closed, cmap='gray')
plt.title('闭运算')
plt.show()
# 填充内部空洞
filled = cv2.fillHoles(closed)
# 显示填充结果
plt.imshow(filled, cmap='gray')
plt.title('填充后图像')
plt.show()
# 寻找轮廓并进行形状分析
contours, _ = cv2.findContours(filled, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
area = cv2.contourArea(cnt)
x, y, w, h = cv2.boundingRect(cnt)
rect_ratio = area / (w * h)
perimeter = cv2.arcLength(cnt, True)
circularity = 4 * np.pi * area / (perimeter ** 2)
centroid = (int(x + w / 2), int(y + h / 2))
distance = cv2.pointPolygonTest(cnt, centroid, True)
if rect_ratio > 0.9 and circularity < 1:
cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.circle(image, centroid, 3, (0, 255, 0), -1)
if np.abs(circularity - np.pi / 4) < 0.1 and rect_ratio > 0.9 and distance > 10:
cv2.rectangle(image, (int(centroid[0] - distance), int(centroid[1] - distance)),
(int(centroid[0] + distance), int(centroid[1] + distance)), (0, 0, 255), 2)
cv2.circle(image, centroid, 3, (0, 0, 255), -1)
# 显示最终结果
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.title('形状分析结果')
plt.show()
中轴变换:是一种图像处理技术,用于提取对象或区域的中轴线特征。中轴线是指与对象边界相切且具有最大内切圆的曲线。中轴变换的基本思想是通过迭代运算,在对象的边界上逐渐向内部收缩,直到达到中轴线所在位置。在这个过程中,计算每个边界点到最近内切圆的距离,并将这些距离值叠加在一起形成一个距离场。通过阈值处理和连接操作,可以得到中轴线。基本步骤如下
中轴变换常用于形状分析、形态学处理、特征提取等领域。通过提取对象的中轴线,可以获得对象的结构信息和几何特征,有助于形状分析、目标识别、图像重建等应用。需要注意的是,中轴变换的结果受到图像预处理、阈值选择、连接方式等因素的影响。因此,在实际应用中,可能需要根据具体情况进行参数调整和优化,以获取更好的中轴线结果
如下,提取目标图像骨架
matlab:
clear,clc,close all;
Image=imread('test.bmp');
BW=im2bw(Image);
figure,imshow(BW);
result=bwmorph(BW,'skel',Inf);
figure,imshow(result);
python:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图像并转换为二值图像
image = cv2.imread('test.bmp', 0)
ret, bw = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY)
# 显示原始二值图像
plt.imshow(bw, cmap='gray')
plt.title('原始二值图像')
plt.show()
# 中轴变换
skeleton = np.zeros_like(bw)
size = np.size(bw)
element = cv2.getStructuringElement(cv2.MORPH_CROSS, (3,3))
while True:
eroded = cv2.erode(bw, element)
temp = cv2.dilate(eroded, element)
temp = cv2.subtract(bw, temp)
skeleton = cv2.bitwise_or(skeleton, temp)
bw = eroded.copy()
zeros = size - cv2.countNonZero(bw)
if zeros == size:
break
# 显示中轴图像
plt.imshow(skeleton, cmap='gray')
plt.title('中轴图像')
plt.show()