scratch lenet(1): 读写 pgm 图像文件

news2024/11/16 3:30:48

scratch lenet(1): 读写 pgm 图像文件

文章目录

  • scratch lenet(1): 读写 pgm 图像文件
    • 1. 目的
    • 2. pgm 格式介绍
      • 2.1 概要
      • 2.2 meta 信息
      • 2.3 像素内容
    • 3. 创建 .pgm 文件
    • 4. 使用C语言读取 .pgm 灰度图文件
      • 4.1 实现
      • 4.2 解释
    • 5. 使用C语言保存 .pgm 灰度图文件

1. 目的

最近在 github 上关注了 LLM 的流行库 llama.cpp 和 whisper.cpp 的作者 Georgi Gerganov, 简称 gg 哥。 通过 gg哥关注到了 deepdream 算法作者开源的 deepdream_c 代码。完全用 C89 写成的 deepdream, 编译只需要1秒钟, Windows 和 Linux 都能运行。完全不依赖 PyTorch 和 OpenCV 等框架, 连 C++ 都没使用,非常克制, 可移植性非常高。这个风格和 gg 哥的风格有点像的: 喜欢单一的 .vimrc 文件, 写 ggml 主要放在一个16000行的文件中。打算按照这种风格, 用 C99 标准,不借助外部库, 实现 lenet。

实现过程中难免遇到不熟悉的内容, 因为这是一种“全都自己造”的风格; 没关系, 我会尝试逐一弄懂, 以博客形式分享出来。

这是这系列的第一篇, 分享的是 pgm 图像的读写。用 pgm 格式的原因是, 项目规模非常小的时候, BMP 的编解码都显得过于复杂, 目前只需要灰度图的前提下, pgm 足够使用, 而 Linux KDE 下的默认图像查看器, 完全可以查看 pgm 格式。

2. pgm 格式介绍

2.1 概要

pgm 里的 g 表示 gray, 是灰度图, 数据范围通常是 0 到 255。

pgm 文件,以二进制格式进行存储, 不过它的内容其实是 文本 + 二进制混合的:meta 信息是文本, 图像像素内容是二进制。

2.2 meta 信息

第一行是 P5 两个字符。
第二行是空格分隔的两个整数, 分别表示图像宽度,高度。
第三行是一个整数, 表示像素最大取值, 通常是255。

这些信息都是文本格式写入, 也就是用 fprintf 写入, fscanf 读取。也可以用记事本查看 .pgm 文件。

2.3 像素内容

用二进制形式存储, row-major。
也就是说, 用 fread 读取, 用 fwrite 写入。

3. 创建 .pgm 文件

我的开发环境是 Ubuntu 22.04, KDE 桌面, 也可以叫做 “KUbuntu”. 不过我认为 KUbuntu 的叫法很奇怪,Ubuntu 并不限制你用什么桌面, 安装了 KDE 之后也可以安装 Cinamon, XFCE 等桌面, 如果安装的不是 Ubuntu 而是 OpenSude, Manjaro 等 Linux 发行版, 也可以安装 KDE。

我使用 KDE 里的 KolourPaint 这个绘图软件,制作一张手写数字“3”的图像:

在这里插入图片描述

保存图像, 选择 .pgm 格式:
在这里插入图片描述

4. 使用C语言读取 .pgm 灰度图文件

4.1 实现

简单起见, 我们直接读取刚刚用 KolourPaint 生成的 3.pgm 文件。

uchar g_image[784];
void read_pgm_image()
{
    FILE* fin = fopen("3.pgm", "rb");
    char magic[3];
    int width, height;
    int nscan = fscanf(fin, "%2s\n%d %d\n255\n", magic, &width, &height);

    if (nscan == 3 && magic[0] == 'P' && magic[1] == '5')
    {
        fread(g_image, 784, 1, fin);
    }
    fclose(fin);
}

如果打算读取其他 .pgm 文件, 可以自行重构, 其中 784 等于 28 * 28, 是图像大小。

4.2 解释

FILE* fin = fopen("3.pgm", "rb"); 是以二进制格式打开文件 3.pgm.

读取第一行的 P5 两个字符时, 使用了

char magic[3];

而不是

char magic[2];

原因是避免内存越界, 具体分析见 Cracking C++(13): 读取不超过n个字符。

int nscan = fscanf(fin, "%2s\n%d %d\n255\n", magic, &width, &height); 是读取 meta 信息,是以文本方式读取。

fread(g_image, 784, 1, fin); 是读取像素内容, 是按二进制格式读取。

5. 使用C语言保存 .pgm 灰度图文件

和读取过程是配套的,代码如下

void write_pgm_image(uchar* image, int width, int height, const char* filename)
{
    FILE* fout = fopen(filename, "wb");
    fprintf(fout, "P5\n%d %d\n255\n", width, height);
    fwrite(image, width * height, 1, fout);
    fclose(fout);
}

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

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

相关文章

车载软件架构 —— 闲聊几句AUTOSAR OS(四)

我是穿拖鞋的汉子,魔都中坚持长期主义的工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 没有人关注你。也无需有人关注你。你必须承认自己的价值,你不能站在他人的角度来反对自己。人生在世,最怕的就是把别人的眼光当成自己生活的唯一标准。到最…

初识Telegraf、InfluxDB和Grafana铁三角形成的监控可视化解决方案

文章目录 前言原始的监控靠人盯进化的监控靠批处理脚本高端的监控靠完整的可视化解决方案Telegraf、InfluxDB和Grafana铁三角TelegrafInfluxDBGrafana Grafana仪表板展示服务器资源总览负载和内存使用网络带宽磁盘IOIO延迟其他指标进程信息 总结 前言 数据监控目前用于各行各业…

Cracking C++(13): 读取不超过n个字符

文章目录 1. 目的2. 正确用法实例3. 纠正错误用法3.1 错误用法3.2 让 AddressSanitizer 告诉你错误3.3 解释 4. 总结 1. 目的 在读取 pgm 格式图像的 meta 信息时, 使用了 %2s 这个格式串, 之前不是很了解, 尝试后发现, 如果不小…

花上半小时帮你快速熟悉微服务架构

本文将介绍微服务架构和相关的组件,介绍他们是什么以及为什么要使用微服务架构和这些组件。本文侧重于简明地表达微服务架构的全局图景,因此不会涉及具体如何使用组件等细节。 要理解微服务,首先要先理解不是微服务的那些。通常跟微服务相对…

读发布!设计与部署稳定的分布式系统(第2版)笔记02_停飞的代码异常

1. 以前“计划内的停机”很正常,现在则不被接受 2. 高可用性架构 2.1. CF系统不会遇到任何常见的单点失效问题 2.1.1. 硬件的每一部分都有冗余 2.1.1.1. CPU 2.1.1.2. 驱动器 2.1.1.3. 网卡 2.1.1.4. 电源 2.1.1.5. 网络交换机 2.1.1.6. 风扇 2.1.2. 为了…

Redis哨兵模式的配置

1.环境准备 master节点1个slave节点2个sentinel【哨兵】节点3个redis版本5.0.3操作系统:Centos7 2.主从节点配置 创建redis-conf目录,此目录用于存放主从节点的配置文件 复制redis.conf,然后创建三个配置文件:redis-6379.conf&…

循环缓冲题目

题目:一环形缓冲区由 6 个缓冲区 0~5 组成,其中 Full 表示装满数据的缓冲区,Empty 表示空缓冲区。按照顺时针方向,指针 Pf 指向第一个 “满” 缓冲区,指针 Pe 指向第一个 “空” 缓冲区。进程 In 在 Pe 指示下不断向 E…

XSS数据接收网站——XSS在线平台

文章目录 前言使用步骤1、进入到xss在线平台主页2、创建项目3、生成攻击poc4、查看返回结果 前言 平台的网址是: 链接: XSS在线平台 使用步骤 1、进入到xss在线平台主页 2、创建项目 我的项目,点击创建,项目名称和描述随便填,…

Docker安装和使用,Docker拉取Mysql.

Docker Unbuntu安装dockerdocker的相关操作开启docker服务查看镜像搜索镜像拉取镜像删除镜像运行容器查看容器停止运行容器重新运行容器删除容器构建一个Docker镜像登陆Dockerhub提交镜像到dockerhub退出dockerhub进入正在运行的容器的交互式终端其他docker操作 docker拉取mysq…

qemu arm Linux 环境测试交叉编译的 glib 库 测试用例 tests

环境搭建 ubuntu 20.04 arm 平台交叉编译 glib 库 交叉编译 glib 库 glib 库 本身带有大量的测试用例 tests,分别在 glib 各个模块目录下的 tests 目录,如果是 ARM Linux 平台的交叉编译,可以开启 installed_tests 选项 开启 glib tests 测…

2.pixi.js编写的塔防游戏(类似保卫萝卜)-场景编辑器

游戏说明 一个用pixi.js编写的h5塔防游戏,可以用electron打包为exe,支持移动端,也可以用webview控件打包为app在移动端使用 环境说明 cnpm6.2.0 npm6.14.13 node12.22.7 npminstall3.28.0 yarn1.22.10 npm config list electron_mirr…

一种正弦信号叠加高频噪声的信号基频率准确测量方法

1.问题 当信号叠加有高频噪声时,特别是类似有变频器这类强干扰源存在的情况下,如何测得信号的准确频率,是个问题。FFT要求长时间采样,对于嵌入式应用,采样点数和时间消耗都是个问题。而即使用示波器的波形叠加功能&…

Debian12 U盘安装

今天买了一个蓝牙适配器,想着在我的Centos7上把这个蓝牙使用起来,但遗憾的是即使经过淘宝客服的远程操作也无法正常使用起来,原因是我的Centos版本太低,有些头文件缺失内容导致编译不过,然后蓝牙驱动无法正常安装。在客…

【八】spring boot集成数据库连接池druid

spring boot集成数据库连接池druid 最近在进行程序优化的过程中发现程序瓶颈在数据库连接这块,于是开始研究怎么对数据库连接池参数进行调优,在这个过程中发现很多人使用druid很不规范,经常会出现导入的包和配置参数不对应的情况,…

Mybatis-Plus《学习笔记(22版尚硅谷)》

一、MyBatis-Plus 1.简介 MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。 我们的愿景是成为 MyBatis 最好的搭档&…

【SpringCloud入门】-- Ribbon入门

1.什么是Ribbon? Ribbon就是netflix公司的一个开源项目,主要功能是提供客户端负载均衡算法和服务调用。Ribbon客户端组件提供了完善的配置项,如连接超时,重试等等。Ribbon作为服务消费者的负载均衡器,有两种使用方式&…

LeetCode 2481. Minimum Cuts to Divide a Circle【数学,几何】简单

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章…

AMD——CPU微架构分析

一、SoC架构 1.1 整体架构 Zeppelin 参考链接:wikichip: Zeppelin 通过infinity fabric总线将单die分成多die的SoC架构,每个Die包含两个CPU核(CCX)、2各DDR通道、USB、低功耗IO以及多个IFOP和IFIS serdes接口。 如下所述中&…

【Python 随练】三数字排序

题目: 输入三个整数 x, y, z,请按照从小到大的顺序输出这三个数。 简介: 在本篇博客中,我们将使用Python代码解决一个简单的排序问题:如何将输入的三个整数按照从小到大的顺序进行排序并输出。我们将提供问题的解析…

力扣动态规划专题(三)完全背包 518.零钱兑换II 377. 组合总和 Ⅳ 70. 爬楼梯 322. 零钱兑换 279.完全平方数 139.单词拆分 步骤及C++实现

文章目录 完全背包一维dp数组 滚动数组 518.零钱兑换II377. 组合总和 Ⅳ70. 爬楼梯322. 零钱兑换279.完全平方数139.单词拆分 完全背包 完全背包的物品数量是无限的,01背包的物品数量只有一个 完全背包和01背包分许步骤一样,唯一不同就是体现在遍历顺序上…