最小外包矩形对象的封装

本文探讨了最小外包矩形(MBR)的概念,它是指能够完全覆盖图形对象且边平行于坐标轴的最小矩形。MBR在地理信息系统、数据压缩和多边形碰撞检测等领域有广泛应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最小外包矩形MBR就是包围图元,且平行于X,Y轴的最小外接矩形。

    template<class T>
    struct CGeoRect
    {
        // Four corner points
        T m_minX; 
        T m_minY;
        T m_maxX;
        T m_maxY;
		
        //
        // Constructors and deconstructor
        //
        /**
        * Default constructor with no any sense.
		*
		* Note: 
		* 1) If passed by self-defined data type, it must have copy constructor being responsible for
		* converting doulbe to it.
		* 2) Since we adopt UEZERO as invalid point which makes sense when not initialized before being used, 
		* we had better notice the assertion happen before correctly using one geometry.
        */
        CGeoRect() : m_minX(-1.), m_minY(-1.), m_maxX(-1.), m_maxY(-1.)
        {
        }
        
        /**
        * Default constructor with specified corner values.
        */
        CGeoRect(const T &minX, const T &minY, const T &maxX, const T& maxY) : m_minX(minX), 
			m_minY(minY), m_maxX(maxX), m_maxY(maxY)
        {
        }

        /**
        * Copy constructor.
		*
		* Note: If passed by self-defined data type, it must overload assignment operator
        */
        CGeoRect(const CGeoRect &other)
        {
            m_minX = other.m_minX;
            m_maxY = other.m_maxY;
            m_maxX = other.m_maxX;
            m_minY = other.m_minY;
        }

        /**
		* Deconstructor.
		*
        * Note:Since we want to take it as inner built-in type, it no need to inherit children and
        * virtual constructor may cause different behavior when against different compilers
        */
        ~CGeoRect()
        {
        }

        //
        // Simple interfaces
        //
		/**
        * Check whether is an valid rectangle.
		**/
		bool IsValid() const
		{
			return !((m_minX == m_maxX && m_minX == static_cast<T>(UE_INVALIDCOORD)) ||
				(m_minY == m_maxY && m_minY == static_cast<T>(UE_INVALIDCOORD)));
		}

        /**
        * Disable this rectangle.
        */
        void Empty()
        {
            m_minX = m_maxX = m_minY = m_maxY = static_cast<T>(UE_ZERO);
        }

        /**
        * Check whether is an rectangle have an validate area.
        */
        bool IsEmpty() const
        {
			//assert(IsValid());

			return (m_minX == m_maxX || 
				m_maxY == m_minY || 
				(m_maxX - m_minX) < static_cast<T>(RECTLIMIT) || 
				(m_maxY - m_minY) < static_cast<T>(RECTLIMIT));
        }

        /**
        * Check whether intersect another rectangle.
        */
        inline bool IsIntersect(const CGeoRect<T> &other) const
        {
			//assert(IsValid());
            if(m_maxY < other.m_minY ||
				m_minY > other.m_maxY ||
				m_maxX < other.m_minX || 
				m_minX > other.m_maxX)
            {
                return false;
            }

            return true;
        }

        /**
        * Check whether contains another smaller rectangle including overstacked borders.
        */
        bool IsContain(const CGeoRect<T> &other) const
        {
			if(IsValid())
			{
				if(other.m_minX >= m_minX && 
					other.m_maxX <= m_maxX && 
					other.m_minY >= m_minY && 
					other.m_maxY <= m_maxY)
				{
					return true;
				}
			}

            return false;
        }

        /**
        * Check whether contain one point.
        */
        bool IsContain(const CGeoPoint<T> &point)
        {
			if(IsValid())
			{
				// Exception even if given this rectangle as one line segment
				double minX = m_minX;
				double maxX = m_maxX;
				if(m_minX == m_maxX)
				{
					minX -= 5;
					maxX += 5;
				}

				// Exception even if given this rectangle as one line segment
				double minY = m_minY;
				double maxY = m_maxY;
				if(m_minY == m_maxY)
				{
					minY -= 5;
					maxY += 5;
				}

				if(point.m_x < minX ||
					point.m_x > maxX ||
					point.m_y < minY ||
					point.m_y > maxY)
				{
					return false;
				}

	            return true;
			}

			return false;
        }

        /**
        * Check whether contain one point.
        */
        bool IsContain(const CGeoPoint<T> &point) const
        {
			//assert(IsValid());
            if(point.m_x < m_minX ||
                point.m_x > m_maxX ||
                point.m_y < m_minY ||
                point.m_y > m_maxY)
            {
                return false;
            }

            return true;
        }

        /**
        * Union two rectangles.
        */
        void Union(const CGeoRect<T> &other)
        {
			//assert(IsValid());

            if(m_minX > other.m_minX)
            {
                m_minX = other.m_minX;
            }
            if(m_maxX < other.m_maxX)
            {
                m_maxX = other.m_maxX;
            }

            if(m_minY > other.m_minY)
            {
                m_minY = other.m_minY;
            }
            if(m_maxY < other.m_maxY)
            {
                m_maxY = other.m_maxY;
            }
        }

		/**
		* Enlarge this rectangle.
		*/
		bool Inflate(T xShift, T yShift)
		{
			//assert(IsValid());

			m_minX -= xShift;
			m_maxX += xShift;
			m_minY -= yShift;
			m_maxY += yShift;

			return !IsEmpty();
		}

		/**
		* Shrink this rectangle.
		*/
		bool Deflate(T xShift, T yShift)
		{
			//assert(IsValid());

			m_minX += xShift;
			m_maxX -= xShift;
			m_minY += yShift;
			m_maxY -= yShift;

			return !IsEmpty();
		}

        /**
        * Get rectangle width.
        */
        T Width() const
        {
			//assert(IsValid());

            return m_maxX - m_minX;
        }

        /**
        * Get rectangle height.
        */
        T Height() const
        {
			//assert(IsValid());
            return m_maxY - m_minY;
        }

        //
        // Useful overloaded operators
        //
        /**
        * Overload assignment operator.
        */
        const CGeoRect &operator = (const CGeoRect &other)
        {
			//assert(other.IsValid());
            if(this == &other)
            {
                return *this;
            }

            m_minX = other.m_minX;
            m_maxY = other.m_maxY;
            m_maxX = other.m_maxX;
            m_minY = other.m_minY;

            return *this;
        }

        /**
        * Overload bool operator.
        */
        bool operator == (const CGeoRect &other) const
        {
			//assert(IsValid());
			if(m_minX == other.m_minX && 
				m_maxY == other.m_maxY && 
				m_maxX == other.m_maxX && 
				m_minY == other.m_minY)
            {
                return true;
            }

            return false;
        }
    };

function [LenMin, LenMax] = CalculateScaleParams(APc) % 确保输入为灰度图 %if size(APc, 3) > 1 %gray_img = rgb2gray(APc); %else %gray_img = APc; %end % 直方图均衡化 %eq_img = histeq(gray_img); [binary_img, params, threshold] = ga_image_segmentation(APc); % 二值化处理 %EZ = im2bw(APc, 125/255); % 删除与图像边缘粘连的连通域 %EZ = imclearborder(EZ, 4); % 填补空洞 % EZ1 = binary_img; [rows, cols] = size(EZ1); % 获取图像尺寸 totalPixels = rows * cols; % 图像总像素数 % 1. 自适应结构元素大小(根据图像最小边长动态调整) minDim = min(rows, cols); % 图像最小边长(宽或高) if minDim <= 30 % 极小图像(如30×30以下) seSize = 2; % 用2×2结构元素(避免过度腐蚀) elseif minDim <= 60 % 小图像(30×30 ~ 60×60) seSize = 2; % 仍用2×2,平衡去噪和保形 else % 较大图像(60×60以上) seSize = 3; % 保留3×3结构元素 end se = strel('square', seSize); % 自适应结构元素 % 2. 轻量化形态学操作(小图像减少处理强度) if minDim <= 30 % 极小图像:仅做1次开运算(等效于腐蚀+膨胀,但避免重复操作) EZ2 = imopen(EZ1, se); % 开运算=腐蚀+膨胀,更轻量 else % 较大图像:保留原逻辑(腐蚀+膨胀) EZ2 = imerode(EZ1, se); EZ2 = imdilate(EZ2, se); end % 3. 删除边缘连通域(保留下边粘连的区域) EZ3 = removeBorderCC(EZ2, 'top+left+right'); % 4. 自适应面积阈值(根据图像总像素比例设置) if totalPixels <= 30*30 % 极小图像(总像素≤900) areaThresh = max(10, round(totalPixels * 0.005)); % 最低保留10像素(或总像素0.5%) elseif totalPixels <= 60*60 % 小图像(总像素≤3600) areaThresh = max(30, round(totalPixels * 0.01)); % 最低保留30像素(或总像素1%) else % 较大图像 areaThresh = 100; % 保留原阈值 end EZ3 = bwareaopen(EZ3, areaThresh); % 自适应删除小区域 % 5. 填补空洞(小图像保留该操作,避免空洞被误判为背景) EZ4 = imfill(EZ3, 'holes'); %删除水尺连通域下边超出水尺宽度的像素 % 6. 轻微膨胀(小图像用1×1结构元素,避免过度扩张) % if minDim <= 30 % se1 = strel('square', 1); % 1×1结构元素(等效于不膨胀,仅修复边缘) % else % se1 = strel('square', 1); % 较大图像保留原逻辑 % end % EZ4 = imdilate(EZ4, se1); % ---------------------- 宽度约束处理(同时支持单/多连通域) ---------------------- [L, N] = bwlabel(EZ4, 8); % 标记连通域 cleanedImg = EZ4; % 初始化清理后的图像 if N == 1 % 场景1:单个连通域(基于顶部基准约束底部) % 提取连通域的所有像素坐标 [y, x] = find(L); yMin = min(y); yMax = max(y); % 取顶部1/3区域作为基准(更稳定,干扰少) topRatio = 1/3; % 顶部区域占比(可调整) topYMax = yMin + round((yMax - yMin) * topRatio); topMask = (y >= yMin) & (y <= topYMax); topX = x(topMask); % 计算顶部区域的左右边缘平均坐标(抗噪) leftLimit = mean(topX(topX == min(topX))); % 顶部左边缘平均x rightLimit = mean(topX(topX == max(topX))); % 顶部右边缘平均x % 下边区域为顶部以下的部分 lowerMask = (y > topYMax) & (y <= yMax); [lowerY, lowerX] = find(lowerMask); % 对下边区域进行宽度约束(删除超出范围的像素) if ~isempty(lowerY) cleanedImg = EZ4; % 复制原图用于修改 % 允许轻微超出(容错范围,避免过度裁剪) tolerance = 2; % 允许超出2个像素(可调整) validX = (lowerX >= leftLimit - tolerance) & (lowerX <= rightLimit + tolerance); % 标记超出范围的像素(需要删除的干扰像素) invalidIdx = ~validX; if any(invalidIdx) % 删除超出范围的像素(设为背景0) cleanedImg(sub2ind(size(EZ4), lowerY(invalidIdx), lowerX(invalidIdx))) = 0; end EZ4 = cleanedImg; end elseif N >= 2 % 场景2:2个及以上连通域(基于最大连通域约束) % 找到最大连通域 stats = regionprops(L, 'Area', 'BoundingBox'); areas = [stats.Area]; [~, maxIdx] = max(areas); % 最大连通域索引 % 获取最大连通域的宽度范围(基准) maxBBox = stats(maxIdx).BoundingBox; % [x, y, width, height] maxLeft = maxBBox(1); % 左边界 maxRight = maxBBox(1) + maxBBox(3); % 右边界 % 容错范围(可调整) tolerance = 2; validLeft = maxLeft - tolerance; validRight = maxRight + tolerance; % 找到所有超出宽度范围的像素并删除 [y, x] = find(EZ4); invalid = (x < validLeft) | (x > validRight); if any(invalid) cleanedImg(sub2ind(size(EZ4), y(invalid), x(invalid))) = 0; cleanedImg = bwareaopen(cleanedImg, 5); % 清理孤立点 end disp(['多连通域处理:已基于最大连通域约束宽度(基准宽度:', num2str(maxBBox(3)), ')']); else % 其他情况(0个连通域) disp('未检测到连通域,未进行宽度约束'); end % 更新处理后的图像 EZ4 = cleanedImg; % ------------------------------------------------------------------------- % % 形态学操作 % se = strel('square', 3); % EZ2 = imerode(EZ1, se); % %膨胀 % EZ2 = imdilate(EZ2, se); % EZ3 = removeBorderCC(EZ2, 'top+left+right'); % %EZ3 = imclearborder(EZ2, 4); % EZ3 = bwareaopen(EZ3, 100); % EZ4 = imfill(EZ3, 'holes'); % se1 = strel('square', 1); % EZ4 = imdilate(EZ4, se1); % 绘图 figure; subplot(3,3,1); imshow(APc); title('原图'); subplot(3,3,2); imshow(EZ1); title('二值化'); subplot(3,3,3); imshow(EZ1); title('不变'); subplot(3,3,4); imshow(EZ2); title('先腐蚀再膨胀'); subplot(3,3,5); imshow(EZ3); title('删除干扰像素点的面积'); subplot(3,3,6); imshow(EZ4); title('填补孔洞'); % 识别图形中的各连通域,并绘制蓝色方框 [L0,N0]=bwlabel(EZ4,8); % 找到连通区域 stats=regionprops(L0,'all');% 获取每个连通区域的属性 for i=1:N0 rectangle('Position',stats(i).BoundingBox,'EdgeColor','g'); %画出每个连通区域的最小外接矩形 end % 判断连通域数量 disp('上下连通域数量:');disp(N0); if N0==2 % 计算各连通域的矩形面积,选取最大面积的连通域,并绘制红色框 numrectangle=ones(1,N0); % 用于存储每个连通区域最小外接矩形的面积 N0为二值图像连通域的数目 for i=1:N0 stats(i).BoundingBox; u=stats(i).BoundingBox(3);v=stats(i).BoundingBox(4);T=u*v; % stats(i).BoundingBox(3)为正向外包矩形的长,stats(i).BoundingBox(4)为正向外包矩形的宽。 numrectangle(i)=T; end [bx,by]=find(numrectangle==max(numrectangle)); % numrectangle为正向外包矩形的数组,此处为寻找最大的外包矩形 rectangle('Position',stats(by).BoundingBox,'EdgeColor','r'); for i=1:N0 % 将非最大面积的连通区域标记为0,将最大面积的连通区域标记为1 % 仅保留具有最小外包矩形的连通域 if i~=by EZ4(L0==i)=0; else EZ4(L0==i)=by; end end % 确定最大面积连通域的最小外包矩形顶点坐标 [conx,cony]=find(EZ4==1); [rectx,recty]=minboundrect(cony,conx,'a'); % minboundrect是最小外包矩形是自定义函数,配套脚本一起使用,area为最小外包矩形的面积 rectx(5)=[]; recty(5)=[]; % rectx、recty为最小外包矩形的顶点坐标; recty(5)是与recty(1)重复的坐标,清除 % 确定中心点坐标和边长 centrex=mean(rectx); centrey=mean(recty); % centrex、centrey为最小外包矩形的中心点的横纵坐标 recta=sqrt((rectx(1)-rectx(2))^2+(recty(1)-recty(2))^2); rectb=sqrt((rectx(1)-rectx(4))^2+(recty(1)-recty(4))^2); % recta、b为最小外包矩形的边长 disp('最小外包矩形参数如下:'); disp('(0) 上面连通域中心坐标:'); CoorCentre=[centrex,centrey]; disp(CoorCentre); disp('(1) 上面连通域短边长度:'); LenMin=min(recta,rectb); disp(LenMin); disp('(2) 上面连通域长边长度:'); LenMax=max(recta,rectb); disp(LenMax); for i=1:N0 % 将最大面积的连通区域标记为0,将非最大面积的连通区域标记为1 if i~=by EZ4(L0==i)=1; else EZ4(L0==i)=0; end end [conx1,cony1]=find(EZ4==1); [rectx1,recty1]=minboundrect(cony1,conx1,'a'); rectx1(5)=[]; recty1(5)=[]; % 确定中心点坐标和边长 centrex1=mean(rectx1); centrey1=mean(recty1); recta1=sqrt((rectx1(1)-rectx1(2))^2+(recty1(1)-recty1(2))^2); rectb1=sqrt((rectx1(1)-rectx1(4))^2+(recty1(1)-recty1(4))^2); disp('最小外包矩形参数如下:'); disp('(0) 下面连通域中心坐标:'); CoorCentre1=[centrex1,centrey1]; disp(CoorCentre1); disp('(1) 下面连通域短边长度:'); LenMin1=min(recta1,rectb1); disp(LenMin1); disp('(2) 下面连通域长边长度:'); LenMax1=max(recta1,rectb1); disp(LenMax1); disp('上下合并连通域短边长度:');LenMin2=LenMin; disp(LenMin2); disp('上下合并连通域长边长度:');LenMax2=LenMin1+LenMax;disp(LenMax2); LenMin = LenMin2; LenMax = LenMax2; else % 计算各连通域的矩形面积,选取最大面积的连通域,并绘制红色框 numrectangle=ones(1,N0); % 用于存储每个连通区域最小外接矩形的面积 N0为二值图像连通域的数目 for i=1:N0 stats(i).BoundingBox; u=stats(i).BoundingBox(3);v=stats(i).BoundingBox(4);T=u*v; % stats(i).BoundingBox(3)为正向外包矩形的长,stats(i).BoundingBox(4)为正向外包矩形的宽。 numrectangle(i)=T; end [bx,by]=find(numrectangle==max(numrectangle)); % numrectangle为正向外包矩形的数组,此处为寻找最大的外包矩形 rectangle('Position',stats(by).BoundingBox,'EdgeColor','r'); for i=1:N0 % 将非最大面积的连通区域标记为0,将最大面积的连通区域标记为1 % 仅保留具有最小外包矩形的连通域 if i~=by EZ4(L0==i)=0; else EZ4(L0==i)=by; end end % 确定最大面积连通域的最小外包矩形顶点坐标 [conx,cony]=find(EZ4==1); [rectx,recty]=minboundrect(cony,conx,'a'); % minboundrect是最小外包矩形是自定义函数,配套脚本一起使用,area为最小外包矩形的面积 rectx(5)=[]; recty(5)=[]; % rectx、recty为最小外包矩形的顶点坐标; recty(5)是与recty(1)重复的坐标,清除 % 确定中心点坐标和边长 centrex=mean(rectx); centrey=mean(recty); % centrex、centrey为最小外包矩形的中心点的横纵坐标 recta=sqrt((rectx(1)-rectx(2))^2+(recty(1)-recty(2))^2); rectb=sqrt((rectx(1)-rectx(4))^2+(recty(1)-recty(4))^2); % recta、b为最小外包矩形的边长 disp('最小外包矩形参数如下:'); disp('(0) 连通域中心坐标:'); CoorCentre=[centrex,centrey]; disp(CoorCentre); disp('(1) 连通域短边长度:'); LenMin2=min(recta,rectb); disp(LenMin2); disp('(2) 连通域长边长度:'); LenMax2=max(recta,rectb); disp(LenMax2); LenMin = LenMin2; LenMax = LenMax2; end end 错误使用 sub2ind 下标超出范围。 出错 CalculateScaleParams (第 104 行) cleanedImg(sub2ind(size(EZ4), lowerY(invalidIdx), lowerX(invalidIdx))) = 0; 出错 Main_724 (第 36 行) [LenMinH, LenMaxH] = CalculateScaleParams(AB1); 报错
最新发布
07-26
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值