【问题标题】:C++ create png/bitmap from array of numbersC++ 从数字数组创建 png/位图
【发布时间】:2016-07-17 06:02:35
【问题描述】:

所以我找到了这个关于我的问题的链接,但它适用于 c#

Create a PNG from an array of bytes

我有一个可变的 int 数字数组。 我称之为“pix[]” 现在它可以是从 3 到 256 的任意大小,以后可能会更大。

我现在要做的是将其转换为像素图像。 我仍然是一个noobin c++,所以请原谅。 我尝试下载一些使使用 libpng 更容易的库,但它们似乎不起作用(ubuntu,code::blocks) 所以我有以下问题:

1) 你如何创建一个新的位图(哪些库,哪些命令)?

2) 我如何用“pix[]”中的信息填充它?

3) 如何保存?

如果它是一个问题的转贴,我也很高兴有一个链接;)

这是我到目前为止的工作,感谢您的帮助。

int main(){
 FILE *imageFile;
 int x,y,pixel,height=2,width=3;

 imageFile=fopen("image.pgm","wb");
 if(imageFile==NULL){
  perror("ERROR: Cannot open output file");
  exit(EXIT_FAILURE);
 }

 fprintf(imageFile,"P3\n");           // P3 filetype
 fprintf(imageFile,"%d %d\n",width,height);   // dimensions
 fprintf(imageFile,"255\n");          // Max pixel
  int pix[100] {200,200,200, 100,100,100, 0,0,0, 255,0,0, 0,255,0, 0,0,255};


       fwrite(pix,1,18,imageFile);


fclose(imageFile);
}

我还没有完全理解它的作用。我可以打开输出图像,但它不是数组的正确表示。

如果我改变一些东西,例如制作一个二维数组,那么图像查看器会告诉我“预期为整数”并且不会显示图像。

到目前为止一切顺利。 因为我在图像之前有数组,所以我创建了一个函数 aufrunden 来四舍五入到下一个整数,因为我想创建一个方形图像。

int aufrunden (double h)
{
int i =h;
if (h-i == 0)
  {
  return i;
  }
else
  {
  i = h+1;
  return i;
  }
}

此函数用于创建图像。 如果图像大于数组提供的信息,如下所示(a 是数组的长度)

double h;
h= sqrt(a/3.0);
int i = aufrunden(h);
FILE *imageFile;                           
int height=i,width=i;

现在可能会发生,该数组的长度为a=24aufrunden 使图像成为 3x3,因此它有 27 个值……这意味着它缺少 1 个像素的值。 或者更糟糕的是它只有a=23 long。同时创建一个 3x3 图像。

fwrite(pix,1,18,imageFile); 会在这些像素中写入什么信息?如果剩余的值只有 0,那将是最好的。

*edit 没关系,我只会在数组末尾添加 0,直到它填满整个正方形......对不起

【问题讨论】:

  • 如果只需要结果png文件,可以使用Qt框架。它包含适当的 QPixmap 类,该类具有有用的 loadFromData 方法。这是附加参考:doc.qt.io/qt-5/qpixmap.html#loadFromData。输出的png文件可以用save方法保存(doc.qt.io/qt-5/qpixmap.html#save)
  • 也许你应该解释“但他们似乎没有工作”,因为他们真的应该这样做。 (“他们”是谁?)
  • 但 codeblocks 无法识别该库,即使我正确安装了 libpng 和 pngwriter 目录

标签: c++ arrays bitmap png


【解决方案1】:

考虑使用 Netpbm 格式(pbm、pgm 或 ppm)。

这些图像是非常简单的文本文件,无需任何特殊库即可编写。然后使用一些第三方软件,如 ImageMagick、GraphicsMagick 或 pnmtopng 将您的图像转换为 PNG 格式。这是一篇描述 Netpbm format 的 wiki 文章。

这是一个简单的 PPM 图像:

P3 2 3 255
0 0 0       255 255 255
255 0 0     0 255 255
100 100 100 200 200 200

第一行包含“P3”(将其识别为文本的“幻数-PPM”)、2(宽度)、3(高度)、255(最大强度)。 第二行包含顶行的两个 RGB 像素。 第三行和第四行分别包含第 2 行和第 3 行的两个 RGB 像素。

如果您需要更大范围的强度,请使用更大的数字来获得最大强度(例如 1024),最高可达 65535。

由 Mark Setchell 编辑超出这一点 - 所以我是有罪的一方!

图像看起来像这样(放大六个像素时):

ImageMagick 的转换和放大命令如下:

convert image.ppm -scale 400x result.png

如果 ImageMagick 有点重,或者难以安装,您可以更简单地使用 NetPBM 工具(来自here),就像这样(它是一个预编译的二进制文件)

pnmtopng image.ppm > result.png

【讨论】:

  • 很好,但请稍等,所以在创建它时,语法是什么,我无法从 wiki 页面读取它。还有一种方法可以逐个像素地创建超过 255 种颜色的图像文件吗?
  • @busssard 我添加了一张图片以使其更容易可视化 - 我希望你不介意,格伦。您现在可以看到颜色确实是可能的,因为每个像素都有一个红色、绿色和蓝色值 - 所以您可以实现 1600 万种颜色......红色为 0 到 255,绿色为 0 到 255,绿色为 0 到 255蓝色。如果您想要更多,可以将第一行的 255 更改为 65535,并使用 0 到 65535 范围内的红色、绿色和蓝色 3 个值。
  • @busssard 如果你想看一些实际的代码给你一个线索,你可以在这里看看我的答案并适应...stackoverflow.com/a/22580958/2836621
  • 我非常感谢您的详细说明!所以fputc(pixel, imagefile) 是魔法命令吗? pixel 可以是数组吗?使用 Glenns 答案中给出的信息?
  • 我的图片是彩色的,你必须使用PPM,这意味着文件必须以P3P6开头。如果您使用P3,您将使用 ASCII(人类可读的数字,与 Glenn 的示例完全相同)编写数据,而在C/C++ 编程中您将使用printf "%d ",pix[0]。如果您使用P6,它仍然是PPM,但您使用fputc() 以二进制形式写入文件,它写入单个无符号字符(字节)。二进制 (P6) 的优点是文件大约小 3-4 倍。但是 ImageMagick 和 pnmtopng 会同时理解 P3P6
【解决方案2】:

如果您似乎拥有 Magick++ 并且乐于使用它,您可以使用 C/C++ 编写代码,如下所示:

////////////////////////////////////////////////////////////////////////////////
// sample.cpp
// Mark Setchell
//
// ImageMagick Magick++ sample code
//
// Compile with:
// g++ sample.cpp -o sample $(Magick++-config --cppflags --cxxflags --ldflags --libs)
////////////////////////////////////////////////////////////////////////////////
#include <Magick++.h> 
#include <iostream> 

using namespace std; 
using namespace Magick; 

int main(int argc,char **argv) 
{ 
   unsigned char pix[]={200,200,200, 100,100,100, 0,0,0, 255,0,0, 0,255,0, 0,0,255};

   // Initialise ImageMagick library
   InitializeMagick(*argv);

   // Create Image object and read in from pixel data above
   Image image; 
   image.read(2,3,"RGB",CharPixel,pix);

   // Write the image to a file - change extension if you want a GIF or JPEG
   image.write("result.png"); 
}

【讨论】:

  • 哇,这真的让它变得容易多了,我会努力让它运行起来。
  • 我可以将.ppm 转换为.png,但只能在线。你在哪里找到这个Magick++?我找不到任何下载,并且可能不知道我需要下载哪一个。我正在运行 Windows 10、64 位。谢谢
  • magick++ 是一个你必须下载然后才能与你的编译器一起使用的库
【解决方案3】:

你离你不远了——干得好!据我所知,您只有几个错误:

  1. 你有P3,如果用二进制写,你实际上需要P6

  2. 您的数据使用 int 类型,而 8 位数据需要使用 unsigned char

  3. 宽度和高度互换了。

  4. 您使用的是适用于便携式 灰色 地图的 PGM 扩展名,而您的数据是彩色的,因此您需要使用适用于便携式像素地图的 PPM 扩展名.

所以,工作代码如下所示:

#include <stdio.h>
#include <stdlib.h>

int main(){
   FILE *imageFile;
   int x,y,pixel,height=3,width=2;

   imageFile=fopen("image.ppm","wb");
   if(imageFile==NULL){
      perror("ERROR: Cannot open output file");
      exit(EXIT_FAILURE);
   }

   fprintf(imageFile,"P6\n");               // P6 filetype
   fprintf(imageFile,"%d %d\n",width,height);   // dimensions
   fprintf(imageFile,"255\n");              // Max pixel

   unsigned char pix[]={200,200,200, 100,100,100, 0,0,0, 255,0,0, 0,255,0, 0,0,255};
   fwrite(pix,1,18,imageFile);
   fclose(imageFile);
}

如果你然后运行它,你可以将生成的图像转换为一个漂亮的大 PNG

convert image.ppm -scale 400x result.png

如果您随后需要 16 位数据,则​​将 255 更改为 65535,并存储在 unsigned short 数组而不是 unsigned char 中,当您来到 fwrite() 时,您需要写入双倍字节数。

【讨论】:

  • 首先尝试使用来自 Sourceforge 的 NetPBM 库中的 pnmtopng 我在 Glenn 的答案底部显示了如何,因为它更小且更易于安装。除非你在 Linux 上,在这种情况下,只需使用 convert 命令,它是 ImageMagick 的一部分并且可能已经安装。你在哪个平台上?
  • 我使用的是 Ubuntu,我认为 ImageMagick 已安装,但我的编译器没有找到 Magick++.h 所以我不确定...
  • 还有一件事。如果数组小于定义的区域,那么它只是用随机值填充剩余的像素?
  • 如果你有并且想使用Magick++,答案会完全不同!格伦(和我)都认为您想要 “最简单” 的方式,这就是我们俩的回答方式。如果你想使用 Magick++,你需要安装magick++-dev 包并编写完全不同的代码和不同的编译和链接。不过,你会得到一个更清洁的项目。通过电子邮件聊天可能会更好
  • 回答您的其他问题,不,它不只是填满。如果您说图像是 10x20 且每像素 3 个字节,则预计为 600 字节。
【解决方案4】:

下面的代码将像素颜色的整数数组作为输入并将其写入.bmp 位图文件,或者反过来,读取.bmp 位图文件并将其图像内容存储为int 数组。它只需要&lt;fstream&gt; 库。输入参数path 可以是例如C:/path/to/your/image.bmpdata 格式为data[x+y*width]=(red&lt;&lt;16)|(green&lt;&lt;8)|blue;,其中redgreenblueblue 范围内的整数,像素位置是@ 987654336@.

#include <string>
#include <fstream>
using namespace std;
typedef unsigned int uint;
int* read_bmp(const string path, uint& width, uint& height) {
    ifstream file(path, ios::in|ios::binary);
    if(file.fail()) println("\rError: File \""+filename+"\" does not exist!");
    uint w=0, h=0;
    char header[54];
    file.read(header, 54);
    for(uint i=0; i<4; i++) {
        w |= (header[18+i]&255)<<(8*i);
        h |= (header[22+i]&255)<<(8*i);
    }
    const int pad=(4-(3*w)%4)%4, imgsize=(3*w+pad)*h;
    char* img = new char[imgsize];
    file.read(img, imgsize);
    file.close();
    int* data = new int[w*h];
    for(uint y=0; y<h; y++) {
        for(uint x=0; x<w; x++) {
            const int i = 3*x+y*(3*w+pad);
            data[x+(h-1-y)*w] = (img[i]&255)|(img[i+1]&255)<<8|(img[i+2]&255)<<16;
        }
    }
    delete[] img;
    width = w;
    height = h;
    return data;
}
void write_bmp(const string path, const uint width, const uint height, const int* const data) {
    const uint pad=(4-(3*width)%4)%4, filesize=54+(3*width+pad)*height; // horizontal line must be a multiple of 4 bytes long, header is 54 bytes
    char header[54] = { 'B','M', 0,0,0,0, 0,0,0,0, 54,0,0,0, 40,0,0,0, 0,0,0,0, 0,0,0,0, 1,0,24,0 };
    for(uint i=0; i<4; i++) {
        header[ 2+i] = (char)((filesize>>(8*i))&255);
        header[18+i] = (char)((width   >>(8*i))&255);
        header[22+i] = (char)((height  >>(8*i))&255);
    }
    char* img = new char[filesize];
    for(uint i=0; i<54; i++) img[i] = header[i];
    for(uint y=0; y<height; y++) {
        for(uint x=0; x<width; x++) {
            const int color = data[x+(height-1-y)*width];
            const int i = 54+3*x+y*(3*width+pad);
            img[i  ] = (char)( color     &255);
            img[i+1] = (char)((color>> 8)&255);
            img[i+2] = (char)((color>>16)&255);
        }
        for(uint p=0; p<pad; p++) img[54+(3*width+p)+y*(3*width+pad)] = 0;
    }
    ofstream file(path, ios::out|ios::binary);
    file.write(img, filesize);
    file.close();
    delete[] img;
}

代码 sn-p 的灵感来自https://stackoverflow.com/a/47785639/9178992

对于.png 图像,使用lodepng.cpplodepng.h

#include <string>
#include <vector>
#include <fstream>
#include "lodepng.h"
using namespace std;
typedef unsigned int uint;
int* read_png(const string path, uint& width, uint& height) {
    vector<uchar> img;
    lodepng::decode(img, width, height, path, LCT_RGB);
    int* data = new int[width*height];
    for(uint i=0; i<width*height; i++) {
        data[i] = img[3*i]<<16|img[3*i+1]<<8|img[3*i+2];
    }
    return data;
}
void write_png(const string path, const uint width, const uint height, const int* const data) {
    uchar* img = new uchar[3*width*height];
    for(uint i=0; i<width*height; i++) {
        const int color = data[i];
        img[3*i  ] = (color>>16)&255;
        img[3*i+1] = (color>> 8)&255;
        img[3*i+2] =  color     &255;
    }
    lodepng::encode(path, img, width, height, LCT_RGB);
    delete[] img;
}

【讨论】:

    猜你喜欢
    • 2015-11-19
    • 1970-01-01
    • 2014-02-28
    • 2012-08-22
    • 1970-01-01
    • 2021-03-30
    • 1970-01-01
    相关资源
    最近更新 更多