I'm trying to understand how the hpp, cpp, and main all work together. for this example I'm working on a code that coverts ARGB to RGBA and I'm confused on what to put in each file.
This is my code:
color.hpp
using namespace std;
#include <stdio.h>
#include <stdio.h>
#include <iostream>
#ifndef colors_hpp
#define colors_hpp
/* colors_hpp */
string getHex();
uint32_t fromArgb();
#endif
color.cpp
#include "colors.hpp"
#include <iostream>
#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
template <typename T>
struct Color
{
public:
/* Works fine!!! */
Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255)
{
buffer((r << 0) | (g << 8) | (b << 16) | (a << 24));
}
Color(const uint32_t argb)
{
buffer = fromArgb(argb);
}
inline uint32_t fromArgb(uint32_t argb)
{
return
// Source is in format: 0xAARRGGBB
((argb & 0x00FF0000) >> 16) | //____RR
((argb & 0x0000FF00)) | //_GG_
((argb & 0x000000FF) << 16) | //__BB_
((argb & 0xFF000000)); //AA____
// Return value is in format: 0xAABBGGRR
}
inline uint8_t getRed(void) const
{
return (buffer >> 0) & 0xFF;
}
inline uint8_t getGreen(void) const
{
return (buffer >> 8) & 0xFF;
}
inline uint8_t getBlue(void) const
{
return (buffer >> 16) & 0xFF;
}
inline uint8_t getAlpha(void) const
{
return (buffer >> 24) & 0xFF;
}
/* Works fine!!!*/
std::string getHex(void) const
{
std::string result = "#";
char colorBuffer[255] = {};
// Order is intentionally end to beginning
sprintf_s(colorBuffer, 255, "%.2X", getAlpha());
result.append(colorBuffer);
sprintf_s(colorBuffer, 255, "%.2X", getBlue());
result.append(colorBuffer);
sprintf_s(colorBuffer, 255, "%.2X", getGreen());
result.append(colorBuffer);
sprintf_s(colorBuffer, 255, "%.2X", getRed());
result.append(colorBuffer);
return result;
}
private:
uint32_t buffer;
};
main.cpp
int main(int argc, char**argv) {
fromArgb(255,255,0,0);
getHex();
}
I'm not able to understand where to use or call the struct or functions, and i'm really confused on what to put in hpp, cpp, and main files.
Some advice
Remove this template <typename T>
Move struct Color { ... }; to color.hpp (all of it, you can delete color.cpp, it is not needed).
Remove using namespace std; from color.hpp
Remove string getHex(); uint32_t fromArgb(); from color.hpp
change main to this
int main(int argc, char**argv) {
Color c;
c.fromArgb(255,255,0,0);
std::cout << c.getHex() << std::endl;
}
The main problem seems to be that you don't know how objects work. In order to use the fromArgb and getHex methods you need a Color object. So in my code I declared a Color object like this Color c; and then I used that colour object like this c.fromArgb(255,255,0,0); and this c.getHex(). How to use classes and objects is a more important topic than how to organise your code into headers and cpp files.
I haven't tested these changes. If there are any further problems you can't figure out then ask again.
I'm trying to create a class that saves BMP image given the pixels.
This is my .h:
#ifndef _IMAGE_SAVER_
#define _IMAGE_SAVER_
#include <vector>
#include <fstream>
#include <sstream>
#include <string>
#include <bitset>
//#define _DEBUG_
#ifdef _DEBUG_
#include <iostream>
#endif
#include <stdint.h>
typedef struct BMFileHeader {
uint16_t _bfType = 19778;
uint32_t _bfSize;
uint16_t _bfReserved1 = 0;
uint16_t _bfReserved2 = 0;
uint32_t _bfOffBits = 54;
} BMFileHeader;
typedef struct BMInfoHeader {
uint32_t _biSize = 40;
uint32_t _biWidth;
uint32_t _biHeight;
uint16_t _biPlanes = 1;
uint16_t _biBitCount = 24;
uint32_t _biCompression = 0;
uint32_t _biSizeImage = 0;
uint32_t _biXPelsPerMeter = 3780;
uint32_t _biYPelsPerMeter = 3780;
uint32_t _biClrUser = 0;
uint32_t _biClrImportant = 0;
} BMInfoHeader;
typedef struct RGBQuad {
uint8_t _blue;
uint8_t _green;
uint8_t _red;
} RGBQuad;
class ImageSaver
{
public:
ImageSaver() = delete;
ImageSaver(const uint32_t& height, const uint32_t& width, const std::vector<RGBQuad>& pixels) : _RGBQuad_Vector(pixels) {
this->_Info_Header._biHeight = height;
this->_Info_Header._biWidth = width;
};
void saveImage(const std::string& fileName);
protected:
void setFileSize();
void createImageFile(const std::string& fileName);
private:
BMFileHeader _File_Header;
BMInfoHeader _Info_Header;
std::vector<RGBQuad> _RGBQuad_Vector;
};
#endif
this is my .cpp:
#include "ImageSaver.h"
void ImageSaver::saveImage(const std::string& fileName)
{
this->setFileSize();
this->createImageFile(fileName);
}
void ImageSaver::setFileSize()
{
uint32_t height = this->_Info_Header._biHeight;
uint32_t width = this->_Info_Header._biWidth;
this->_File_Header._bfSize = 3 * (height * width) + 54;
}
void ImageSaver::createImageFile(const std::string& fileName)
{
std::ofstream imageFile(fileName + ".bmp", std::ios::binary);
imageFile.write((char*)&this->_File_Header, 14);
imageFile.write((char*)&this->_Info_Header, 40);
size_t numberOfPixels = this->_Info_Header._biHeight * this->_Info_Header._biWidth;
for (int i = 0; i < numberOfPixels; i++) {
imageFile.write((char*)&(this->_RGBQuad_Vector[i]), 3);
}
imageFile.close();
}
and this is my main.cpp:
#include "ImageSaver.h"
#include <vector>
#include <iostream>
int main() {
std::vector<RGBQuad> pixels;
for (unsigned i = 0; i < 512 * 512; i++) {
RGBQuad pixel;
pixel._blue = 0;
pixel._green = 255;
pixel._red = 255;
pixels.push_back(pixel);
}
ImageSaver im(512, 512, pixels);
im.saveImage("scene1");
std::cout << "ESTOP0" << std::endl;
return 0;
}
Whenever I try to create an image file, it says that that image file is corrupt, even though it seems to me that I've been following BMP format properly. I've analyzed raw binary data and data seems (to me) to be correct. GIMP can open the picture, but it gives a black 512x512 picture which is not what I'm going for here.
Certainly you miss #pragma pack. Wrap your structures with #pragma pack(push,1)/#pragma pack(pop)
When structures represent memory layout and should have no padding, even if having unaligned data, pack structures.
It is very helpful to have static_assert on expected sizeof value for such cases.
Additionally, when you pack them, the RGBQuad will become only 3 bytes. If you really want triple, suggest renaming it to triple. Otherwise add dummy or alpha byte to complete it to quad.
I'm having trouble porting the following C# code to C++:
protected override void OnPaint(CefBrowser browser, CefPaintElementType type, CefRectangle[] dirtyRects
, System.IntPtr buffer, int width, int height)
{
if (isPainting == true)
return;
isPainting = true;
// Save the provided buffer (a bitmap image) as a PNG.
using (System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(width, height, width * 4, System.Drawing.Imaging.PixelFormat.Format32bppRgb, buffer))
{
bitmap.Save(#"LastOnPaint.png", System.Drawing.Imaging.ImageFormat.Png);
} // End Using bitmap
}
What it does:
Create an image from a WebSite/SVG as rendered by the latest version of Chromium embedded and save it as a file.
So this is the corresponding render-handler in C++:
void RenderHandler::OnPaint(
CefRefPtr<CefBrowser> browser,
CefRenderHandler::PaintElementType type,
const CefRenderHandler::RectList& dirtyRects,
const void* buffer, int width, int height
) {
// size_t len = sizeof(buffer) / sizeof(void*);
// printf("buffer length: %zu\n", len); // 1...
// Array size is probably: width*height * 4;
}
So I was looking into what C# does in the bitmap-constructor, which is the following:
public Bitmap(int width, int height, int stride, PixelFormat format, IntPtr scan0)
{
IntPtr bitmap = IntPtr.Zero;
int status = Gdip.GdipCreateBitmapFromScan0(width, height, stride, unchecked((int)format), new HandleRef(null, scan0), out bitmap);
Gdip.CheckStatus(status);
SetNativeImage(bitmap);
}
internal void SetNativeImage(IntPtr handle) {
if (handle == IntPtr.Zero)
throw new ArgumentException(SR.GetString(SR.NativeHandle0), "handle");
nativeImage = handle;
}
Which traces to
internal const string Gdiplus = "gdiplus.dll";
[DllImport(ExternDll.Gdiplus, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Unicode)] // 3 = Unicode
[ResourceExposure(ResourceScope.Machine)]
internal static extern int GdipCreateBitmapFromScan0(int width, int height, int stride, int format, HandleRef scan0, out IntPtr bitmap);
So I thought I could just call GdipCreateBitmapFromScan0 in gdibitmapflat and be almost finished
GpStatus WINGDIPAPI GdipCreateBitmapFromScan0(INT width
, INT height, INT stride, PixelFormat format
, BYTE* scan0, GpBitmap** bitmap)
So I gathered the necessary header-files for GDI, which was a horrible experience
#ifndef __BITMAPHELPER_H__
#define __BITMAPHELPER_H__
// #define WIN32_LEAN_AND_MEAN
#pragma warning(disable:4458)
#include <Windows.h>
#include <ObjIdl.h>
#include <minmax.h>
#include <gdiplus.h>
#include <wingdi.h>
#include <gdiplusbitmap.h>
#include <gdiplusflat.h>
using namespace Gdiplus;
#pragma comment (lib,"gdiplus.lib")
#pragma warning(default:4458)
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdint>
#include <cstdbool>
#include <algorithm>
#include <memory>
And thought this would about do it
#include "BitmapHelper.h"
static void Test()
{
GpBitmap *bitmap = NULL;
GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap); // create a bitmap object with specified width/height/color
// GpGraphics *graph;
// Image * syntaxTest = NULL;
//syntaxTest->FromFile(TEXT("d:\\abc.jpg"), true); // create an image object
// Bitmap::FromBITMAPINFO
// GpImage *image = NULL;
// Gdiplus::Image()
Bitmap *bmp = NULL;
// GdipLoadImageFromFile(TEXT("d:\\abc.jpg"), &image); // create an image object
// GdipGetImageGraphicsContext(bitmap, &graph); // create a graphic object via bitmap object
// GdipDrawImageI(graph, image, 100, 100); // draw image to this graphic object, it can be done
}
However, it turns out the compiler doesn't know GdipCreateBitmapFromScan0, although it's definitely inside #include <gdiplusflat.h>...
How to create a bitmap/image from Scan0 ?
Note:
While I am at it, I don't want to resort to C++.NET, and ideally not to the WinAPI either; because i'd like it to work on Linux too. And not to a monstrous dependency like SDL either.
So far, it looks like my possible alternatives are using this code:
https://codereview.stackexchange.com/questions/196084/read-and-write-bmp-file-in-c
which means I have to create the bitmap header myselfs.
Or I could use some code from ImageIO.
I can't quite belive that creating a simple bitmap on even a single operating-system is that hard...
Is there really no better (and portable) way to create a simple bitmap from a trivial array of pixel colors ?
And why does the compiler not find GdipCreateBitmapFromScan0 ?
If I had used LoadLibrary and GetProcAddress to invoke it instead of f*ing windows header files, I'd be about finished by now...
And why does #include <gdiplus.h> not include its own dependencies ?
Your looking at the internals of .NET have led you toward using a function that's not part of the documented, public interface of GDI+. It looks to me like that's the real cause of most of your problems.
What I think you probably want to do is start by creating a GdiPlus::Bitmap object from your pixels. It has a constructor that looks like it'll directly accept your data.
Once you've created the Bitmap object, you call its Save member function. Bitmap is publicly derived from Image, so you're basically dealing with the normal Image::Save to generate a PNG.
If you want to eliminate the dependency on Windows code, you might consider using (for one obvious possibility) libpng instead. This gives you quite a lot more control over the process, at the expense of being quite a bit more work to use (depending on what you want to do, probably on the order of a half dozen to a dozen lines of code rather than one or two).
So, after having done this in both GDI+ and raw C, I can safely say that it's actually faster, and not to mention considerably less problematic and less google-intensive just doing the image-handling without GDI/GDI+. Whoever implemented GDI+ has a major brain damage.
Since I haven't yet handled transparency properly, and not yet incorporated lodepng, I've added GDI+ as an optional extra option, for the time being.
// A program to read, write, and crop BMP image files.
#include "Bmp.h"
// Make a copy of a string on the heap.
// - Postcondition: the caller is responsible to free
// the memory for the string.
char *_string_duplicate(const char *string)
{
char *copy = (char*)malloc(sizeof(*copy) * (strlen(string) + 1));
if (copy == NULL)
{
// return "Not enough memory for error message";
const char* error_message = "Not enough memory for error message";
size_t len = strlen(error_message);
char* error = (char*)malloc(len * sizeof(char) + 1);
strcpy(error, error_message);
return error;
}
strcpy(copy, string);
return copy;
}
// Check condition and set error message.
bool _check(bool condition, char **error, const char *error_message)
{
bool is_valid = true;
if (!condition)
{
is_valid = false;
if (*error == NULL) // to avoid memory leaks
{
*error = _string_duplicate(error_message);
}
}
return is_valid;
}
// Write an image to an already open file.
// - Postcondition: it is the caller's responsibility to free the memory
// for the error message.
// - Return: true if and only if the operation succeeded.
bool write_bmp(FILE *fp, BMPImage *image, char **error)
{
// Write header
rewind(fp);
size_t num_read = fwrite(&image->header, sizeof(image->header), 1, fp);
if (!_check(num_read == 1, error, "Cannot write image"))
{
return false;
}
// Write image data
num_read = fwrite(image->data, image->header.image_size_bytes, 1, fp);
if (!_check(num_read == 1, error, "Cannot write image"))
{
return false;
}
return true;
}
// Free all memory referred to by the given BMPImage.
void free_bmp(BMPImage *image)
{
free(image->data);
free(image);
}
// Open file. In case of error, print message and exit.
FILE *_open_file(const char *filename, const char *mode)
{
FILE *fp = fopen(filename, mode);
if (fp == NULL)
{
fprintf(stderr, "Could not open file %s\n", filename);
exit(EXIT_FAILURE);
}
return fp;
}
// Close file and release memory.void _clean_up(FILE *fp, BMPImage *image, char **error)
void _clean_up(FILE *fp, BMPImage *image, char **error)
{
if (fp != NULL)
{
fclose(fp);
}
free_bmp(image);
free(*error);
}
// Print error message and clean up resources.
void _handle_error(char **error, FILE *fp, BMPImage *image)
{
fprintf(stderr, "ERROR: %s\n", *error);
_clean_up(fp, image, error);
exit(EXIT_FAILURE);
}
void write_image(const char *filename, BMPImage *image, char **error)
{
FILE *output_ptr = _open_file(filename, "wb");
if (!write_bmp(output_ptr, image, error))
{
_handle_error(error, output_ptr, image);
}
fflush(output_ptr);
fclose(output_ptr);
_clean_up(output_ptr, image, error);
}
// Return the size of an image row in bytes.
// - Precondition: the header must have the width of the image in pixels.
uint32_t computeImageSize(BMPHeader *bmp_header)
{
uint32_t bytes_per_pixel = bmp_header->bits_per_pixel / BITS_PER_BYTE;
uint32_t bytes_per_row_without_padding = bmp_header->width_px * bytes_per_pixel;
uint32_t padding = (4 - (bmp_header->width_px * bytes_per_pixel) % 4) % 4;
uint32_t row_size_bytes = bytes_per_row_without_padding + padding;
return row_size_bytes * bmp_header->height_px;
}
#ifdef USE_GDI
#pragma warning(disable:4189)
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
Gdiplus::ImageCodecInfo* pImageCodecInfo = NULL;
Gdiplus::GetImageEncodersSize(&num, &size);
if (size == 0)
return -1; // Failure
pImageCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(size));
if (pImageCodecInfo == NULL)
return -1; // Failure
Gdiplus::GetImageEncoders(num, size, pImageCodecInfo);
for (UINT j = 0; j < num; ++j)
{
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
} // if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
} // Next j
free(pImageCodecInfo);
return -1; // Failure
}
// https://github.com/lvandeve/lodepng
static bool notInitialized = true;
void WriteBitmapToFile(const char *filename, int width, int height, const void* buffer)
{
// HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (notInitialized)
{
// https://learn.microsoft.com/en-us/windows/desktop/api/gdiplusinit/nf-gdiplusinit-gdiplusstartup
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::Status isOk = Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
if (isOk != Gdiplus::Status::Ok)
{
printf("Failed on GdiplusStartup\n");
}
notInitialized = false;
// defer
// GdiplusShutdown(gdiplusToken);
} // End if (notInitialized)
// https://learn.microsoft.com/en-us/windows/desktop/gdiplus/-gdiplus-constant-image-pixel-format-constants
Gdiplus::Bitmap* myBitmap = new Gdiplus::Bitmap(width, height, width*4, PixelFormat32bppARGB, (BYTE*)buffer);
// myBitmap->RotateFlip(Gdiplus::Rotate180FlipY);
CLSID pngClsid;
// int result = GetEncoderClsid(L"image/tiff", &tiffClsid);
int result = GetEncoderClsid(L"image/png", &pngClsid);
printf("End GetEncoderClsid:\n");
if (result == -1)
printf("Error: GetEncoderClsid\n");
// throw std::runtime_error("Bitmap::Save");
// if (Ok != myBitmap->Save(L"D\foobartest.png", &pngClsid)) printf("Error: Bitmap::Save");
// WTF ? I guess a standard C/C++-stream would have been too simple ?
IStream* oStream = nullptr;
if (CreateStreamOnHGlobal(NULL, TRUE, (LPSTREAM*)&oStream) != S_OK)
printf("Error on creating an empty IStream\n");
Gdiplus::EncoderParameters encoderParameters;
encoderParameters.Count = 1;
encoderParameters.Parameter[0].Guid = Gdiplus::EncoderQuality;
encoderParameters.Parameter[0].Type = Gdiplus::EncoderParameterValueTypeLong;
encoderParameters.Parameter[0].NumberOfValues = 1;
ULONG quality = 100;
encoderParameters.Parameter[0].Value = &quality;
// https://learn.microsoft.com/en-us/windows/desktop/api/gdiplusheaders/nf-gdiplusheaders-image-save(inistream_inconstclsid_inconstencoderparameters)
if (Gdiplus::Status::Ok != myBitmap->Save(oStream, &pngClsid, &encoderParameters))
printf("Error: Bitmap::Save\n");
// throw std::runtime_error("Bitmap::Save");
ULARGE_INTEGER ulnSize;
LARGE_INTEGER lnOffset;
lnOffset.QuadPart = 0;
oStream->Seek(lnOffset, STREAM_SEEK_END, &ulnSize);
oStream->Seek(lnOffset, STREAM_SEEK_SET, NULL);
uint8_t *pBuff = new uint8_t[(unsigned int)ulnSize.QuadPart];
ULONG ulBytesRead;
oStream->Read(pBuff, (ULONG)ulnSize.QuadPart, &ulBytesRead);
FILE *output_ptr = _open_file(filename, "wb");
fwrite((void*)pBuff, sizeof(uint8_t), (unsigned int)ulnSize.QuadPart, output_ptr);
fflush(output_ptr);
fclose(output_ptr);
oStream->Release();
delete pBuff;
delete myBitmap;
// https://renenyffenegger.ch/notes/development/Base64/Encoding-and-decoding-base-64-with-cpp
// std::string rotated_string = base64_encode((const unsigned char*)pBuff, ulnSize.QuadPart);
}
#pragma warning(default:4189)
#else
// TODO: PNG-Encoder
// https://github.com/lvandeve/lodepng
// https://lodev.org/lodepng/
BMPImage * CreateBitmapFromScan0(int32_t w, int32_t h, uint8_t* scan0)
{
BMPImage *new_image = (BMPImage *)malloc(sizeof(*new_image));
BMPHeader *header = (BMPHeader *)malloc(sizeof(*header));
new_image->header = *header;
new_image->header.type = MAGIC_VALUE;
new_image->header.bits_per_pixel = BITS_PER_PIXEL;
new_image->header.width_px = w;
new_image->header.height_px = h;
new_image->header.image_size_bytes = computeImageSize(&new_image->header);
new_image->header.size = BMP_HEADER_SIZE + new_image->header.image_size_bytes;
new_image->header.dib_header_size = DIB_HEADER_SIZE;
new_image->header.offset = (uint32_t) sizeof(BMPHeader);
new_image->header.num_planes = 1;
new_image->header.compression = 0;
new_image->header.reserved1 = 0;
new_image->header.reserved2 = 0;
new_image->header.num_colors = 0;
new_image->header.important_colors = 0;
new_image->header.x_resolution_ppm = 3780; // image->header.x_resolution_ppm;
new_image->header.y_resolution_ppm = 3780; // image->header.y_resolution_ppm;
new_image->data = (uint8_t*)malloc(sizeof(*new_image->data) * new_image->header.image_size_bytes);
memcpy(new_image->data, scan0, new_image->header.image_size_bytes);
return new_image;
}
void WriteBitmapToFile(const char *filename, int width, int height, const void* buffer)
{
BMPImage * image = CreateBitmapFromScan0((int32_t)width, (int32_t)height, (uint8_t*)buffer);
char *error = NULL;
write_image(filename, image, &error);
}
#endif
Header:
#ifndef BITMAPLION_BITMAPINFORMATION_H
#define BITMAPLION_BITMAPINFORMATION_H
#ifdef __cplusplus
// #include <iostream>
// #include <fstream>
#include <cstdio>
#include <cstdlib>
#include <cstdint>
#include <cstring>
#else
#include <stdio.h>
#include <stdlib.h> // for malloc
#include <stdint.h>
#include <stdbool.h>
#include <string.h> // for strlen, strcopy
#endif
#ifdef __linux__
//linux specific code goes here
#elif _WIN32
// windows specific code goes here
#pragma warning(disable:4458)
#include <Windows.h>
#include <ObjIdl.h>
#include <minmax.h>
#include <gdiplus.h>
// #include <gdiplusheaders.h>
// #include <wingdi.h>
// #include <gdiplusbitmap.h>
// #include <gdiplusflat.h>
// #include <Gdipluspixelformats.h>
#pragma comment (lib,"gdiplus.lib")
// using namespace Gdiplus;
#pragma warning(default:4458)
#else
#endif
#define BMP_HEADER_SIZE 54
#define DIB_HEADER_SIZE 40
// Correct values for the header
#define MAGIC_VALUE 0x4D42
#define NUM_PLANE 1
#define COMPRESSION 0
#define NUM_COLORS 0
#define IMPORTANT_COLORS 0
#define BITS_PER_BYTE 8
// #define BITS_PER_PIXEL 24
#define BITS_PER_PIXEL 32
#ifdef _MSC_VER
#pragma pack(push) // save the original data alignment
#pragma pack(1) // Set data alignment to 1 byte boundary
#endif
typedef struct
#ifndef _MSC_VER
__attribute__((packed))
#endif
{
uint16_t type; // Magic identifier: 0x4d42
uint32_t size; // File size in bytes
uint16_t reserved1; // Not used
uint16_t reserved2; // Not used
uint32_t offset; // Offset to image data in bytes from beginning of file
uint32_t dib_header_size; // DIB Header size in bytes
int32_t width_px; // Width of the image
int32_t height_px; // Height of image
uint16_t num_planes; // Number of color planes
uint16_t bits_per_pixel; // Bits per pixel
uint32_t compression; // Compression type
uint32_t image_size_bytes; // Image size in bytes
int32_t x_resolution_ppm; // Pixels per meter
int32_t y_resolution_ppm; // Pixels per meter
uint32_t num_colors; // Number of colors
uint32_t important_colors; // Important colors
} BMPHeader;
#ifdef _MSC_VER
#pragma pack(pop) // restore the previous pack setting
#endif
typedef struct {
BMPHeader header;
// unsigned char* data;
// It is more informative and will force a necessary compiler error
// on a rare machine with 16-bit char.
uint8_t* data;
} BMPImage;
// #define USE_GDI true
#ifndef USE_GDI
BMPImage * CreateBitmapFromScan0(int32_t w, int32_t h, uint8_t* scan0);
#endif
void WriteBitmapToFile(const char *filename, int width, int height, const void* buffer);
#endif //BITMAPLION_BITMAPINFORMATION_H
I have an image and want to encode it with OpenH264.
So far this is the code I derived from their wiki:
#include <fstream>
#include <iterator>
#include <iostream>
#include <codec_api.h> //standard api for openh264
//additional libaries used by sample code
#include <codec_app_def.h>
#include <codec_def.h>
#include <codec_ver.h>
#include <assert.h>
#include <vector>
#include <cstring>
int main()
{
//parameter values
int width = 1920;
int height = 1080;
int framerate = 60;
int bitrate = 5000000;
int total_num = 500; //what does this value do?
//end parameter values
//Read in the File from bmp
std::vector<char> buf; //to store the image information
std::basic_ifstream<char> file("/home/megamol/Git/h264_sample/build/test.bmp", std::ios::binary); //opens bitstream to source
buf = std::vector<char>((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); // reads in data to the vector
std::cout << "sizeof buf: " << buf.size() << std::endl;
//Step 1: set up Encoder
ISVCEncoder* encoder_; //declaration of encoder pointer
int rv = WelsCreateSVCEncoder (&encoder_);
//Step 2: initialize with basic parameter
SEncParamBase param;
memset(¶m, 0, sizeof (SEncParamBase));
param.iUsageType = EUsageType::SCREEN_CONTENT_REAL_TIME;
param.fMaxFrameRate = framerate;
param.iPicWidth = width;
param.iPicHeight = height;
param.iTargetBitrate = bitrate; //default value of example
encoder_->Initialize(¶m);
//Step 3: set video format
int videoFormat = videoFormatI420;
encoder_->SetOption (ENCODER_OPTION_DATAFORMAT, &videoFormat);
//Step 4: encocode and store output bitstream
int frameSize = width * height * 3 / 2;
buf.resize(frameSize);
SFrameBSInfo info;
std::vector<char> compressedData;
memset (&info, 0, sizeof (SFrameBSInfo));
SSourcePicture pic;
memset (&pic, 0, sizeof (SSourcePicture));
pic.iPicWidth = width;
pic.iPicHeight = height;
pic.iColorFormat = videoFormatI420;
pic.iStride[0] = pic.iPicWidth;
pic.iStride[1] = pic.iStride[2] = pic.iPicWidth >> 1;
pic.pData[0] = reinterpret_cast<unsigned char*>(&buf[0]);
pic.pData[1] = pic.pData[0] + width * height;
pic.pData[2] = pic.pData[1] + (width * height >> 2);
//encodes the frame
rv = encoder_->EncodeFrame (&pic, &info); // encodes the Frame
//encoding done encoded Frame should be stored in &info
//begin decoding block
ISVCDecoder *pSvcDecoder;
unsigned char *pBuf= &info;
return 0;
}
I'm not entirely sure whether this is the correct usage of OpenH264 but I'm also not sure how to test it properly.
Now the code example is kind of poorly documented.
What is BufferedData buf; for example? I get that that's supposed to be the input but what is that type? Like how do I load my test.bmp as BufferedData? I don't think that I'm doing that correctly yet.
Another thing I'm pretty confused about is how do I access the output after the encoding? In the example it just says //output bitstream and nothing about saving this output anywhere. I thought the output was info like it says in the codec_api.h header file:
/**
* #brief Encode one frame
* #param kpSrcPic the pointer to the source luminance plane
* chrominance data:
* CbData = kpSrc + m_iMaxPicWidth * m_iMaxPicHeight;
* CrData = CbData + (m_iMaxPicWidth * m_iMaxPicHeight)/4;
* the application calling this interface needs to ensure the data validation between the location
* #param pBsInfo output bit stream
* #return 0 - success; otherwise -failed;
*/
virtual int EXTAPI EncodeFrame (const SSourcePicture* kpSrcPic, SFrameBSInfo* pBsInfo) = 0;
But apparently it only saves informations about the output. I'm just really confused about all of this.
Based on https://github.com/cisco/openh264/blob/master/codec/console/enc/src/welsenc.cpp
#include <codec_api.h>
#include <cassert>
#include <cstring>
#include <vector>
#include <fstream>
#include <iostream>
//Tested with OpenCV 3.3
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
using namespace std;
using namespace cv;
int main()
{
ISVCEncoder *encoder_ = nullptr;
int rv = WelsCreateSVCEncoder (&encoder_);
assert (0==rv);
assert (encoder_ != nullptr);
int width = 640;
int height = 480;
int total_num = 100;
SEncParamBase param;
memset (¶m, 0, sizeof (SEncParamBase));
param.iUsageType = CAMERA_VIDEO_REAL_TIME;
param.fMaxFrameRate = 30;
param.iPicWidth = width;
param.iPicHeight = height;
param.iTargetBitrate = 5000000;
encoder_->Initialize (¶m);
Mat image = imread("test.jpg", IMREAD_COLOR );
Mat imageResized, imageYuv, imageYuvMini;
resize(image, imageResized, Size(width, height));
Mat imageYuvCh[3], imageYuvMiniCh[3];
cvtColor(imageResized, imageYuv, cv::COLOR_BGR2YUV);
split(imageYuv, imageYuvCh);
resize(imageYuv, imageYuvMini, Size(width/2, height/2));
split(imageYuvMini, imageYuvMiniCh);
SFrameBSInfo info;
memset (&info, 0, sizeof (SFrameBSInfo));
SSourcePicture pic;
memset (&pic, 0, sizeof (SSourcePicture));
pic.iPicWidth = width;
pic.iPicHeight = height;
pic.iColorFormat = videoFormatI420;
pic.iStride[0] = imageYuvCh[0].step;
pic.iStride[1] = imageYuvMiniCh[1].step;
pic.iStride[2] = imageYuvMiniCh[2].step;
pic.pData[0] = imageYuvCh[0].data;
pic.pData[1] = imageYuvMiniCh[1].data;
pic.pData[2] = imageYuvMiniCh[2].data;
ofstream outFi;
outFi.open ("test.264", ios::out | ios::binary);
for(int num = 0; num<total_num; num++)
{
//prepare input data
rv = encoder_->EncodeFrame (&pic, &info);
assert (rv == cmResultSuccess);
if (info.eFrameType != videoFrameTypeSkip /*&& cbk != nullptr*/)
{
//output bitstream
for (int iLayer=0; iLayer < info.iLayerNum; iLayer++)
{
SLayerBSInfo* pLayerBsInfo = &info.sLayerInfo[iLayer];
int iLayerSize = 0;
int iNalIdx = pLayerBsInfo->iNalCount - 1;
do {
iLayerSize += pLayerBsInfo->pNalLengthInByte[iNalIdx];
--iNalIdx;
} while (iNalIdx >= 0);
unsigned char *outBuf = pLayerBsInfo->pBsBuf;
outFi.write((char *)outBuf, iLayerSize);
}
}
}
if (encoder_) {
encoder_->Uninitialize();
WelsDestroySVCEncoder (encoder_);
}
outFi.close();
}
Good Morning
I'm implementing a distributed image normalization algorithm an I'm using Boost::mpi with a class Pixel that contain the serialization code,
#ifndef PIXEL_H
#define PIXEL_H
#include <boost/mpi.hpp>
#include <boost/serialization/access.hpp>
class Pixel
{
private:
unsigned char m_red;
unsigned char m_green;
unsigned char m_blue;
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive &ar, const unsigned int version) {
ar & m_red;
ar & m_green;
ar & m_blue;
}
public:
Pixel();
Pixel(unsigned char red,unsigned char green,unsigned char blue) : m_red(red), m_green(green), m_blue(blue) {};
virtual ~Pixel();
unsigned char getRed();
void setRed(unsigned char val);
unsigned char getGreen();
void setGreen(unsigned char val);
unsigned char getBlue();
void setBlue(unsigned char val);
void setColor (unsigned char red,unsigned char green,unsigned char blue);
};
The main.cpp is
#include <iostream>
#include <boost/mpi.hpp>
#include <vector>
#include "include/Pixel.h"
#include <cstdlib>
#include <ctime>
#define ALTEZZA 2
#define LARGHEZZA 2
namespace mpi=boost::mpi;
int main(int argc, char * argv[]) {
std::cout<<"Inizializzazione dell'ambiente MPI"<<std::endl;
mpi::environment env;
mpi::communicator world;
Pixel **vettore;
int i,j;
//Inizializzazione della matrice di test
if(world.rank() == 0){
std::cout<<"Inizializzazione matrice di test..."<<std::endl;
std::srand(std::time(0));
vettore = new Pixel *[ALTEZZA];
for (i = 0; i < ALTEZZA; i++) {
vettore[i] = new Pixel[LARGHEZZA];
}
for (i = 0; i < ALTEZZA; i++) {
for (j = 0; j < LARGHEZZA; j++) {
vettore[i][j].setColor(std::rand() % 256, std::rand() % 256, std::rand() % 256);
std::cout<<"Vettore["<<i<<"]["<<j<<"] = ("<<int(vettore[i][j].getRed())<<","<<int(vettore[i][j].getGreen())<<","<<int(vettore[i][j].getBlue())<<");"<<std::endl;
}
}
}
if (world.rank() == 0) {
std::cout<<"Invio matrice.."<<std::endl;
world.send(1, 0, vettore[0]);
}else {
Pixel *px;
world.recv(0, 0, px);
for (j = 0; j < LARGHEZZA; j++) {
std::cout<<int(px[j].getRed())<<" "<<int(px[j].getGreen())<<" "<<int(px[j].getBlue())<<std::endl;
}
}
return 0;
}
but when i run the program the cout on the receiving process print wrong value like this
Inizializzazione dell'ambiente MPI
Inizializzazione dell'ambiente MPI
Inizializzazione matrice di test...
Vettore[0][0] = (170,103,165);
Vettore[0][1] = (84,0,186);
Vettore[1][0] = (93,228,162);
Vettore[1][1] = (31,100,204);
Invio matrice..
170 103 165
217 1 0
I think that the problem is the 2d array because if I use std::vector i haven't this problem but I don't understand why.
I would imagine you have several problems (I can't test as I don't have a capable MPI installation..)
Firstly, your send() is wrong, currently you are triggering the overload:
template<typename T> void send(int, int, const T &) const;
But you are trying to send a raw array, I imagine the fix here has to be to pass the count, for example:
world.send(1, 0, vettore[0], 2); // 2 Pixels
Secondly, on the receiver side (this I'm not sure about), but I imagine you need to have a suitable array to read the data into.., for example:
Pixel px[LARGHEZZA];
world.recv(0, 0, px, 2);
I think this should fix your problems...