芯片缺陷检测与定位
图像模式识别系统的组成
图像模式识别是指综合运用图像处理、特征变换、分类方法和计算机等各种技术,模拟人类的视觉思维过程,以自动地识别、分类目标物体的过程或方法。通常由以下部分组成
蒋先刚:数字图像模式识别工程项目研究
芯片缺陷检测与定位流程概述
(1)由于芯片区域仅占采集到的图像的一部分,因此考虑先对图像采集卡采集到的原图像进行芯片区域提取,以提高后续算法的运行效率,并避免非焊盘区域的噪声影响。该步可考虑使用角点检测算法(为仅得到强角点,可使用Shi-Tomasi角点检测算法)等;
(2)芯片图像预处理。为减弱光照强度变化的影响(较强和较弱时),可考虑使用顶帽变换(Top-Hat)、Otsu方法、基于图像分块的可变阈值处理及形态学操作等流程(见下图),得到较理想的二值化图像。
(3)芯片图像特征提取。主要提取并筛选出芯片焊球轮廓,得到芯片焊球边缘点集数据。并对其进行拟合,以得到其位置、尺寸等信息,用于进一步的缺陷检测与定位;
(4)芯片缺陷检测与定位。
(5)算法测试。包括算法的抗干扰性能(鲁棒性)、通用性等的测试。
基于Qt的GUI设计
GUI主界面设计如图
其中左侧输入参数窗口使用QtreeWidget控件,且用户输入参数栏可编辑。鼠标双击可编辑功能主要通过以下代码实现:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
initWindow();
connect(ui->treeWidget, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)),
this, SLOT(openEditor(QTreeWidgetItem*, int)));
connect(ui->treeWidget, SIGNAL(itemSelectionChanged()),
this, SLOT(closeEditor()));
ui->treeWidget->setStyleSheet("QTreeWidget::item{height:20px}");// 设置行距大小,可以防止编译修改完成以后,行距会发生变化
ui->statusBar->showMessage(tr("欢迎使用该检测系统!"), 2000);
}
//在两个槽处理函数中
// 设置第一列除了不用设置参数的部分,双击的可以修改
QTreeWidgetItem *temItem; int temColumn;
void MainWindow::openEditor(QTreeWidgetItem *item, int column)
{
//qDebug()<<"clicked"<<item->data(temColumn,Qt::DisplayRole).toString();;
//qDebug()<<"item"<<item;
if (column == 1 && item != item_Noise && item != item_ROI
&& item != item_Rreprocess && item != item_FeatureExtract
&& item != item_Inspection && item != child_Rreprocess_LightComp
&& item != child_Rreprocess_Binarize && item != child_Rreprocess_adaptThresh
&& item != child_Rreprocess_Morph
&& item != child_FeatureExtract_ContourExtract
&& item != child_FeatureExtract_circleFit
&& item != child_FeatureExtract_ContourSift)
{
ui->treeWidget->openPersistentEditor(item, column);// 设置某一item可以编辑
temItem = item;
temColumn = column;
}
}
// 当修改完以后或者选中其他列的时候,关闭可编辑状态
void MainWindow::closeEditor()
{
//QMessageBox::question(this,"修改",QString("%1").arg(temColumn),QMessageBox::Yes,QMessageBox::No);
if (temItem != NULL)
{
QString str = temItem->data(temColumn, Qt::DisplayRole).toString();
//qDebug()<<"String arguments"<<str;
QRegExp rxs("^([1.0-9.9][0.0-9.9]*)$");
if (str != NULL&&!rxs.exactMatch(str))
{
QMessageBox::about(this, tr("waring"), tr("请输入数字!"));
temItem->setText(temColumn, QString(""));
//temItem->data(temColumn,Qt::DisplayRole)->clear();
}
//qDebug()<<"Input arguments"<<temItem->data(temColumn,Qt::DisplayRole);
else{
ui->treeWidget->closePersistentEditor(temItem, temColumn);// 设置某一item不可编辑
}
}
}
输出参数窗口则主要通过QTextBrowser类实现参数的输出。如当要输出如下图所示的芯片中心坐标
可通过以下代码实现
ui->textBrowser->setText(tr("***************标准芯片***************"));
ui->textBrowser->append(tr("标准芯片的芯片中心(已知数据):("));
ui->textBrowser->moveCursor(QTextCursor::End);
ui->textBrowser->insertPlainText(QString::number(std_center_chip.x));
ui->textBrowser->moveCursor(QTextCursor::End);
ui->textBrowser->insertPlainText(",");
ui->textBrowser->moveCursor(QTextCursor::End);
ui->textBrowser->insertPlainText(QString::number(std_center_chip.y));
ui->textBrowser->moveCursor(QTextCursor::End);
ui->textBrowser->insertPlainText(tr(")"));
其中std_center_chip.x和std_center_chip.y为标准芯片对象的x坐标和y坐标值。
图像批量处理
当输入图像位于某一文件夹下时,为完成图像的批量处理,可以:
(1)在DOS模式下生成txt文件:
DOS环境中进入该文件路径,执行如下命令:
当前文件路径> dir/b>images.txt
(2)即可通过TXT批量处理图片。见:opencv中批量读取图片并保存
(3)而在Qt中,则需将输入图像路径的QString类型转化为char/string类型,进而使用OpenCV中的imread()函数读取图像,进而完成后续的图像批量处理操作。具体代码如下:
void MainWindow::on_action_batchPro_triggered()
{
ui->statusBar->showMessage(tr("检测与定位--图像批量处理"));
bool isOK;
QString text1 = QInputDialog::getText(NULL, tr("输入图像(txt文本)路径"),
tr("请输入txt路径:"),
QLineEdit::Normal,
"E:/GDW/bga/flipchip/flipchip.txt",
&isOK);
QString text2 = QInputDialog::getText(NULL, tr("输出图像路径"),
tr("请输入路径:"),
QLineEdit::Normal,
"E:/GDW/bga/output",
&isOK);
QDir dir1(text1);
qDebug()<<dir1;
dir1.cdUp( );// 目前在E:/
qDebug()<<tr("父目录")<<dir1;
QDir dir2(text2);
qDebug()<<dir2;
/* dir2.cdUp( );// 目前在E:/
qDebug()<<tr("父目录")<<dir2;
*/
QString inputpath=dir1.absolutePath();
qDebug()<<"QString input path"<<inputpath;
QByteArray inputba = inputpath.toLatin1();
const char *inputchar = inputba.data();
QString outputpath=dir2.absolutePath();
QByteArray outputba = outputpath.toLatin1();
const char *outputchar = outputba.data();
qDebug()<<"QString output path"<<outputchar;
QFile file(text1);
QString name;
if(file.open((QFile::ReadOnly)))
{
qDebug()<<"file open!";
QTextStream input(&file);
int img_index = 0;
while(!input.atEnd())
{
name=input.readLine();
QByteArray nameba = name.toLatin1();
const char *namechar = nameba.data();
qDebug()<<"QString input line"<<namechar;
char img_file[200],save_file[200];
sprintf(img_file, "%s/%s",inputchar, namechar);
sprintf(save_file, "%s/%d.bmp", outputchar,img_index);
qDebug()<<"img_file"<<img_file;
qDebug()<<"save_file"<<save_file;
//sprintf(save_file, "%s/%d.bmp", inputstr,img_index);
Mat src=imread(img_file,0);
if(!src.data){cout<<"no image"<<endl;}
//相关图像处理操作……
img_index++;
qDebug()<<img_index;
}
QMessageBox::warning(this, tr("提示"), tr("批量处理已完成!"));
}
}