【庖丁解牛】机器视觉核心算法之圆环展开


作为全球某机器视觉头部大厂的资深工程师,深感学校和互联网上主要资料局限于计算机视觉领域,能落地工业领域的算法寥寥无几,且相关算法原理语焉不详、乏善可陈,或示例代码缺少关键实现、谬误百出, 故作【庖丁解牛】系列课程,掰开揉碎了讲解机器视觉领域核心算法,并提供完整的高质量代码,力争为从业者提供严格贴近工业应用场景的、高质量的国际一线算法剖析
本系列课程围绕工业视觉算法原理及实现,具体场景落地应考虑的工程实践化设计要点及经验,笔者将在其他系列课程中讲解。
庖丁解牛本期公开课的主题是 圆环展开

术语解释

顾名思义,圆环展开是将指定圆环展开为矩形。

几何基础

如图1所示,圆环内径为r,外径为R,圆心为O。
图1 圆环展开示意图
设圆心O坐标为 ( x 0 , y 0 ) (x0,y0) (x0,y0),射线PO到射线OC1的夹角为θ,由几何知识可知:
内圆上点P的坐标 ( x , y ) (x,y) (x,y)为:
x = x 0 + r ∗ c o s θ y = y 0 − r ∗ s i n θ x = x0 + r * cosθ\\ y = y0 - r * sinθ x=x0+rcosθy=y0rsinθ
外圆上点P’的坐标 ( x ′ , y ′ ) (x',y') (x,y)为:
x ′ = x 0 + R ∗ c o s θ y ′ = y 0 − R ∗ s i n θ x' = x0 + R * cosθ\\ y' = y0 - R * sinθ x=x0+Rcosθy=y0Rsinθ
同理可知,若点P落在以O为圆心,半径为Radius(r≤Radius≤R)的圆周上,则其坐标 ( X , Y ) (X,Y) (X,Y)为:
X = x 0 + R a d i u s ∗ c o s θ Y = y 0 − R a d i u s ∗ s i n θ X = x0 + Radius * cosθ\\ Y = y0 - Radius * sinθ X=x0+RadiuscosθY=y0Radiussinθ

原理推导

首先要确定展开矩形图像的宽和高。在此,我们不假思索地给出以下结论:
D s t W i d t h = ( r + R ) ∗ 2 π ∗ 0.5 D s t H e i g h t = R − r \begin{align*} & DstWidth = (r + R) * 2π * 0.5 & \\ & DstHeight = R - r & \end{align*} DstWidth=(r+R)2π0.5DstHeight=Rr
然后,要确定展开矩形图像中,第i行第j列像素点的灰度值。
假设图1中,点C1到G1连线方向为圆环展开的起始方向,由【原理推导】小节结论可知,展开矩形图像中,第i行第j列像素点对应原图像中的坐标为:
x = x 0 + ( r + i ) ∗ c o s ( j ∗ 2 π / D s t W i d t h ) y = y 0 − ( r + i ) ∗ s i n ( j ∗ 2 π / D s t W i d t h ) x = x0 + (r + i) * cos(j * 2π / DstWidth)\\ y = y0 - (r + i) * sin(j * 2π / DstWidth) x=x0+(r+i)cos(j2π/DstWidth)y=y0(r+i)sin(j2π/DstWidth)
显然,展开矩形图像中,第i行第j列像素点对应原图像中的像素坐标不为整数,因此需要采用图像插值算法。最常用的图像插值算法包括最近邻插值和双线性插值,后者插值效果更好,但耗时也相对更长。

  1. 最近邻插值
    最近邻插值示意如图2所示。
    图2 最近邻插值示意图
    如图2所示, ( x 0 , y 0 ) (x0,y0) (x0,y0) ( x 0 , y 1 ) (x0,y1) (x0,y1) ( x 1 , y 0 ) (x1,y0) (x1,y0) ( x 1 , y 1 ) (x1,y1) (x1,y1)都是原图像上的坐标点,灰度值分别对应为Q11、Q12、Q21、Q22。而灰度值未知的插值点 ( x , y ) (x, y) (x,y),根据最近邻插值方法的约束,其与坐标点 ( x 0 , y 0 ) (x0, y0) (x0,y0)位置最接近 (即位于 ( x 0 , y 0 ) (x0,y0) (x0,y0)的邻域内),故插值点 ( x , y ) (x, y) (x,y)的灰度值等于Q11。
  2. 双线性插值
    双线性插值示意如图3所示。
    图3 双线性插值示意图
    由图3可知,二维图像的双线性插值,需要经过三次一阶线性插值。将其投影至平面,可得图4。
    图4 双线性插值平面示意图
    首先,在 x x x方向做一维线性插值:
    f ( x , y 0 ) = x 1 − x x 1 − x 0 f ( x 0 , y 0 ) + x − x 0 x 1 − x 0 f ( x 1 , y 0 ) f ( x , y 1 ) = x 1 − x x 1 − x 0 f ( x 0 , y 1 ) + x − x 0 x 1 − x 0 f ( x 1 , y 1 ) f\left( x,y_{0} \right)=\frac{x_{1}-x}{x_{1}-x_{0}}f\left( x_{0},y_{0} \right)+\frac{x-x_{0}}{x_{1}-x_{0}}f\left( x_{1},y_{0} \right)\\ f\left( x,y_{1} \right)=\frac{x_{1}-x}{x_{1}-x_{0}}f\left( x_{0},y_{1} \right)+\frac{x-x_{0}}{x_{1}-x_{0}}f\left( x_{1},y_{1} \right) f(x,y0)=x1x0x1xf(x0,y0)+x1x0xx0f(x1,y0)f(x,y1)=x1x0x1xf(x0,y1)+x1x0xx0f(x1,y1)
    然后,在 y y y方向做一维线性插值:
    f ( x , y ) = y 1 − y y 1 − y 0 f ( x , y 0 ) + y − y 0 y 1 − y 0 f ( x , y 1 ) f\left( x,y \right)=\frac{y_{1}-y}{y_{1}-y_{0}}f\left( x,y_{0} \right)+\frac{y-y_{0}}{y_{1}-y_{0}}f\left( x,y_{1} \right) f(x,y)=y1y0y1yf(x,y0)+y1y0yy0f(x,y1)
    整理上式,可得像素点的灰度值插值计算公式为:
    f ( x , y ) = ( y 1 − y ) ( x 1 − x ) ( y 1 − y 0 ) ( x 1 − x 0 ) f ( x 0 , y 0 ) + ( y 1 − y ) ( x − x 0 ) ( y 1 − y 0 ) ( x 1 − x 0 ) f ( x 1 , y 0 ) + ( y − y 0 ) ( x 1 − x ) ( y 1 − y 0 ) ( x 1 − x 0 ) f ( x 0 , y 1 ) + ( y − y 0 ) ( x − x 0 ) ( y 1 − y 0 ) ( x 1 − x 0 ) f ( x 1 , y 1 ) f\left( x,y \right)=\frac{(y_{1}-y)(x_{1}-x)}{(y_{1}-y_{0})(x_{1}-x_{0})}f\left( x_{0},y_{0} \right)+\frac{(y_{1}-y)(x-x_{0})}{(y_{1}-y_{0})(x_{1}-x_{0})}f\left( x_{1},y_{0} \right)+\frac{(y-y_{0})(x_{1}-x)}{(y_{1}-y_{0})(x_{1}-x_{0})}f\left( x_{0},y_{1} \right)+\frac{(y-y_{0})(x-x_{0})}{(y_{1}-y_{0})(x_{1}-x_{0})}f\left( x_{1},y_{1} \right) f(x,y)=(y1y0)(x1x0)(y1y)(x1x)f(x0,y0)+(y1y0)(x1x0)(y1y)(xx0)f(x1,y0)+(y1y0)(x1x0)(yy0)(x1x)f(x0,y1)+(y1y0)(x1x0)(yy0)(xx0)f(x1,y1)

核心代码

// 双线性插值
unsigned char CAlgorithmModule::BilinearInterpolation(float x, float y, MVDSDK_BASE_MODU_INPUT* modu_input, int width, int height)
{
	x = (x < 0) ? 0 : x;
	y = (y < 0) ? 0 : y;
	x = (x > width) ? width : x;
	y = (y > height) ? height : y;

	int x1 = floor(x);
	int x2 = ceil(x);
	int y1 = floor(y);
	int y2 = ceil(y);

	int pos1 = y1 * width + x1;
	int pos2 = y1 * width + x2;
	int pos3 = y2 * width + x1;
	int pos4 = y2 * width + x2;

	unsigned char* pImgData = modu_input->pImageInObj->GetImageData(0)->pData;
	unsigned char grayValue = pImgData[pos1] * (x2 - x) * (y2 - y) + pImgData[pos2] * (x - x1) * (y2 - y) + pImgData[pos3] * (x2 - x) * (y - y1) + pImgData[pos4] * (x - x1) * (y - y1);

	return grayValue;
}

运行结果对比

分别采用笔者提出的算法、VisionMaster(国内最领先的算法平台,后面统称为VM)对多幅图像进行圆环展开。
VM圆环展开结果:
图5 VM圆环展开结果
本文圆环展开结果:
图6 本文圆环展开结果
可以看出,笔者提出的圆环展开算法效果足以媲美VM。

本期课程就到此结束了,下期课程我们不见不散。

如觉得本文内容不错,请记得给小编点赞、收藏、分享一键三连,您的支持是笔者更新的不懈动力。
想要算法源码工程的读者朋友可在评论区留言或私信小编。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值