V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
h3xz
V2EX  ›  程序员

c++对大量图片进行序列化和反序列化

  •  
  •   h3xz · 2024-04-10 14:19:07 +08:00 · 2449 次点击
    这是一个创建于 370 天前的主题,其中的信息可能已经有所发展或是发生改变。
    工作中开发的软件有这样一个需求:
    程序用了某种算法对大量图片(约有 9 千多张,每张约 4M )进行了处理。我该怎么将这所有的图片以数组的形式序列化为一个文件夹,当从硬盘将这些图片读回内存时可以随机访问图片,以便用户第二次打开。
    34 条回复    2024-04-11 15:48:26 +08:00
    feirisu
        1
    feirisu  
       2024-04-10 14:23:52 +08:00
    没看懂
    wangtian2020
        2
    wangtian2020  
       2024-04-10 14:26:40 +08:00
    不就是跟个系统自带的相册、或者说是图片查看器差不多吗。搞个缩略图差不多得了
    xtreme1
        3
    xtreme1  
       2024-04-10 14:29:08 +08:00
    意思是图片在内存里什么样就在文件里什么样?
    存 PPM 呗
    I3tZ9NgHU44xmaA4
        4
    I3tZ9NgHU44xmaA4  
       2024-04-10 14:30:01 +08:00   ❤️ 1
    9k 张图片也不多,我近千万张图片在电脑。
    将所有文件的名字存在一个文本文件里就行了,随机到那个图片的名字再读哪个图片出来,我还是用弱鸡 C#弄的。
    h3xz
        5
    h3xz  
    OP
       2024-04-10 14:40:08 +08:00
    感谢大家的回复。我没有表达清楚问题的意思,我是希望将所有的图片能够序列化到一个文件中(不是文件夹),我保存的不单单只有图片,还有与图片相关的一系列的数据,编号等,因此我想请教有没有这样一种第三方库:能够以将图片,数据,编号,日期作为一个对象进行序列化,我起初使用的是 protobuf ,但是该库不能序列化太大的文件 。如果采用的是读取图片的名称,用户一旦修改了图片名,那么读取图片就会出错了。非常感谢大家的建议。
    xieym
        6
    xieym  
       2024-04-10 14:40:49 +08:00
    sqlite?
    h3xz
        7
    h3xz  
    OP
       2024-04-10 14:42:20 +08:00
    @xtreme1 我想的是每次都保存对象,这个对象包含了图片,编号,和一系列相关数据。
    hitmanx
        8
    hitmanx  
       2024-04-10 14:49:57 +08:00   ❤️ 1
    看不出来这个用例里面,图片和普通的二进制文件对于序列化有什么区别?你把图片想象成一个普通的二进制文件不就行了。
    那么现在就是序列化一个 array 或者 map ,里面每个元素包含一些 metadata(number, string etc)和一个不定长的二进制文件。
    F7TsdQL45E0jmoiG
        9
    F7TsdQL45E0jmoiG  
       2024-04-10 14:50:07 +08:00
    这不就是对象存储干的事儿嘛
    StrangerA
        10
    StrangerA  
       2024-04-10 14:51:40 +08:00
    9k * 4M ,36 个 G 存入一个文件,还想瞬间读写?

    建议存缩略图进 sqlite
    jones2000
        11
    jones2000  
       2024-04-10 14:52:16 +08:00
    内存数据库
    mxalbert1996
        12
    mxalbert1996  
       2024-04-10 14:53:40 +08:00 via Android   ❤️ 1
    换个思路,直接把文件打包成一个不带压缩的 zip 文件。zip 文件是支持随机读取的。
    3dwelcome
        13
    3dwelcome  
       2024-04-10 15:01:15 +08:00
    zip 文件可以,但是大小有一定限制,有 zip64 的变种,可以支持 4G 以上的大文件。
    picone
        14
    picone  
       2024-04-10 15:12:41 +08:00
    记录 offset ,fseek 即可
    leonshaw
        15
    leonshaw  
       2024-04-10 15:16:15 +08:00
    在文件上建文件系统,mount
    taygetus
        16
    taygetus  
       2024-04-10 15:20:00 +08:00
    #include <boost/serialization/map.hpp>
    #include <boost/archive/text_oarchive.hpp>
    #include <boost/archive/text_iarchive.hpp>
    linauror
        17
    linauror  
       2024-04-10 15:23:31 +08:00
    如果要存的数据不多,直接把这些数据给图片命名呢,比如 xxxx_yyyy_20240101235959.jpg
    shyrock
        18
    shyrock  
       2024-04-10 15:39:44 +08:00   ❤️ 3
    建议直接说原始需求,而不是你分析之后想要的算法。。。
    shakeyo
        19
    shakeyo  
       2024-04-10 16:03:44 +08:00
    简单点就 zip
    麻烦点就自己设计一个文件格式,类似|magicNumber|version|indexOffset|fileOffset|
    生成过程就是计算头,填充并依次将头、文件索引表、文件数据 1 ,文件数据 2 。。。。。写入到文件中
    edward1987
        20
    edward1987  
       2024-04-10 16:09:30 +08:00
    确定需求是 [随机访问图片] 而不是 [随时访问图片] ? 怎么看着像是在做一个图文游戏,然后想用序列化的方式加密啊
    xytysingle
        21
    xytysingle  
       2024-04-10 17:15:22 +08:00
    用 base64 编码图片为字符串,比如一行一个编号,日期,图片的 base64 字符串
    bertonzh
        22
    bertonzh  
       2024-04-10 17:21:04 +08:00
    .img 文件
    knva
        23
    knva  
       2024-04-10 17:21:22 +08:00
    还是实现个文件系统简单。
    kokutou
        24
    kokutou  
       2024-04-10 17:22:10 +08:00 via Android
    啥都无所谓的话。。。直接丢数据库?
    feirisu
        25
    feirisu  
       2024-04-10 17:37:04 +08:00   ❤️ 1
    大概懂了,就是游戏资源的打包方式呗,其设计目的就是不让用户修改,又能快速定位资源,格式也简单。

    但是单文件过大建议考虑分包。

    通常格式是:
    固定自定义格式头,包含多少个文件[int]等
    资源名长度[int]、资源名[char*]、资源尺寸[int]、资源内容[byte*]

    读取时可以直接打开文件,用文件句柄偏移的方式可以快速构建索引以及针对对应的偏移取出数据。
    GeruzoniAnsasu
        26
    GeruzoniAnsasu  
       2024-04-10 17:37:13 +08:00
    数据库中存索引和 meta info ,图片文件一律改名成 ID ,完毕。

    IO 不够就把文件丢进分布式文件系统里从网络读
    mioktiar56
        27
    mioktiar56  
       2024-04-10 17:37:53 +08:00
    我怀疑你是微信上找我的那个人

    内存映射即可解决
    feirisu
        28
    feirisu  
       2024-04-10 17:40:39 +08:00
    就依照我#25 写的,可以直接让 chatgpt 给你封装一套打包加拆包的类。
    wxf666
        29
    wxf666  
       2024-04-10 17:56:20 +08:00   ❤️ 1
    @h3xz 那就存 SQLite 数据库呗。。

    优点:

    - 单文件
    - 可随机读写
    - 依赖库只有几百 KB
    - 有原生 C/C++ 接口
    - 可存一系列数据(编号、日期、数据、图片文件本身、……),单个数据最大 2GB


    写了 10GB 共 2500 条数据(每条包含 4MB 图片及数据),
    再测试下,随机读取图片速度(测试前,已用 RamMap 清空系统文件缓存):

    - 机械盘:27 条数据/秒( 150 MB/s 顺序读取,0.65 MB/s 随机 4K 读取)
    - 内存盘:323 条数据/秒( 6754 MB/s 顺序读取,310 MB/s 随机 4K 读取,感觉明显没吃满 IO )
    rpWQTyfsAjMCKgPA
        30
    rpWQTyfsAjMCKgPA  
       2024-04-11 00:27:19 +08:00 via iPhone   ❤️ 1
    @h3xz 谷歌的 protobuf 。你这个需求和深度学习里准备训练数据十分类似:都是要把图片和一些 meta 信息(标注)序列化到硬盘上方便 retrieval 。
    h3xz
        31
    h3xz  
    OP
       2024-04-11 11:03:31 +08:00
    @feirisu 今天测试了一下,我存了 1000 张 4M/张 的图片到 SQLite 中,为什么显示 db 文件占了 11 个 G 呀
    h3xz
        32
    h3xz  
    OP
       2024-04-11 11:10:34 +08:00
    @wxf666 今天测试了一下,我存了 1000 张 4M/张 的图片到 SQLite 中,为什么显示 db 文件占了 11 个 G 呀
    ma46
        33
    ma46  
       2024-04-11 11:40:43 +08:00
    @h3xz 你是不是直接存了图片的矩阵数据, 因为你看到的文件大小其实是图像压缩编码后的大小
    wxf666
        34
    wxf666  
       2024-04-11 15:48:26 +08:00   ❤️ 1
    @h3xz 不知道你是怎么存的。我写个示例,你按情况改了后,执行看看?


    1. 准备 SQLite 环境

    原因:
    ① 不想在这儿写 C/C++,太冗长。
    ② 后文的脚本,需要用到 dll 没有的,仅命令行版本才有的 readfile 函数。

    步骤:
    ① 打开 https://sqlite.org/download.html
    ② 找到 sqlite-tools-win-x64-3450200.zip (当前版本)并下载(若 Linux/MacOS 找对应平台的)
    ③ 解压,得到 sqlite3.exe


    2. 准备(存数据的) SQL 文件

    参考以下内容,按你自己情况更改后,用 UTF-8 编码,保存为 test.sql 。

    ```sql
    -- 把默认页大小 4KB 改为 64KB 。因为你大部分数据都很大,一次读取便加载更多数据,能提速
    PRAGMA page_size = 65536;

    CREATE TABLE image (
    id INTEGER PRIMARY KEY,
    -- 假设你通过名字来定位图片及其他数据。UNIQUE 既创建了索引,也保证名字唯一
    name TEXT NOT NULL UNIQUE,
    -- 注意,默认生成的时间,是 UTC 时间,比中国慢 8 个小时
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    extra_data JSON,
    data BLOB NOT NULL
    );

    BEGIN;
    -- 省略 id ,SQLite 则会自动填写为最大 ID + 1 。
    -- 单引号内的 \ 不用转义。若要表示 ',双写即可。如 'Kai''Sa.jpg'。
    -- 文件路径不要包含中文,因为 SQLite 会把 UTF8 字符串,原样调用系统 API ,然而 Windows 会以为是 GBK 编码。。
    -- 如果文件路径一定包含中文,请保证其它字符串(如 name 列)没有中文,然后 SQL 文件转为 GBK 编码。
    INSERT INTO image (name, data) VALUES ('aaa.jpg', readfile('C:\aaa.jpg'));
    INSERT INTO image (name, data) VALUES ('bbb.jpg', readfile('C:\bbb.jpg'));
    INSERT INTO image (name, extra_data, data) VALUES ('ccc.jpg', '{"width": 123, "height": 456}', readfile('C:\ccc.jpg'));
    COMMIT;

    -- 可选:碎片整理数据库文件,并去除冗余空间,达到瘦身紧实的效果。
    -- VACUUM;

    SELECT printf('写入了 %d 个文件,共 %d 字节。', COUNT(*), SUM(LENGTH(data))) FROM image;
    ```


    3. 执行

    ```shell
    sqlite3.exe images.db < test.sql
    ```
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3025 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 13:26 · PVG 21:26 · LAX 06:26 · JFK 09:26
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.