对多时相影像进行精度评估,通常都是使用混淆矩阵求其精确率、漏检率和虚警率;但是要想使用混淆矩阵求上述指标一般都需要知道参考图,也就是在变化检测前知道变化部分和未变化部分,然后用其和变化检测后的结果进行对比,最后求出上述指标;
道路变化检测使用的往往是矢量数据,即如果想使用变化检测结果和参考图进行对比求变化检测精度指标的话,参考图的生成也必须使用矢量数据;难点在于,道路二值图在进行矢量化时,矢量化结果受道路拓扑结构的影响,从而带来一系列问题,即得到的精度指标和真实情况相差较远;亲测过,在使用道路二值图矢量化结果生成参考图时,未变化部分无法完全重合!!!
退而求其次,先用道路二值图生成参考图,然后再对参考图进行细化、矢量化操作;此举有原因有二:
(1)生成的参考图相当于把原始道路分为了变化部分和未变化部分两张影像,降低了道路拓扑结构对道路二值图矢量化的影像,使其结果更接近真实情况;
(2)多时相影像中未变化的道路占很大比例,所以针对生成的参考图进行细化、矢量化时,其结果更接近真实情况;
1、生成参考图
(1)制作标签
我使用的是 labelme 进行打样,标签制作流如下:
A.在对两幅多时相影像进行打样时,任意挑选其中的一幅影像旧影像(或新影像),对比两幅影像,然后针对未变化部分对旧影像(新影像)进行打样,打样完成后生成的是文件;
B.对另一幅影像随意打样(随便打两条线即可),目标是为了生成文件;
C.打开和文件,打开方式选择 vs/记事本 都可以,然后把第一个 .json 文件中的打样数据复制到第二个 .json 中(注意:在复制之前需要把第二个 .json 中的打样数据删掉),并保存;
D.最后用 labelme 分别打开两个 .json 文件,然后对剩余部分进行打样即可;
说明:上面做法的目的是,使得两幅多时相影像中未变化部分,使用相同的打样数据,以便后面获取参考图像;
下面举例说明:
下面是使用上述方法生成的多时相影像的道路二值图;
(2)生成参考图
A.生成参考图核心代码
//生成参考图 void myReference::outputImage() { //变化、未变化部分图像,change_images、unchange_images是全局变量 change_images = Mat::zeros(oldImage.rows, oldImage.cols, CV_8UC3); unchange_images = Mat::zeros(oldImage.rows, oldImage.cols, CV_8UC3); //把输入的影像读到矩阵中以便显示 for (int i = 0; i < change_images.rows; i++) { Vec3b* image1_1 = change_images.ptr<Vec3b>(i); //变化部分图像 Vec3b* image2_2 = unchange_images.ptr<Vec3b>(i); //未变化部分图像 Vec3b* image3_3 = oldImage.ptr<Vec3b>(i); //旧影像二值图 Vec3b* image4_4 = newImage.ptr<Vec3b>(i); //新影像二值图 for (int j = 0; j < change_images.cols; j++) { //变化部分图像 if ((image4_4[j][0] == 0 && image4_4[j][1] == 0 && image4_4[j][2] == 0) && (image3_3[j][0] != 0 && image3_3[j][1] != 0 && image3_3[j][2] != 0)) { image1_1[j][0] = image3_3[j][0]; image1_1[j][1] = image3_3[j][1]; image1_1[j][2] = image3_3[j][2]; } else if ((image4_4[j][0] != 0 && image4_4[j][1] != 0 && image4_4[j][2] != 0) && (image3_3[j][0] == 0 && image3_3[j][1] == 0 && image3_3[j][2] == 0)) { image1_1[j][0] = image4_4[j][0]; image1_1[j][1] = image4_4[j][1]; image1_1[j][2] = image4_4[j][2]; } //未变化部分图像 else if ((image4_4[j][0] != 0 && image4_4[j][1] != 0 && image4_4[j][2] != 0) && (image3_3[j][0] != 0 && image3_3[j][1] != 0 && image3_3[j][2] != 0)) { image2_2[j][0] = image4_4[j][0]; image2_2[j][1] = image4_4[j][1]; image2_2[j][2] = image4_4[j][2]; } } } cv::imwrite("..\\myChangedetection\\image_save\\change_reference.png", change_images); //保存变化部分 cv::imwrite("..\\myChangedetection\\image_save\\unchange_reference.png", unchange_images); //保存未变化部分 //下面代码主要功能是把结果显示在QT界面中 cv::resize(change_images, change_images, Size(SIZE_ROW, SIZE_COL)); cv::resize(unchange_images, unchange_images, Size(SIZE_ROW, SIZE_COL)); QImage change_img = QImage((const unsigned char*)(change_images.data), change_images.cols, change_images.rows, QImage::Format_RGB888); QImage unchange_img = QImage((const unsigned char*)(unchange_images.data), unchange_images.cols, unchange_images.rows, QImage::Format_RGB888); label_1->setPixmap(QPixmap::fromImage(change_img)); label_1->resize(QSize(change_img.width(), change_img.height())); label_2->setPixmap(QPixmap::fromImage(unchange_img)); label_2->resize(QSize(unchange_img.width(), unchange_img.height())); /**************** 精度评估 ***************/ accuracyAssess(); }
B.参考图结果
注:左图是变化部分,有图是未变化部分;
C.参考图矢量化结果
注:左图是变化部分,有图是未变化部分;
说明:在生成了参考图后,然后对其进行细化、矢量化操作,最后使用该结果去进行变化检测精度评估;上面给的代码是生成参考图的核心代码,我在实现时使用 QT 将其封装成独立的模块了,如有需要可留言!!!
2、精度评价
根据上述生成的参考图矢量化结果和自己变化检测算法得到的结果进而求变化检测精度评价指标;
(1)精度评估核心代码
//把输入的道路数据细化、矢量化 void myReference::dealImage(QString str1, QString str2) { process.createVectorFromFile(str1, "old_Thin_reference.png"); //旧时相影像栅格数据矢量化 process.createVectorFromFile(str2, "new_Thin_reference.png"); //新时相影像栅格数据矢量化 process.simplifyVector(0); //简化旧影像矢量化结果 process.simplifyVector(1); //简化新影像矢量化结果 //输出简化后的矢量道路数据 oldImageoutputResult = process.outputBinaryResult(0, "change_Vector_reference.png", ".\\old_vector_result.txt"); newImageoutputResult = process.outputBinaryResult(1, "unchange_Vector_reference.png", ".\\new_vector_result.txt"); //注:此时 oldImageoutputResult 中存放的矢量化化结果是单通道矩阵 } //精度评估 void myReference::accuracyAssess() { //读取变化和未变化部分参考图 QString str1 = QString::fromStdString("..\\myChangedetection\\image_save\\change_reference.png"); QString str2 = QString::fromStdString("..\\myChangedetection\\image_save\\unchange_reference.png"); //参考图像道路数据矢量化 dealImage(str1, str2); Mat old_images, new_images; //参考图矢量化结果 Mat change_image = imread("..\\myChangedetection\\image_save\\change_Vector_reference.png"); Mat unchange_image = imread("..\\myChangedetection\\image_save\\unchange_Vector_reference.png"); int accuracy = 0; //精确率 int miss_detection = 0; //漏检率 int false_alarm = 0; //虚警率 int all_roads = 0; //所有道路 //读取变化检测结果 Mat outputResult = imread("..\\myChangedetection\\image_save\\final_all.png"); for (int i = 0; i < outputResult.rows; i++) { Vec3b* image1_1 = outputResult.ptr<Vec3b>(i); //变化检测结果 Vec3b* image2_2 = change_image.ptr<Vec3b>(i); //参考图变化部分 Vec3b* image3_3 = unchange_image.ptr<Vec3b>(i); //参考图未变化部分 for (int j = 0; j < outputResult.cols; j++) { //红色(0, 0, 255)、黄色(0, 255, 255)、绿色(0, 255, 0) //正确率—变化部分 if (((image1_1[j][0] == 0 && image1_1[j][1] == 0 && image1_1[j][2] == 255) || (image1_1[j][0] == 0 && image1_1[j][1] == 255 && image1_1[j][2] == 255)) && (image2_2[j][0] == 255 && image2_2[j][1] == 255 && image2_2[j][2] == 255)) { accuracy++; all_roads++; } //正确率—未变化部分 else if ((image1_1[j][0] == 0 && image1_1[j][1] == 255 && image1_1[j][2] == 0) && (image3_3[j][0] == 255 && image3_3[j][1] == 255 && image3_3[j][2] == 255)) { accuracy++; all_roads++; } //虚警率 else if (((image1_1[j][0] == 0 && image1_1[j][1] == 0 && image1_1[j][2] == 255) || (image1_1[j][0] == 0 && image1_1[j][1] == 255 && image1_1[j][2] == 255)) && (image3_3[j][0] == 255 && image3_3[j][1] == 255 && image3_3[j][2] == 255)) { false_alarm++; all_roads++; } //漏检率 else if ((image1_1[j][0] == 0 && image1_1[j][1] == 255 && image1_1[j][2] == 0) && (image2_2[j][0] == 255 && image2_2[j][1] == 255 && image2_2[j][2] == 255)) { miss_detection++; all_roads++; } } } double accuracy_rate = (double)accuracy / all_roads; //精确率 double miss_detection_rate = (double)false_alarm / all_roads; //漏检率 double false_alarm_rate = 1 - accuracy_rate - miss_detection_rate; //虚警率 qDebug() << u8"变化检测精确率为:" << accuracy_rate << endl; qDebug() << u8"变化检测虚警率为:" << miss_detection_rate / all_roads << endl; qDebug() << u8"变化检测漏检率为:" << false_alarm_rate / all_roads << endl; }
说明:上述代码第一个函数中 :createVectorFromFile、simplifyVector、outputBinaryResult 分别是自己的道路二值图矢量化、矢量化简化、输出矢量化结果函数;
(2)变化检测结果
说明:红色为拆除部分、黄色为新增部分、绿色为未变化部分;
(3)精度评价结果
因为研究内容偏工程,所以基本上所有的内容都使用 VS + QT 封装成独立的模块,由上面给出的代码和显示结果也可以看出; 如果您仅仅需要求变化检测结果,针对上述代码微调足以(主要是路径);
如需要完整代码模块请移步:https://download.csdn.net/download/weixin_47156401/72310899