-1

I am working in c++ trying to create an image and save it to a given directory. I don't care what type of image file it is (PNG, jpeg, bitmap, etc.), I just want it to be viewable in Windows 10. The data stream is in the following form: std::vector<unsigned char>.

I would like to do this natively in c++, but I am not against using a library if it is straightforward to implement and lightweight.

I have this working in C# using the following code, but I don't know if there is a direct translation into c++

// C# code to translate into c++?
var image = new BitmapImage();
using (var ms = new MemoryStream(message.ImageData))
{
  image.BeginInit();
  image.CacheOption = BitmapCacheOption.OnLoad; 
  image.StreamSource = ms;
  image.EndInit();
  image.Freeze(); 
}
BeanKing
  • 11
  • 2
  • no. there is no direct translation to C++. You will need to use a library to store images, there is nothing for images in the standard library – 463035818_is_not_an_ai Aug 11 '22 at 18:53
  • @463035818_is_not_a_number Do you know of a library that you recommend? – BeanKing Aug 11 '22 at 18:54
  • I would try to find something in Qt – 463035818_is_not_an_ai Aug 11 '22 at 18:57
  • [Imagemagick](https://www.imagemagick.org/Magick++/Documentation.html) might be an alternative. – Daniel Langr Aug 11 '22 at 19:36
  • Assuming you have raw bytes for the image, you have a number of choices. I'd probably use [CXImage](https://github.com/chkob/cximage_full) because I've used it before, but I don't think it's maintained any more. [libpng](https://github.com/glennrp/libpng) is still maintained, but not as easy to use. [OpenCV](https://github.com/opencv/opencv) is kind of overkill, but still works fine. IJG's [jpeg library](https://www.ijg.org/) seems kind of like libpng to me--small and works, but not as easy to use as some others. But there are *lots* of others as well (including Windows GDI and GDI+) as well. – Jerry Coffin Aug 11 '22 at 19:40
  • @DanielLangr Do you know the sytax I might use to accomplish this? I looked at the documentation and still am a bit confused. Another problem is that I don't have dimensions for the image I am trying to create, only the the data stream. – BeanKing Aug 11 '22 at 19:44
  • 2
    @BeanKing What does the *content* of the data stream look like? Without knowing what the *data* looks like, there is no way to tell you how to *encode* the data in any particular image format – Remy Lebeau Aug 11 '22 at 19:46
  • I'd go with CImg at https://cimg.eu/ Easy to use, header-only, so no linking issues. Put `[cimg]` in StackOverflow search box to find examples. – Mark Setchell Aug 11 '22 at 20:11
  • You could also, even more simply, write a NetPBM file, see https://stackoverflow.com/a/48839768/2836621 – Mark Setchell Aug 11 '22 at 20:14
  • @BeanKing Unfortunately I don't. I have never used Imagemagick in this way. I just know that it is a library that is able to open and process images. – Daniel Langr Aug 12 '22 at 09:43

2 Answers2

0

C# implementations have image routines in the language's standard library. C++ does not. So there is no equivalent code in standard C++: you need to use a third party library. On Windows you could use Win32.

Below I use stb_image_write.h, which can be found here; the stb libraries are barebones 1-file or 2-file libraries typically used in independent game development where having a dependency on libPNG et. al. would be overkill.

#include <vector>

#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"

std::vector<unsigned char> generate_some_image(int wd, int hgt)
{
    // this is just an example for tutorial purposes ... generate a red circle
    // in a white field.

    std::vector<unsigned char> data(wd * hgt * 4);

    int c_x = wd / 2;
    int c_y = hgt / 2;
    int radius = c_x;
    int i = 0;
    for (int y = 0; y < hgt; y++) {
        for (int x = 0; x < wd; x++) {
            if ((x - c_x) * (x - c_x) + (y - c_y) * (y - c_y) <= radius * radius) {
                data[i++] = 255;
                data[i++] = 0;
                data[i++] = 0;
                data[i++] = 255;
            } else {
                data[i++] = 255;
                data[i++] = 255;
                data[i++] = 255;
                data[i++] = 255;
            }
        }
    }

    return data;
}

int main()
{
    const int wd = 128;
    const int hgt = 128;
    std::vector<unsigned char> data = generate_some_image(wd, hgt);
    return stbi_write_png( "c:\\test\\foo.png", wd, hgt, 4, &(data[0]), 4*wd);
}
jwezorek
  • 8,592
  • 1
  • 29
  • 46
  • I believe this is exactly what I am looking for. The only issue I have with this solution is I do not know the width or height of the image ahead of time. I only have the data stream to work with. – BeanKing Aug 11 '22 at 20:23
  • if you don't know the width or height then you don't have an image. i mean, unless the data you are getting is already in some image file format, in memory. I assumed you have pixel values forming an image and want to encode them to an image format + write it to disk – jwezorek Aug 11 '22 at 20:24
0

There is no default standard C++ library for creating an image file with a specific format (PNG, Bitmap ... etc). However, there are tricks to create an image and make it viewable if this is all that you need. The ways are discussed below:

  1. Use a library as jwezorek! mentioned in his answer. There are tons of libraries that can help: OpenCV, STB ... etc
  2. Create a function that creates a file and streams the pixels data to that file in a specific format (Can be fun for some and a headache for others).
  3. Save the image data as raw data (pixels data only) in a file, and use a simpler programming language to read the file data and view the image. For instance, let's assume that you will use "python" as the simpler language to create a simple image viewer on windows 10, the code will look as follows:
import NumPy as np
import scipy.misc as smp
from PIL import Image

w = 1066
h = 600

with open('rgb.raw', 'r') as file:
    data = file.read().replace('\n', ' ')       # Replacing new lines with spaces
    rgbs = data.split(' ')                      # Get a 1D array of all the numbers (pixels) in file
    rgbs = [ int(x) for x in rgbs  if x != '']  # Change the data into integers

    nprgbs = np.array(rgbs, dtype=np.uint8)     # Define a numpy array
    arr = np.reshape(nprgbs, (h,w,3))           # Reorder the 1D array to 3D matrix (width, height, channels)

    img = Image.fromarray(arr)                  # Create an Image from the pixels. 
    img.show()                                  # View the image in a window (It uses the default viewer of the OS)

The above code will read a file containing the RGB channels of an image and view it on the default viewer.

As you can see, you can do it in a variety of ways, choose the simplest and the most suitable for you. In conclusion, the answer to your question is "No" unless you use a custom library, create your own functions, or use simpler programming languages to read the stream of data and use them to create the file.

Mohido
  • 1
  • 2