I am using stbi_image library to load images. I have a JPG that's loaded into unsigned char* with given width and height (and # components = 3 and each has 8 bits). To reconstruct the height value, I'm guessing I have to reassemble each components into their places:
height_map_data = stbi_load(heightmap_path, &width, &height, &comp, 1);
if(height_map_data != nullptr)
{
unsigned char* current_head = height_map_data;
unsigned int r, g, b, a;
for(int i = 0; i < height; ++i)
{
for(int j = 0; j < width; ++j)
{
//???? get height ????
//assume comp == 3, img is in rgb, 8bpp
unsigned int pix = current_head[0] << 16 | current_head[1] << 8 | current_head[2];
float h = pix / (256.f * 256 * 256); //is this right??
current_head += comp;
}
}
}
this is the jpg image:
So how do you correctly unpack the height from the image? This image has different RGB numbers so I'm guessing it's not an 8bit grayscale and so I don't think I can just use one of the components.
Related
I wrote a program in c++ to draw the pixel of bmp image into the console using SetPixel windows function, but after loading the pixel array into the array the image got printed on the console with gaps between the pixels. Thanks in advance for your help!
This is the output of the printed image on the console.
This is the original Image I provided to it.
As you can see here the image width also changes after the print on the console.
// bmp bitmap
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
using namespace std;
#pragma pack(1)
struct BitmapFileHeader {
unsigned short type;
unsigned int size;
unsigned short reserved1;
unsigned short reserved2;
unsigned int offset;
};
#pragma pack(0)
unsigned char grayScale(unsigned char r, unsigned char g, unsigned char b) {
return ((r + g + b) / 3);
}
int main() {
char *data;
FILE *filePointer;
int **ImageArray;
BitmapFileHeader *bmp = (struct BitmapFileHeader*)malloc(sizeof(struct BitmapFileHeader));
BITMAPINFOHEADER *BitmapInfoHeader = (BITMAPINFOHEADER*)malloc(sizeof(BITMAPINFOHEADER));
HWND console = GetConsoleWindow();
HDC context = ::GetDC(console) ;
filePointer = fopen("tom.bmp", "rb");
if(!filePointer) {
perror("");
}
fread(reinterpret_cast<BitmapFileHeader*>(bmp), sizeof(BitmapFileHeader), 1, filePointer);
fread(reinterpret_cast<BITMAPINFOHEADER*>(BitmapInfoHeader), sizeof(BITMAPINFOHEADER), 1, filePointer);
if(BitmapInfoHeader->biSize == 40 && BitmapInfoHeader->biCompression == BI_BITFIELDS) {
printf("This types of image uses Extra bit masks\n");
}
// row pading
int RowSize = ((BitmapInfoHeader->biBitCount * BitmapInfoHeader->biWidth + 31) / 32) * 4;
int PixelArraySize = RowSize * BitmapInfoHeader->biHeight;
int height = BitmapInfoHeader->biHeight * 5;
int width = BitmapInfoHeader->biWidth * 5;
printf("RowSize: %d PixelArraySize: %d\n", RowSize, PixelArraySize);
ImageArray = (int**)malloc(sizeof(int*)*height);
// memory allocation
for(int i = 0; i < height; i++)
ImageArray[i] = (int*)malloc(sizeof(int)*width);
data = (char*)malloc(PixelArraySize);
fseek(filePointer, bmp->offset, SEEK_SET);
// set image into array
for(int ii = 0; ii < height; ii+=3) {
fread(data, RowSize, 3, filePointer);
for(int jj = 0; jj < width; jj+=3) {
ImageArray[ii][jj] = grayScale(data[jj+2], data[jj+1], data[jj]);
SetPixel(context, -jj+1000, -ii+500, RGB(data[jj+2], data[jj+1], data[jj]));
}
}
fclose(filePointer);
return 0;
}
here is the code, which I wrote.
A pixel is described by three bytes, one for each RGB channel. You are dealing with two indices here: The index of the pixel in the row data and the position of the pixel in width direction. You place the pixel and access the row data with the same index.
So:
for (int jj = 0; jj < width; jj++) { // jj: position
int kk = 3 * jj; // kk: data index
ImageArray[ii][jj] = grayScale(data[kk + 2], data[kk + 1], data[kk]);
SetPixel(context, -jj + 1000, -ii + 500, RGB(data[kk + 2], data[kk + 1], data[kk]));
}
The vertical gaps, i.e. the blank lines, come from incrementing by 3, where you should just increment by 1. (You have no "data index" here, because you read your data row-wide for the current row ii.)
If you want to enlarge your image, as the multiplication of width and height by 5 suggests, you must add a third index: You now have two positions, the source and target positions. This will be easier if you separate your loops: Create ImageArray of the source image in a first nested loop, then draw your scaled target image to the console with a loop over the target oordinates:
int scale = 5;
int ww = scale * w;
int hh = scale * h;
// read ImageArray
for (int y = 0; y < h; y++) {
fread(data, RowSize, 3, filePointer);
for (int x = 0; x < w; x++) {
ImageArray[y][x] = ...;
SetPixel(context, -jj+1000, -ii+500, RGB(data[jj+2], data[jj+1], data[jj]));
}
}
for (int yy = 0; yy < hh; yy++) {
fread(data, RowSize, 3, filePointer);
for (int xx = 0; xx < ww; xx++) {
int x = xx / scale;
int y = yy / scale;
SetPixel(context, yy, xx, ImageArray[y][x]);
}
}
(Here, single letters re source values, double leters are target values.)
I'm reading an .img Gray Scale Image (512 byte header, 512 x 512 pixels x 8 bits per pixel).
There I'm determining a threshold to turn the Gray-Scale image into a binary image (black/white).
But when I assign 0 to the pixels, it turns out to be a light blue (#01fffd)?!?! When I use the value 2 it's almost black (#020100).
Can you explain me how this comes?
I read the image like this:
int width = 512;
int height = 512;
File* imageFilePointerIn = fopen("image.img", "r+b");
File* binaryImageFilePointerOut = fopen("imageOut.img",
"w+b");
unsigned char* header = new unsigned char[512];
unsigned char** imageData = new unsigned char*[width];
unsigned char** binaryImageArray = new unsigned char*[width];
fread(header, sizeof(char), 512, imageFilePointerIn);
for (int i = 0; i < width; i++) {
data[i] = new unsigned char[height];
fread(data[i], sizeof(char), height, imageFilePointerIn);
}
for (int i = 0; i < width; i++) {
binaryImageArray[i] = new unsigned char[height];
}
char unsigned thresholdBinaryImage = 128;
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
if (thresholdBinaryImage <= data[i][j]) {
binaryImageArray[i][j] = 255;
} else {
binaryImageArray[i][j] = 0;
}
}
}
fwrite(header, sizeof(char), headerSize, binaryImageFilePointerOut);
for (int i = 0; i < width; i++) {
fwrite(binaryImageArray[i], sizeof(char), height,
binaryImageFilePointerOut);
}
fclose(binaryImageFilePointerOut);
fclose(imageFilePointerIn);
I have no idea why colors could be displayed at all?
For displaying I transfer the .img file into a .ras file with a code snipped from the internet. But I don't think that the error could be there since I saw others using the same code for transforming .img Grayscales into .ras just for displaying without having colors.
Would be nice to get any suggestion :).
I cannot seem to get libpng to dump its data into my struct. I can't figure out what I'm doing wrong. I'm trying to flip the bytes because PNG's are stored top->down and I need the data bottom->up.
First my struct looks like:
typedef union RGB
{
uint32_t Color;
struct
{
unsigned char B, G, R, A;
} RGBA;
} *PRGB;
Then I created a vector as such:
png_init_io(PngPointer, hFile);
png_set_sig_bytes(PngPointer, 8);
png_read_info(PngPointer, InfoPointer);
uint32_t width, height;
int bitdepth, colortype, interlacetype, channels;
png_set_strip_16(PngPointer);
channels = png_get_channels(PngPointer, InfoPointer);
png_get_IHDR(PngPointer, InfoPointer, &width, &height, &bitdepth, &colortype, &interlacetype, nullptr, nullptr);
uint32_t RowBytes = png_get_rowbytes(PngPointer, InfoPointer);
unsigned char** RowPointers = png_get_rows(PngPointer, InfoPointer);
std::vector<RGB> Pixels(RowBytes * height); //Amount of bytes in one row * height of image.
//Crashes in the for loop below :S
for (int I = 0; I < height; I++)
{
for (int J = 0; J < width; J++)
{
Pixels[(height - 1 - I) * width + J].RGBA.B = *(RowPointers[J]++);
Pixels[(height - 1 - I) * width + J].RGBA.G = *(RowPointers[J]++);
Pixels[(height - 1 - I) * width + J].RGBA.R = *(RowPointers[J]++);
}
}
std::fclose(hFile);
png_destroy_read_struct(&PngPointer, &InfoPointer, nullptr);
What did I do wrong? How can I get the pixels of the PNG and store them upside down? I used the same technique for bitmaps but PNG just isn't working :l
Shouldn't this:
Pixels[(height - 1 - I) * width + J].RGBA.B = *(RowPointers[J]++);
Index RowPointers via I instead?
I want to convert a yuv picture into RGB to display on a QT pixmap. What I have right now is that I see a empty pixmap.
int convertYUVtoRGB(int y, int u, int v) {
int r,g,b;
r = y + (int)(1.402f*v);
g = y - (int)(0.344f*u +0.714f*v);
b = y + (int)(1.772f*u);
r = r>255? 255 : r<0 ? 0 : r;
g = g>255? 255 : g<0 ? 0 : g;
b = b>255? 255 : b<0 ? 0 : b;
return 0xff000000 | (b<<16) | (g<<8) | r;
}
unsigned char * convertYUV420_NV21toRGB8888(unsigned char data[78080], int width, int height) {
int size = width*height;
int offset = size;
unsigned char * pixels = new unsigned char[size];
int u, v, y1, y2, y3, y4;
// i percorre os Y and the final pixels
// k percorre os pixles U e V
for(int i=0, k=0; i < size; i+=2, k+=2) {
y1 = data[i ]&0xff;
y2 = data[i+1]&0xff;
y3 = data[width+i ]&0xff;
y4 = data[width+i+1]&0xff;
u = data[offset+k ]&0xff;
v = data[offset+k+1]&0xff;
u = u-128;
v = v-128;
pixels[i ] = convertYUVtoRGB(y1, u, v);
pixels[i+1] = convertYUVtoRGB(y2, u, v);
pixels[width+i ] = convertYUVtoRGB(y3, u, v);
pixels[width+i+1] = convertYUVtoRGB(y4, u, v);
if (i!=0 && (i+2)%width==0)
i+=width;
}
return pixels;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
unsigned char * buffer;
unsigned char * image = NULL;
QPixmap pixmap;
QImage img(320, 122, QImage::Format_ARGB32_Premultiplied);
img.fill(QColor(Qt::white).rgb());
ifstream is;
is.open ("bunny.yuv", ios::binary );
// is.seekg (0, ios::end);
// length = is.tellg();
// is.seekg (0, ios::beg);
buffer = new unsigned char[78081];
is.read((char * )buffer,78080);
is.close();
/*
for (int x = 0; x < 10; ++x) {
for (int y = 0; y < 10; ++y) {
img.setPixel(x, y, qRgb(0, 0, 0));
}
}
*/
for(int i = 0; i < 78080; i++)
qDebug() << buffer[i]<< endl;
image = convertYUV420_NV21toRGB8888(buffer,320,122);
QByteArray byteImage((const char *)image);
pixmap.loadFromData(byteImage);
QLabel myLabel;
myLabel.setPixmap(pixmap);
myLabel.setGeometry(20,100,320,122);
myLabel.show();
return a.exec();
}
EDIT: I've added the right format and parenthesses. Still the same problem.
Kind regards,
And when you have done the calculation in Jason's post - the best format for the pixmap is QImage::Format_ARGB32_Premultiplied - the format is Blue byte Green byte Red byte 0xff
This won't do what you think it does:
r = y + (int)1.402f*v;
g = y - (int)(0.344f*u +0.714f*v);
b = y + (int)1.772f*u;
As you've missed parentheses and so cast your float constants to ints before the calculation.
And then having converted your YUV data into RGB packed into an int, you store that int into a char...
So:
unsigned char * pixels = new unsigned char[size];
Should probably be:
unsigned int* pixels = new unsigned int[size];
And obviously that affects the return type of the function too. You might also want to change the type of your YUV -> RGB function to unsigned as well, just for consistency.
In convertYUV420_NV21toRGB8888, pixels is an unsigned char array but you're trying to assign a int to it. All you will get is the low byte, and most of the bytes in your output will remain unset. You need to completely rethink the way you're writing the output.
I create a c# function that moves BitmapFrame picture to byte[] (using copypixels). Then I paste this buffer into c++ dll where it is uint8*. There is a structure in cpp
typedef struct
{
float r;
float g;
float b;
} pixel;
Is it possible to organize a loop for this uint8* buffer to get pixel-by pixel (for example by x y - height and width of image(this data I have too))?
something like
for(i=0; i< height;i++)
{
for(j=0; j <width;j++)
{
SomeWorkWithPixelsfromUint8(i,j) //???
}
}
Where SomeWorkWithPixelsfromUint8(i,j) could operate RGB structure
So simple uint8 -> getPixel(x,y) ????
Assuming that your picture data have a layout like this
Pixels are scanlines per scanlines, left to right
One pixel is packed as RGB, or eventually RGBA
You use pixelSize bytes per pixel (might be 3, might be 4 if you alpha channel)
uint8_t* picData = ...;
uint8_t* pixel = picData;
for(int i = 0; i < height; ++i) {
for(int j = 0; j < width; ++j, pixel += pixelSize) {
float r = pixel[0];
float g = pixel[1];
float b = pixel[2];
// Do something with r, g, b
}
}