Imgui(3) | 基于 imgui-SFML 的 mnist 数据集查看器

Imgui(3) | 基于 imgui-SFML 的 mnist 数据集查看器

文章目录

  • Imgui(3) | 基于 imgui-SFML 的 mnist 数据集查看器
    • 0. 介绍
    • 1. 处理 mnist 数据集
    • 2. 显示单张图像和label
      • 2.1 显示单张图像
      • 2.2 点选列表后更新显示的图像
      • 2.3 显示 label
      • 2.4 使用完整的列表
    • 总结

0. 介绍

把mnist数据集保存为多张.png图像、 train-label.txt 后, 编写一个 GUI 程序,查看图像和对应的标签。

这是一个简陋的demo,可以扩展它来支持其他数据集的显示。

规划:

  • 处理 mnist 数据集
  • 显示单张图像和label
  • 图像文件名列表的显示
  • 点选列表item后切换显示的图像和label

1. 处理 mnist 数据集

from mnist import MNIST
import matplotlib.pyplot as plt
import numpy as np  # 导入numpy
import osdef save_images_and_labels(images, labels, directory, label_filename):"""将图像和标签保存到指定目录。"""if not os.path.exists(directory):os.makedirs(directory)label_file_path = os.path.join(directory, label_filename)with open(label_file_path, 'w') as label_file:for i, (image, label) in enumerate(zip(images, labels), start=1):image_filename = f"{i}.png"image_path = os.path.join(directory, image_filename)# 将图像数据转换为NumPy数组并重塑image_array = np.array(image, dtype=np.uint8).reshape(28, 28)# 保存图像plt.imsave(image_path, image_array, cmap='gray')# 写入标签label_file.write(f"{image_filename} {label}\n")def main():# 加载MNIST数据集mndata = MNIST('mnist_data')train_images, train_labels = mndata.load_training()test_images, test_labels = mndata.load_testing()# 保存训练数据集save_images_and_labels(train_images, train_labels, 'train', 'train-label.txt')# 保存测试数据集save_images_and_labels(test_images, test_labels, 'test', 'test-label.txt')if __name__ == "__main__":main()

目录结构:

train|-- 1.png|-- 2.png...|-- train-label.txt

2. 显示单张图像和label

2.1 显示单张图像

简单起见, 直接使用 imgui-SFML 进行窗口创建, 方便后续直接添加 imgui 的 GUI widget. CMakeLists.txt 的写法,查看之前的博客内容。

加载了 train/1.png 图像文件到 sf::Texture 对象,通过 sf::Sprite 装载。 sprite 的好处是可以设定位置,还可以缩放。 我放大了20倍。关键代码:

    sf::Texture texture;if (!texture.loadFromFile("../train/1.png")){std::cerr << "Error loading image" << std::endl;return -1;}sf::Sprite sprite;sprite.setTexture(texture);sprite.setScale(20, 20);sprite.setPosition(20, 20);

完整代码:

#include "imgui.h"
#include "imgui-SFML.h"#include <SFML/Graphics.hpp>
#include <SFML/System/Clock.hpp>
#include <SFML/Window/Event.hpp>#include <iostream>int main()
{constexpr int win_width = 960;constexpr int win_height = 640;sf::RenderWindow window(sf::VideoMode(win_width, win_height), "mnist viewer");window.setFramerateLimit(60);bool ret = ImGui::SFML::Init(window); // [imgui-SFML]if (!ret)return -1;sf::CircleShape shape(100.f);shape.setFillColor(sf::Color::Green);sf::Texture texture;if (!texture.loadFromFile("../train/1.png")){std::cerr << "Error loading image" << std::endl;return -1;}sf::Sprite sprite;sprite.setTexture(texture);sprite.setScale(10, 10); // 水平和垂直方向都放大20倍sprite.setPosition(20, 20);sf::Clock deltaClock;while (window.isOpen()){sf::Event event;while (window.pollEvent(event)){ImGui::SFML::ProcessEvent(window, event); // [imgui-SFML]if (event.type == sf::Event::Closed){window.close();}}ImGui::SFML::Update(window, deltaClock.restart()); // [imgui-SFML]window.clear();window.draw(sprite);ImGui::SFML::Render(window); // [imgui-SFML]window.display();}ImGui::SFML::Shutdown(); // [imgui-SFML]return 0;
}

在这里插入图片描述

2.2 点选列表后更新显示的图像

sprite 对象就是一个容器, 或者说带有位置的放大镜, 你随时可以换一个新的 texture 丢给它。 现在我们通过 imgui 显示一个 list, 任意个一个 list item 被选中的时候,就加载对应的图像文件,获取 texture 后装到 sprite 里头,界面上就看到更新的图像了:

在这里插入图片描述

关键代码:

        // 使用ImGui创建一个简单的列表ImGui::Begin("Image List");// 假设文件名为1.png到10.pngfor (int i = 1; i <= 10; ++i) {std::string fileName = std::to_string(i) + ".png";// 如果列表项被点击if (ImGui::Selectable(fileName.c_str(), currentFile == fileName)) {currentFile = fileName; // 更新当前选中的文件名// 加载对应的纹理if (!texture.loadFromFile(currentFile)) {std::cerr << "Failed to load " << currentFile << std::endl;} else {sprite.setTexture(texture);}}}ImGui::End();

2.3 显示 label

train-label.txt 每一行是 <image_name> <image_label> 的形式, 例如:

1.png 0

使用哈希表存储这一映射关系, 然后每当通过GUI交互选择了新的文件名字时, 从字典里查询出对应的 label。

在 imgui 里显示当前图像的 label 很简单, 只需要添加 ImGui::Text, 并用 ImGui::Begin() 和 End() 包裹即可:

    int currentLabel = label_map[currentFile];ImGui::Begin("Label Info");ImGui::Text("Current Image: %s", currentFile.c_str());ImGui::Text("Label: %d", currentLabel);ImGui::End();

增大显示的字体

label 是我们关注的重要信息, 要用大字体显示 label, 需要先加载字体,并执行build和刷新, 否则程序运行阶段会遇到 imgui 的检查失败。

关键代码:

    // 加载字体const std::string asset_dir = "../../games/Resources";const std::string font_path = asset_dir + "/Arial.ttf";// 在这里添加字体ImFont* bigFont = ImGui::GetIO().Fonts->AddFontFromFileTTF(font_path.c_str(), 24.0f);if (!bigFont) {std::cerr << "Failed to load font." << std::endl;}// 构建字体图集ImGui::GetIO().Fonts->Build();// 更新SFML中的字体纹理ret = ImGui::SFML::UpdateFontTexture();if (!ret)return -2;while(){while(){// 显示当前图像的标签if (!currentFile.empty()) {int currentLabel = label_map[currentFile];ImGui::Begin("Label Info", nullptr, ImGuiWindowFlags_AlwaysAutoResize);ImGui::PushFont(bigFont); // 使用更大的字体ImGui::Text("Label: %d", currentLabel);ImGui::PopFont(); // 恢复默认字体ImGui::End();}}}

如果不这样设定, 会遇到这些报错:

Assertion failed: (g.IO.Fonts->IsBuilt() && "Font Atlas not built! Make sure you called ImGui_ImplXXXX_NewFrame() function for renderer backend, which should call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8()"), function ErrorCheckNewFrameSanityChecks, file imgui.cpp, line 9579.Assertion failed: (io.Fonts->TexID != (ImTextureID) nullptr), function RenderDrawLists, file imgui-SFML.cpp, line 876.

效果:

在这里插入图片描述

2.4 使用完整的列表

读取文件名字的时候,原来是硬编码10张图,现在改为从 unordered_map 里读取。 注意, 由于这个数据量不大, 因此没有另外开启线程。

        for (int i = 1; i <= 10; ++i) {std::string fileName = std::to_string(i) + ".png";...}
        for (const auto& kv : label_map){const std::string fileName = kv.first;...}

最终效果:
在这里插入图片描述

总结

通过使用 SFML, 加载并显示了了图像(texture->sprite->window)。 通过使用 imgui, 显示了图像文件列表、 label, 并且列表被选中元素和图像、 label 是联动的。

在设置 label 字体的时候, 需要额外调用 imgui 的构建字体和 imgui-SFML 的刷新, 来避免运行时候的检查报错。

是一个很简陋的图像数据集查看工具,可以进一步完善。

源码放在: https://github.com/zchrissirhcz/imgui-sfml-examples/tree/main/mnist-viewer

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/684149.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

每日一练:LeeCode-98、 验证二叉搜索树【二叉搜索树+DFS】

本文是力扣LeeCode-98、 验证二叉搜索树【二叉搜索树DFS】】 学习与理解过程&#xff0c;本文仅做学习之用&#xff0c;对本题感兴趣的小伙伴可以出门左拐LeeCode。 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&am…

C#,巴都万数列(Padonve Number)的算法与源代码

1 巴都万数列&#xff08;Padovan Sequence&#xff09; 巴都万数列&#xff08;Padovan Sequence&#xff09;是一个整数数列。 首数个值为1, 1, 1, 2, 2, 3, 4, 5, 7, 9, 12, 16, 21, 28, 37 ... 此数列以建筑师理察巴都万命名&#xff0c;他的论文Dom&#xff08;1994年&a…

嵌入式CAN通信协议原理(下)

本篇文章结合实际CAN控制器继续介绍协议相关的内容&#xff0c;还有示例讲解。 好了&#xff0c;继续吧&#xff01; 二. STM32 CAN 控制器介绍 STM32 的芯片中具有 bxCAN 控制器 (Basic Extended CAN)&#xff0c;它支持 CAN 协议 2.0A 和 2.0B 标准。 该 CAN 控制器支持最…

Vi 和 Vim 编辑器

Vi 和 Vim 编辑器 vi 和 vim 的基本介绍 Linux 系统会内置 vi 文本编辑器 Vim 具有程序编辑的能力&#xff0c;可以看做是 Vi 的增强版本&#xff0c;可以主动的以字体颜色辨别语法的正确性&#xff0c;方便程序设计。 代码补完、编译及错误跳转等方便编程的功能特别丰富&…

读十堂极简人工智能课笔记03_遗传算法与进化

1. 寻找正确答案 1.1. 卡尔西姆斯 1.1.1. 计算机图形艺术家和研究者 1.1.2. 演示过数字进化之创造性和新颖性的先驱 1.1.3. 1994年 1.1.3.1. 创造一批能游泳、走路、跳跃&#xff0c;甚至互相竞争的虚拟动物震惊了整个科学界 1.1.3.2. 它们的人工大脑却是个极其复杂的网络…

Json-序列化字符串时间格式问题

序列化字符串时间格式问题 一、项目场景二、问题描述三、解决方案 一、项目场景 最近C#中需要将实体进行json序列化&#xff0c;使用了Newtonsoft.Json public static void TestJson(){DataTable dt new DataTable();dt.Columns.Add("Age", Type.GetType("Sys…

java8使用流

这种处理数据的方式很有用&#xff0c;因为你让Stream API管理如何处理数据。这样StreamAPI就可以在背后进行多种优化。此外&#xff0c;使用内部迭代的话&#xff0c;SteamAPI可以决定并行运行你的代码。这要是用外部迭代的话就办不到了&#xff0c;因为你只能用单一线程挨个迭…

VMwareWorkstation17.0虚拟机安装Windows2.03

VMwareWorkstation17.0虚拟机安装Windows2.03 第一篇 下载Windows2.03第二篇 配置Windows2.03虚拟机机器环境第三篇 启动Windows2.03系统 第一篇 下载Windows2.03 1.Windows2.0原版软盘下载地址是 暂不提供&#xff0c;后续更新 2.Windows2.03虚拟机镜像下载地址是 Windows2…

STM32物联网(ESP-01S模块及STM32和ESP-01S通信方式介绍)

文章目录 前言一、ESP-01S模块介绍二、STM32和ESP-01S通信方式介绍三、什么是AT指令四、创建基础工程总结 前言 本篇文章我们开始正式进入STM32物联网的专栏&#xff0c;在这个专栏中将会带大家学习使用STM32进行联网&#xff0c;联网模块的话主要就是使用到了ESP-01S WIFI模块…

『运维备忘录』之 Zip 命令详解

运维人员不仅要熟悉操作系统、服务器、网络等只是&#xff0c;甚至对于开发相关的也要有所了解。很多运维工作者可能一时半会记不住那么多命令、代码、方法、原理或者用法等等。这里我将结合自身工作&#xff0c;持续给大家更新运维工作所需要接触到的知识点&#xff0c;希望大…

WebStorm | 如何修改webstorm中新建html文件默认生成模板中title的初始值

在近期的JS的学习中&#xff0c;使用webstorm&#xff0c;总是要先新建一个html文件&#xff0c;然后再到里面书写<script>标签&#xff0c;真是麻烦&#xff0c;而且标题也是默认的title&#xff0c;想改成文件名还总是需要手动去改 经过小小的研究&#xff0c;找到了修…

单片机学习笔记---LCD1602

LCD1602介绍 LCD1602&#xff08;Liquid Crystal Display&#xff09;液晶显示屏是一种字符型液晶显示模块&#xff0c;可以显示ASCII码的标准字符和其它的一些内置特殊字符&#xff08;比如日文的片假名&#xff09;&#xff0c;还可以有8个自定义字符 显示容量&#xff1a;…

AlmaLinux更换鼠标样式为Windows样式

文章目录 前言先看看条件与依赖第一步&#xff1a;测试最终效果第二步&#xff1a;使用CursorXP修改鼠标样式CurosrXP安装CursorXP使用 第三步&#xff1a;Linux端环境搭建与命令执行UbuntuFedora其他系统均失败 第四步&#xff1a;应用主题 前言 只不过是突发奇想&#xff0c…

jacob使用教程---环境搭建及基本组成元素

参考资料: jacob的GitHub地址 jacob官网(个人感觉不重要) microsoft官方VBA文档(很重要,jacob所有的参数都来自于这里) jacob找COM组件 jacob环境配置教程 jacob将word转为各种格式 提取word中审阅内容 本人参考例子及相关资料 具有参考价值的博客 jacob操作e…

【Python】通过conda安装Python的IDE

背景 系统&#xff1a;win11 软件&#xff1a;anaconda Navigator 问题现象&#xff1a;①使用Navigator安装jupyter notebook以及Spyder IDE 一直转圈。②然后进入anaconda prompt执行conda install jupyter notebook一直卡在Solving environment/-\。 类似问题&#xff1a; …

计算机网络——12DNS

DNS DNS的必要性 IP地址标识主机、路由器但IP地址不好记忆&#xff0c;不便于人类用使用&#xff08;没有意义&#xff09;人类一般倾向于使用一些有意义的字符串来标识Internet上的设备存在着“字符串”——IP地址的转换的必要性人类用户提供要访问机器的“字符串”名称由DN…

FreeRTOS知识点

1>任务调度算法 1.抢占式调度&#xff1a;高优先级的任务优先执行&#xff0c;并且可以打断低优先级的任务执行。 在FreeRTOSConfig.h中开启configUSE_PREEMPTION宏&#xff0c;将宏设置为1&#xff0c;关闭&#xff08;将宏设置为0&#xff09; 2.时间片轮转&#xff1a…

【动态规划】【C++算法】1563 石子游戏 V

作者推荐 【数位dp】【动态规划】【状态压缩】【推荐】1012. 至少有 1 位重复的数字 本文涉及知识点 动态规划汇总 LeetCoce:1563 石子游戏 V 几块石子 排成一行 &#xff0c;每块石子都有一个关联值&#xff0c;关联值为整数&#xff0c;由数组 stoneValue 给出。 游戏中…

【JAVA-Day82】线程中断

线程中断 线程中断&#xff1a;探索 Java 中发送中断信号的方法 &#x1f6a8;&#x1f6d1;摘要 &#x1f4dd;引言 &#x1f680;正文 &#x1f4da;一、什么是线程中断 ❓二、线程什么情况下需要中断 ❓三、模拟线程中断 &#x1f6a8;四、线程中断的应用场景 &#x1f3de;…

SpringBoot3 + Vue3 由浅入深的交互 基础交互教学

说明&#xff1a;这篇文章是适用于已经学过SpringBoot3和Vue3理论知识&#xff0c;但不会具体如何实操的过程的朋友&#xff0c;那么我将手把手从教大家从后端与前端交互的过程教学。 目录 一、创建一个SpringBoot3项目的和Vue3项目并进行配置 1.1后端配置: 1.1.1applicatio…