I have to load a 24 bit BMP file at a certain (x,y) index of glut window from a file using OpenGL. I have found a function that uses glaux library to do so. Here the color mentioned in ignoreColor is ignored during rendering.
void iShowBMP(int x, int y, char filename[], int ignoreColor)
{
AUX_RGBImageRec *TextureImage;
TextureImage = auxDIBImageLoad(filename);
int i,j,k;
int width = TextureImage->sizeX;
int height = TextureImage->sizeY;
int nPixels = width * height;
int *rgPixels = new int[nPixels];
for (i = 0, j=0; i < nPixels; i++, j += 3)
{
int rgb = 0;
for(int k = 2; k >= 0; k--)
{
rgb = ((rgb << 8) | TextureImage->data[j+k]);
}
rgPixels[i] = (rgb == ignoreColor) ? 0 : 255;
rgPixels[i] = ((rgPixels[i] << 24) | rgb);
}
glRasterPos2f(x, y);
glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_BYTE, rgPixels);
delete []rgPixels;
free(TextureImage->data);
free(TextureImage);
}
But the problem is that glaux is now obsolete. If I call this function, the image is rendered and shown for a minute, then an error pops up (without any error message) and the glut window disappears. From the returned value shown in the console, it seems like a runtime error.
Is there any alternative to this function that doesn't use glaux? I have seen cimg, devil etc but none of them seems to work like this iShowBMP function. I am doing my project in Codeblocks.
I have to load every frame to keep the implementation consistent with other parts of the program. Also, the bmp file whose name has been passed as a parameter to the function has both width and height in powers of 2.
The last two free() statements were not getting executed for some unknown reasons, so the memory consumption was increasing. That's why the program was crashing after a moment. Later I solved it using stb_image.h.
I have a bunch of "JPG" files that are different only for EXIF data.
I wanted to do a quick check (using the Qt Framework), trying to compute the Hash of the "core" image data (and not the file itself, which will include the metadata).
So far so good.
This is how I load an image and compute the Hash:
QImage img(R"(D:\Picture.jpg)");
auto data = QByteArray::fromRawData(reinterpret_cast<const char *>(img.constBits()), int(img.sizeInBytes()));
QCryptographicHash hash(QCryptographicHash::Sha256);
hash.addData(data);
qDebug() << hash.result().toHex();
I wanted to extend the same concept to files other than "JPG", so I've saved the original JPG file in different LOSSLESS formats (BMP, PNG, TIF), without altering the resolution.
I got a problem here. The Hash of the BMP, PNG, TIF images gives me the same result, but different from that of the same image in JPG.
If I would create a JPG file from a LOSSLES format I can understand the result.But the other way round???
Can someone help me understand where I'm wrong?
Give the following code I see that:
All QImage have the same bytes size
The QByteArray of the two JPG are identical
The QByteArray of the BMP, PNG, TIF are identical
The QByteArray of JPG and BMP (PNG and TIF too) are NOT identical
// JPG w/o EXIF data
QImage img1(R"(D:\Picture.jpg)");
auto data1 = QByteArray::fromRawData(reinterpret_cast<const char *>(img1.constBits()), int(img1.sizeInBytes()));
// JPG w/ EXIF data
QImage img2(R"(D:\Picture_EXIF.jpg)");
auto data2 = QByteArray::fromRawData(reinterpret_cast<const char *>(img2.constBits()), int(img2.sizeInBytes()));
// BMP
QImage img3(R"(D:\Picture.bmp)");
auto data3 = QByteArray::fromRawData(reinterpret_cast<const char *>(img3.constBits()), int(img3.sizeInBytes()));
// PNG w/o transparency
QImage img4(R"(D:\Picture.png)");
auto data4 = QByteArray::fromRawData(reinterpret_cast<const char *>(img4.constBits()), int(img4.sizeInBytes()));
// TIF (lossles)
QImage img5(R"(D:\Picture.tif)");
auto data5 = QByteArray::fromRawData(reinterpret_cast<const char *>(img5.constBits()), int(img5.sizeInBytes()));
qDebug() << img1.sizeInBytes(); // 23918592
qDebug() << img2.sizeInBytes(); // 23918592
qDebug() << img3.sizeInBytes(); // 23918592
qDebug() << img4.sizeInBytes(); // 23918592
qDebug() << img5.sizeInBytes(); // 23918592
qDebug() << (data1 == data2); // True
qDebug() << (data1 == data3); // False
qDebug() << (data3 == data4); // True
qDebug() << (data3 == data5); // True
qDebug() << img1.format(); // 4 = QImage::Format_RGB32
qDebug() << img2.format(); // 4 = QImage::Format_RGB32
qDebug() << img3.format(); // 4 = QImage::Format_RGB32
qDebug() << img4.format(); // 5 = QImage::Format_ARGB32
qDebug() << img5.format(); // 6 = QImage::Format_ARGB32_Premultiplied
QCryptographicHash hash(QCryptographicHash::Sha256);
hash.reset(); hash.addData(data1); qDebug() << hash.result().toHex(); // c37143639914056add1f90be4bfe780e14500d24f1d3484a087fc1943508157f
hash.reset(); hash.addData(data2); qDebug() << hash.result().toHex(); // c37143639914056add1f90be4bfe780e14500d24f1d3484a087fc1943508157f
hash.reset(); hash.addData(data3); qDebug() << hash.result().toHex(); // 0149c60b883df67ba002d791a1362dbd02ccab09241864341483a16ec0af635d
hash.reset(); hash.addData(data4); qDebug() << hash.result().toHex(); // 0149c60b883df67ba002d791a1362dbd02ccab09241864341483a16ec0af635d
hash.reset(); hash.addData(data5); qDebug() << hash.result().toHex(); // 0149c60b883df67ba002d791a1362dbd02ccab09241864341483a16ec0af635d
Final conclusion
As in subsequent tests I realized that the problem is neither Qt nor my (last) implementation of the code (thanks #Scheff).
The BMP, PNG and TIF are actually different from the original JPG file!
The files BMP, PNG and TIF were created by opening the original JPG file in Windows Paint and saving it in those lossless formats. So Windows Paint fails somehow in the reading (or) saving steps.
The commercial software Duplicate Cleaner fails as well because it reported the JPG file to be 100% identical to the BMP, PNG, TIF version.
Preface:
I consider
data1.append(reinterpret_cast<const char *>(img1.constBits()));
as wrong way to fill a QByteArray with data which doesn't store a C string.
QByteArray::append(const char*) is good to copy C strings in a QByteArray. It copies data until a 0 byte (a 0-terminator) is found. 0 bytes may be anywhere in the raw data of an image or nowhere. In the first case, too less data is copied, in the latter case, out-of-range data is considered. Both is unintended.
Btw. it's even not necessary to copy the image data (which might be of significant size).
I made a sample to compare the raw data of two QImages directly, including a pre-check whether size and format does match.
My sample testQImageRawCmp.cc
#include <QtWidgets>
bool equalImgData(const QImage &qImg1, const QImage &qImg2, int eps = 0)
{
// test properties
#define TEST_PROP(PROP) \
do if (qImg1.PROP != qImg2.PROP) { \
qDebug() << "qImg1."#PROP" != qImg2."#PROP; \
return false; \
} while(false)
TEST_PROP(width());
TEST_PROP(height());
TEST_PROP(format());
#undef TEST_PROP
// test raw data
const uchar *const data1 = qImg1.bits();
const uchar *const data2 = qImg2.bits();
const int bytesPerLine1 = qImg1.bytesPerLine();
const int bytesPerLine2 = qImg2.bytesPerLine();
const int nBits = qImg1.depth() * qImg1.width();
const int nBytes = (nBits + 7) / 8;
assert(nBytes <= bytesPerLine1);
assert(nBytes <= bytesPerLine2);
for (int y = 0; y < qImg1.height(); ++y) {
const uchar *row1 = data1 + y * bytesPerLine1;
const uchar *row2 = data2 + y * bytesPerLine2;
for (int x = 0; x < nBytes; ++x) {
if (abs(row2[x] - row1[x]) > eps) {
qDebug() << "Row" << y << "byte" << x << "difference:" << (row2[x] - row1[x]);
return false;
}
}
}
return true;
}
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
// load sample data
QImage img1("Picture.jpg");
QImage img2("Picture.bmp");
#if 0 // U.B.
// Juttas test:
QByteArray data1; data1.append(reinterpret_cast<const char *>(img1.constBits()));
QByteArray data2; data2.append(reinterpret_cast<const char *>(img2.constBits()));
#endif // 0
// My test:
if (!equalImgData(img1, img2, 3)) {
qDebug() << "Images not equal!";
} else {
qDebug() << "Images equal.";
}
}
I tested this program with the sample data provided by OP:
Picture.jpg
Picture.bmp
and got the following output:
Qt Version: 5.13.0
Row 0 byte 60 difference: 1
Images not equal!
I must admit, the first version of this code just reported the unequality.
Then, I tried to make my own counter-sample and converted the Picture.bmp to Picture.bmp.jpg in GIMP (with 100 % quality setting which I would've considered as loss-less). This resulted in a difference in Row 0 byte 0. Ooops!
Then, I became curious and modified the code to see how much different the images are.
A difference of 1 in a red, green, or blue value of a pixel is not much. I doubt that this is even visible for the average human.
Hence, I modified the code (into the exposed version) to tolerate some kind of difference.
With an eps of 3:
if (equalImgData(img1, img2, 3)) {
the images were considered as equal.
I'm interested in creating a single .ico file (with transparency) from a list of QPixmap images (with sizes 16x16, 32x32, 48x48...). I haven't seen any related method in Qt's documentation: QPixmap, QImage, QIcon (which is for storing images for UI states, not related to the file format)...
Does Qt has such functionality? How can I save such file? May be mixing with Windows API?
PS: A low-level solution would be to directly write the .ico file, but I'm more interested in not reinventing the wheel if possible.
It seems that there is no built in support in Qt for writing ICO files, so here I'm publishing a code snippet to generate one from a list of pixmaps. Hope it may be useful for somebody else.
template<typename T>
void write(QFile& f, const T t)
{
f.write((const char*)&t, sizeof(t));
}
bool savePixmapsToICO(const QList<QPixmap>& pixmaps, const QString& path)
{
static_assert(sizeof(short) == 2, "short int is not 2 bytes");
static_assert(sizeof(int) == 4, "int is not 4 bytes");
QFile f(path);
if (!f.open(QFile::OpenModeFlag::WriteOnly)) return false;
// Header
write<short>(f, 0);
write<short>(f, 1);
write<short>(f, pixmaps.count());
// Compute size of individual images
QList<int> images_size;
for (int ii = 0; ii < pixmaps.count(); ++ii) {
QTemporaryFile temp;
temp.setAutoRemove(true);
if (!temp.open()) return false;
const auto& pixmap = pixmaps[ii];
pixmap.save(&temp, "PNG");
temp.close();
images_size.push_back(QFileInfo(temp).size());
}
// Images directory
constexpr unsigned int entry_size = sizeof(char) + sizeof(char) + sizeof(char) + sizeof(char) + sizeof(short) + sizeof(short) + sizeof(unsigned int) + sizeof(unsigned int);
static_assert(entry_size == 16, "wrong entry size");
unsigned int offset = 3 * sizeof(short) + pixmaps.count() * entry_size;
for (int ii = 0; ii < pixmaps.count(); ++ii) {
const auto& pixmap = pixmaps[ii];
if (pixmap.width() > 256 || pixmap.height() > 256) continue;
write<char>(f, pixmap.width() == 256 ? 0 : pixmap.width());
write<char>(f, pixmap.height() == 256 ? 0 : pixmap.height());
write<char>(f, 0); // palette size
write<char>(f, 0); // reserved
write<short>(f, 1); // color planes
write<short>(f, pixmap.depth()); // bits-per-pixel
write<unsigned int>(f, images_size[ii]); // size of image in bytes
write<unsigned int>(f, offset); // offset
offset += images_size[ii];
}
for (int ii = 0; ii < pixmaps.count(); ++ii) {
const auto& pixmap = pixmaps[ii];
if (pixmap.width() > 256 || pixmap.height() > 256) continue;
pixmap.save(&f, "PNG");
}
return true;
}
Code also available in GitHub.
So i found this link regarding my question, but it is for c#
Create a PNG from an array of bytes
I have a variable int array of numbers.
i will call it "pix[ ]"
for now it can be any size from 3 to 256, later possibly bigger.
What i want to do now, is to convert it into a pixel image.
I am still a noobin c++ so pleas excuse me.
I tried to download some libaries that make working with libpng easier, but they do not seem to be working (ubuntu, code::blocks)
So i have questions in the following:
1) how do you create a new bitmap (which libaries, which command)?
2) how do i fill it with information from "pix[ ]" ?
3) how do i save it?
if it is a repost of a question i am happy about a link also ;)
Here is what i worked out so far, thanks for your help.
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);
}
i have not fully understood what it does. i can open the output image, but it is not a correct representation of the Array.
If i change things around, for example making a 2 dimensional array, then the image viewer tells me "expected an integer" and doesn't show me an image.
So far so good.
As i have the array before the image i created a function aufrunden to round up to the next int number because i want to create a square image.
int aufrunden (double h)
{
int i =h;
if (h-i == 0)
{
return i;
}
else
{
i = h+1;
return i;
}
}
This function is used in the creation of the image.
If the image is bigger than the information the array provides like this (a is the length of th array)
double h;
h= sqrt(a/3.0);
int i = aufrunden(h);
FILE *imageFile;
int height=i,width=i;
It might happen now, that the array is a=24 long. aufrunden makes the image 3x3 so it has 27 values...meaning it is missing the values for 1 pixel.
Or worse it is only a=23 long. also creating a 3x3 image.
What will fwrite(pix,1,18,imageFile); write in those pixels for information? It would be best if the remaing values are just 0.
*edit never mind, i will just add 0 to the end of the array until it is filling up the whole square...sorry
Consider using a Netpbm format (pbm, pgm, or ppm).
These images are extremely simple text files that you can write without any special libraries. Then use some third-party software such as ImageMagick, GraphicsMagick, or pnmtopng to convert your image to PNG format. Here is a wiki article describing the Netpbm format.
Here's a simple PPM image:
P3 2 3 255
0 0 0 255 255 255
255 0 0 0 255 255
100 100 100 200 200 200
The first line contains "P3" (the "magic number identifying it as a text-PPM), 2 (width), 3 (height), 255 (maximum intensity).
The second line contains the two RGB pixels for the top row.
The third and fourth lines each contain the two RGB pixels for rows 2 and 3.
Use a larger number for maximum intensity (e.g. 1024) if you need a larger range of intensities, up to 65535.
Edited by Mark Setchell beyond this point - so I am the guilty party!
The image looks like this (when the six pixels are enlarged):
The ImageMagick command to convert, and enlarge, is like this:
convert image.ppm -scale 400x result.png
If ImageMagick is a bit heavyweight, or difficult to install you can more simply use the NetPBM tools (from here) like this (it's a single precompiled binary)
pnmtopng image.ppm > result.png
If, as it seems, you have got Magick++ and are happy to use that, you can write your code in C/C++ like this:
////////////////////////////////////////////////////////////////////////////////
// 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");
}
You are not far off - well done for trying! As far as I can see, you only had a couple of mistakes:
You had P3 where you would actually need P6 if writing in binary.
You were using int type for your data, whereas you need to be using unsigned char for 8-bit data.
You had the width and height interchanged.
You were using the PGM extension which is for Portable Grey Maps, whereas your data is colour, so you need to use the PPM extension which is for Portable Pix Map.
So, the working code looks like this:
#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);
}
If you then run that, you can convert the resulting image to a nice big PNG with
convert image.ppm -scale 400x result.png
If you subsequently need 16-bit data, you would change the 255 to 65535, and store in an unsigned short array rather than unsigned char and when you come to the fwrite(), you would need to write double the number of bytes.
The code below will take an integer array of pixel colors as input and write it to a .bmp bitmap file or, in reverse, read a .bmp bitmap file and store its image contents as an int array. It only requires the <fstream> library. The input parameter path can be for example C:/path/to/your/image.bmp and data is formatted as data[x+y*width]=(red<<16)|(green<<8)|blue;, whereby red, green and blue are integers in the range 0-255 and the pixel position is (x,y).
#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 int 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;
}
The code snippet was inspired by https://stackoverflow.com/a/47785639/9178992
For .png images, use lodepng.cpp and lodepng.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;
}
I want to get the RGB value of the top/left pixel (0;0) of the whole x11 display.
what I've got so far:
XColor c;
Display *d = XOpenDisplay((char *) NULL);
XImage *image;
image = XGetImage (d, RootWindow (d, DefaultScreen (d)), x, y, 1, 1, AllPlanes, XYPixmap);
c->pixel = XGetPixel (image, 0, 0);
XFree (image);
XQueryColor (d, DefaultColormap(d, DefaultScreen (d)), c);
cout << c.red << " " << c.green << " " << c.blue << "\n";
but I need those values to be 0..255 or (0.00)..(1.00), while they look like 0..57825, which is no format I recognize.
also, copying the whole screen just to get one pixel is very slow. as this will be used in a speed-critical environment, I'd appreciate if someone knows a more performant way to do this. Maybe using XGetSubImage of a 1x1 size, but I'm very bad at x11 development and don't know how to implement that.
what shall I do?
I took your code and got it to compile. The values printed (scaled to 0-255) give me the same values as I set to the desktop background image.
#include <iostream>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
using namespace std;
int main(int, char**)
{
XColor c;
Display *d = XOpenDisplay((char *) NULL);
int x=0; // Pixel x
int y=0; // Pixel y
XImage *image;
image = XGetImage (d, XRootWindow (d, XDefaultScreen (d)), x, y, 1, 1, AllPlanes, XYPixmap);
c.pixel = XGetPixel (image, 0, 0);
XFree (image);
XQueryColor (d, XDefaultColormap(d, XDefaultScreen (d)), &c);
cout << c.red/256 << " " << c.green/256 << " " << c.blue/256 << "\n";
return 0;
}
From the XColor(3) man page:
The red, green, and blue values are always in the range 0 to 65535 inclusive, independent of the number of bits actually used in the display hardware. The server scales these values down to the range used by the hardware. Black is represented by (0,0,0), and white is represented by (65535,65535,65535). In some functions, the flags member controls which of the red, green, and blue members is used and can be the inclusive OR of zero or more of DoRed, DoGreen, and DoBlue.
So you must scale these values to whatever range you want.