【变化检测】多时相影像变化检测精度评价(附有完整代码)

简介: 【变化检测】多时相影像变化检测精度评价(附有完整代码)

对多时相影像进行精度评估,通常都是使用混淆矩阵求其精确率、漏检率和虚警率;但是要想使用混淆矩阵求上述指标一般都需要知道参考图,也就是在变化检测前知道变化部分和未变化部分,然后用其和变化检测后的结果进行对比,最后求出上述指标;


       道路变化检测使用的往往是矢量数据,即如果想使用变化检测结果和参考图进行对比求变化检测精度指标的话,参考图的生成也必须使用矢量数据;难点在于,道路二值图在进行矢量化时,矢量化结果受道路拓扑结构的影响,从而带来一系列问题,即得到的精度指标和真实情况相差较远;亲测过,在使用道路二值图矢量化结果生成参考图时,未变化部分无法完全重合!!!


       退而求其次,先用道路二值图生成参考图,然后再对参考图进行细化、矢量化操作;此举有原因有二:


(1)生成的参考图相当于把原始道路分为了变化部分和未变化部分两张影像,降低了道路拓扑结构对道路二值图矢量化的影像,使其结果更接近真实情况;


(2)多时相影像中未变化的道路占很大比例,所以针对生成的参考图进行细化、矢量化时,其结果更接近真实情况;


1、生成参考图


(1)制作标签


  我使用的是 labelme 进行打样,标签制作流如下:


       A.在对两幅多时相影像进行打样时,任意挑选其中的一幅影像旧影像(或新影像),对比两幅影像,然后针对未变化部分对旧影像(新影像)进行打样,打样完成后生成的是文件;


       B.对另一幅影像随意打样(随便打两条线即可),目标是为了生成文件;


       C.打开和文件,打开方式选择 vs/记事本 都可以,然后把第一个 .json 文件中的打样数据复制到第二个 .json 中(注意:在复制之前需要把第二个 .json 中的打样数据删掉),并保存;


       D.最后用 labelme 分别打开两个 .json 文件,然后对剩余部分进行打样即可;


       说明:上面做法的目的是,使得两幅多时相影像中未变化部分,使用相同的打样数据,以便后面获取参考图像;


下面举例说明:


下面是使用上述方法生成的多时相影像的道路二值图;

991a58ac622a4bde9811462500f12aee.png


(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.参考图结果

5ca0328294044090a8cf0aebe12c9b59.png

注:左图是变化部分,有图是未变化部分;

C.参考图矢量化结果

fefb64cda8e340529a9b93a2b5a545d4.png

注:左图是变化部分,有图是未变化部分;

       说明:在生成了参考图后,然后对其进行细化、矢量化操作,最后使用该结果去进行变化检测精度评估;上面给的代码是生成参考图的核心代码,我在实现时使用 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)变化检测结果


00067453b35b4706a8ead9687fc2b5f5.png

说明:红色为拆除部分、黄色为新增部分、绿色为未变化部分;


(3)精度评价结果


ac05d5730fa549e6bc08644bb629410f.png

     因为研究内容偏工程,所以基本上所有的内容都使用 VS + QT 封装成独立的模块,由上面给出的代码和显示结果也可以看出; 如果您仅仅需要求变化检测结果,针对上述代码微调足以(主要是路径);


如需要完整代码模块请移步:https://download.csdn.net/download/weixin_47156401/72310899

相关文章
|
人工智能 机器人
多模态大模型活动 | 使用 PAI×LLaMA Factory 搭建文旅问答机器人
LLaMA Factory 是一款开源低代码大模型微调框架,集成了业界最广泛使用的微调技术,支持通过 Web UI 界面零代码微调大模型,目前已经成为开源社区内最受欢迎的微调框架,GitHub 星标超过3万。本次活动通过 PAI×LLaMA Factory 微调 Qwen2-VL 模型,快速搭建文旅领域知识问答机器人,期待看到您与 AI 导游的创意对话!
|
10月前
|
编解码 计算机视觉
YOLOv11改进策略【YOLO和Mamba】| 替换骨干 Mamba-YOLOv11-T !!! 最新的发文热点
YOLOv11改进策略【YOLO和Mamba】| 替换骨干 Mamba-YOLOv11-T !!! 最新的发文热点
1622 7
YOLOv11改进策略【YOLO和Mamba】| 替换骨干 Mamba-YOLOv11-T !!! 最新的发文热点
|
10月前
|
Serverless 计算机视觉
YOLOv11改进策略【小目标改进】| Shape-NWD:融合改进,结合Shape-IoU和NWD 更好地适应小目标特性
YOLOv11改进策略【小目标改进】| Shape-NWD:融合改进,结合Shape-IoU和NWD 更好地适应小目标特性
1668 9
|
6月前
|
机器学习/深度学习 人工智能 自然语言处理
视觉感知RAG×多模态推理×强化学习=VRAG-RL
通义实验室自然语言智能团队发布并开源了VRAG-RL,一种视觉感知驱动的多模态RAG推理框架。它能像人一样“边看边想”,通过粗到细的视觉仿生感知机制,逐步聚焦关键区域,精准提取信息。VRAG-RL结合强化学习与多专家采样策略,优化检索与推理路径,在多个视觉语言基准数据集上表现出色,显著提升准确性和效率。项目已发布技术方案并开源代码,支持快速部署和二次开发。
460 11
|
机器学习/深度学习 数据可视化 自动驾驶
YOLO11-seg分割如何训练自己的数据集(道路缺陷)
本文介绍了如何使用自己的道路缺陷数据集训练YOLOv11-seg模型,涵盖数据集准备、模型配置、训练过程及结果可视化。数据集包含4029张图像,分为训练、验证和测试集。训练后,模型在Mask mAP50指标上达到0.673,展示了良好的分割性能。
5518 4
|
机器学习/深度学习 并行计算 PyTorch
从零开始下载torch+cu(无痛版)
这篇文章提供了一个详细的无痛版教程,指导如何从零开始下载并配置支持CUDA的PyTorch GPU版本,包括查看Cuda版本、在官网检索下载包名、下载指定的torch、torchvision、torchaudio库,并在深度学习环境中安装和测试是否成功。
从零开始下载torch+cu(无痛版)
|
机器学习/深度学习 资源调度 自然语言处理
长短时记忆网络(LSTM)完整实战:从理论到PyTorch实战演示
长短时记忆网络(LSTM)完整实战:从理论到PyTorch实战演示
18362 0
|
安全 Linux 网络安全
【工具使用】几款优秀的SSH连接客户端软件工具推荐FinalShell、Xshell、MobaXterm、OpenSSH、PUTTY、Terminus、mRemoteNG、Terminals等
【工具使用】几款优秀的SSH连接客户端软件工具推荐FinalShell、Xshell、MobaXterm、OpenSSH、PUTTY、Terminus、mRemoteNG、Terminals等
128117 0
|
存储 机器学习/深度学习 人工智能
深入浅出 AI 智能体(AI Agent)|技术干货
随着人工智能技术的发展,智能体(AI Agents)逐渐成为人与大模型交互的主要方式。智能体能执行任务、解决问题,并提供个性化服务。其关键组成部分包括规划、记忆和工具使用,使交互更加高效、自然。智能体的应用涵盖专业领域问答、资讯整理、角色扮演等场景,极大地提升了用户体验与工作效率。借助智能体开发平台,用户可以轻松打造定制化AI应用,推动AI技术在各领域的广泛应用与深度融合。
31164 1