当前位置:首页 > 智能硬件 > 机器视觉
[导读] 在机器视觉处理中,我们经常要对检测到的物体的方位特征进行评估。比如说,我们要 OCR 识别一个字符串。那么这个字符串与x轴的夹角就很重要,我们需要这个信息把这个字符串转正,然后才方便识别。

在机器视觉处理中,我们经常要对检测到的物体的方位特征进行评估。比如说,我们要 OCR 识别一个字符串。那么这个字符串与x轴的夹角就很重要,我们需要这个信息把这个字符串转正,然后才方便识别。

条形码识别也类似,尤其是当我们条形码不是很清晰时,首先将条形码转正,然后用各向异性的滤波器处理一下,可以让条形码变得更清晰易于读取。

这里给出一种基于统计参数的特征提取方法。这个方法已经有几十年历史了,算是个老方法,但是效果很不错,所以值得写篇文章来介绍介绍。

区域的矩

一片区域 R 的矩定义为:

当p 和q 都取 0 时,得到的就是这片区域的面积。也就是:

矩还可以归一化,也就是用上面的定义再除以面积 a。

表示的是这片区域的重心。可以用它来描述区域的位置。

归一化的矩回随区域在图像中的位置不同而变化,要去除这个影响,可以用中心矩,中心矩只反映区域本身的特征。

具体的方法是将这个区域当作一个椭圆区域,那么用上面5个参量就可以计算出椭圆的长短轴和旋转角度。具体公式如下:

椭圆的这几个参数的图形解释如下图:

利用这几个参数就可以确定区域的方位和尺寸了。
比如我们有下面的一幅测试图像。

用上面方法计算出的椭圆如下:

可以看出结果非常的好。尤其是旋转角度,计算的非常准确。

下面是我的测试代码,供参考。用到了些 Qt 的功能。
#include
#include
#include
#include
#include "picturebox.h"
#include

QImage threshold(const QImage &image, quint8 th)
{
int height = image.height();
int width = image.width();
QImage ret(width, height, QImage::Format_Indexed8);
ret.setColorCount(256);
for(int i = 0; i < 256; i++)
{
ret.setColor(i, qRgb(i, i, i));
}

for(int i = 0; i < height; i ++)
{
const uchar *pSrc = (uchar *)image.constScanLine(i);
uchar *pDest = (uchar *)ret.scanLine(i);
for( int j = 0; j < width; j ++)
{
pDest[j] = (pSrc[j] > th)? 255: 0;
}
}
return ret;
}

QImage toGray( const QImage &image )
{
int height = image.height();
int width = image.width();
QImage ret(width, height, QImage::Format_Indexed8);
ret.setColorCount(256);
for(int i = 0; i < 256; i++)
{
ret.setColor(i, qRgb(i, i, i));
}

qDebug () << image.format();
switch(image.format())
{
case QImage::Format_Indexed8:
case QImage::Format_Grayscale8:
for(int i = 0; i < height; i ++)
{
const uchar *pSrc = (uchar *)image.constScanLine(i);
uchar *pDest = (uchar *)ret.scanLine(i);
memcpy(pDest, pSrc, width);
}
break;
case QImage::Format_RGB32:
case QImage::Format_ARGB32:
case QImage::Format_ARGB32_PremulTIplied:
for(int i = 0; i < height; i ++)
{
const QRgb *pSrc = (QRgb *)image.constScanLine(i);
uchar *pDest = (uchar *)ret.scanLine(i);

for( int j = 0; j < width; j ++)
{
pDest[j] = qGray(pSrc[j]);
}
}
break;
}
return ret;
}

QPointF center(const QImage &image, int value)
{
if(image.isNull() || image.format() != QImage::Format_Indexed8)
{
return QPointF(-1, -1);
}
int width = image.width();
int height = image.height();
int x_mean = 0;
int y_mean = 0;
int count = 0;
for(int j = 0; j < height; j ++)
{
const uchar * p = image.constScanLine(j);
for(int i = 0; i < width; i++)
{
if( p[i] == value )
{
x_mean += i;
y_mean += j;
count++;
}
}
}
return QPointF((double)x_mean / count, (double)y_mean / count);

}
struct ELLIPSE_PARA
{
double x_mean; //椭圆的中心坐标 x
double y_mean; //椭圆的中心坐标 y
double r1; //椭圆的长轴半径
double r2; //椭圆的短轴半径
double theta; //椭圆的长轴与 x 轴的夹角(逆时针)
};

/**
* @brief ellipseFit 将一片区域当作椭圆来估计五个几何参数
* @param image
* @param value
* @param para
*/
bool ellipseFit(const QImage &image, int value, ELLIPSE_PARA * para)
{
if(image.isNull() || image.format() != QImage::Format_Indexed8)
{
return false;
}
QPointF c = center(image, value);

int width = image.width();
int height = image.height();
double n01 = c.x();
double n10 = c.y();

double mu20 = 0.0;
double mu02 = 0.0;
double mu11 = 0.0;

int count = 0;
for(int row = 0; row < height; row ++)
{
const uchar * p = image.constScanLine(row);
for(int col = 0; col < width; col++)
{
if( p[col] == value )
{
mu02 += (col - n01) * (col - n01);
mu20 += (row - n10) * (row - n10);
mu11 += (col - n01) * (row - n10);
count ++;
}
}
}
if(count == 0)
{
return false;
}
mu20 /= count;
mu02 /= count;
mu11 /= count;

double t1 = mu20 + mu02;
double t2 = mu20 - mu02;
double t3 = sqrt(t2 * t2 + 4 * mu11 * mu11);
double r1 = sqrt(2 * ( t1 + t3) );
double r2 = sqrt(2 * ( t1 - t3) );

double theta = - atan2(2 * mu11, mu02 - mu20) / 2.0;

para->r1 = r1;
para->r2 = r2;
para->theta = theta;
para->x_mean = n01;
para->y_mean = n10;

return true;
}

int main(int argc, char *argv[])
{
QApplicaTIon a(argc, argv);
QImage image("D:/test55.png");
QImage imageGray = toGray(image);
//imageGray = threshold(imageGray, 128);
ELLIPSE_PARA para;
ellipseFit(imageGray, 0, ¶);

qDebug() << para.r1;
qDebug() << para.r2;
qDebug() << para.theta * 180 / 3.14159;
QPointF c(para.x_mean, para.y_mean);
qDebug() << c;
QPainter painter(&image);
painter.setPen(Qt::red);
painter.translate(c);
painter.rotate(-para.theta * 180 / 3.14159);
painter.drawEllipse(QPointF(0, 0), para.r1, para.r2 );

PictureBox box;
box.seTImage(image);
box.show();
return a.exec();
}

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
换一批
延伸阅读

i.MX 8M Plus 是 NXP 首款集成专用 NPU 的应用处理器,采用 14nm FinFET 工艺,4×Cortex-A53@1.8GHz + Cortex-M7@800MHz 异构多核架构,内置 2.3 TO...

关键字: 边缘 AI 应用处理器 机器视觉

苏州2026年8月28日 /美通社/ -- 近日,国际独立第三方检测、检验和认证机构德国莱茵TÜV(简称"TÜV莱茵")受邀...

关键字: 光学 AI 智能机 机器视觉

在新能源汽车、储能电站快速发展的时代背景下,锂离子电池作为储能与动力输出的核心载体,正在向着更高能量密度、更长循环寿命、更高安全可靠性方向快速迭代。电池制造涉及数十道复杂工序,微米级的微小瑕疵,就可能造成电芯自放电、内部...

关键字: 锂离子电池 机器视觉 储能

2026年8月20日,中国上海 — 思特威(上海)电子科技股份有限公司(股票简称:思特威,股票代码:688213)宣布,在2026世界机器人大会(WRC 2026)期间,与行业领先的机器人视觉及AI视觉科技公司奥比中光(...

关键字: 机器视觉 机器人 3D视觉

随着汽车工业向智能化、精细化、新能源化快速转型,传统人工质检、人工辅助生产的模式已无法满足高精度、高效率、零缺陷的制造需求。机器视觉技术作为工业智能制造的核心支撑技术,融合光学成像、人工智能算法、边缘计算与自动控制技术,...

关键字: 汽车 机器视觉 边缘计算

在新能源汽车与储能产业高速扩张的时代浪潮下,锂离子电池作为能源转换与存储的核心载体,正在面临产能扩张、性能迭代、安全管控的多重考验。电池产品的一致性、可靠性直接决定终端设备的使用体验,制造环节的微小瑕疵,都有可能演变为后...

关键字: 机器视觉 锂离子 智能制造

工业相机(Basler、Hikrobot、Daheng 这类)的 USB 3.0 接口看着跟消费级一样是 Type-A/B 或 Micro-B,但工厂环境下要扛伺服驱动器、变频器、焊机、EFT 群脉冲——5 Gbps 的...

关键字: 机器视觉 工业相机

在边缘端运行实时目标检测模型时,传统CPU处理动辄数百毫秒的推理延迟使其难以满足30fps的实时要求。i.MX 8M Plus在设计之初就给出了明确答案——将2.3 TOPS算力的NPU与双ISP在硬件层面直接耦合,在数...

关键字: 机器视觉 NPU ISP

在智能安防、工业检测、智能家居、车载感知等众多领域,红外传感器凭借非接触式探测、全天候工作、环境适应性强的核心优势,成为机器视觉与环境感知的核心元器件。随着智能化设备快速普及,市场对红外传感器的要求不再局限于基础探测功能...

关键字: 传感器 机器视觉 智能化
关闭