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
}
}
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.)
The closest i've gotten is this:
void Engine::flipSurfaceVertically(SDL_Surface* surface)
{
SDL_LockSurface(surface);
Uint8* pixels = reinterpret_cast<Uint8*>(surface->pixels);
for (int k = 0; k < sizeof(Uint32); ++k)
{
for (int i = 0; i < surface->w; ++i)
{
for (int j = 0; j < surface->h / 2; ++j)
{
Uint32 currentPos = (j * surface->pitch) + (i * sizeof(Uint32)) + k;
Uint32 target = ((surface->h - j - 1) * surface->pitch) + (i * sizeof(Uint32)) + k;
Uint8 temp = pixels[target];
pixels[target] = pixels[currentPos];
pixels[currentPos] = temp;
}
}
}
SDL_UnlockSurface(surface);
}
But it doesn't keep the transparency. How can i go about actually achieving this?
I don't know where is the error exactly, I tried your code on my machine and it works well on the image I used. I suspect that your code indeed preserves transparency, but it is removed later in your implementation.
Anyway, if I may suggest an improvement for your code: you don't need such complicated operations to vertically flip a surface. The SDL_Surface structure stores the pixel data in row-major order, meaning that the pixels array is a sequence of rows, where each of these rows have a size of pitch bytes. Thus, to flip your surface vertically, you can simply iterate over the rows and swap them. The advantage of this method is that it does not require knowledge about pixel format, so it can be implemented for all image types (alpha channel or not), and it is pretty simple to implement.
Here is a minimal example that you can compile and experiment with:
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
void flip_surface(SDL_Surface* surface)
{
SDL_LockSurface(surface);
int pitch = surface->pitch; // row size
char* temp = new char[pitch]; // intermediate buffer
char* pixels = (char*) surface->pixels;
for(int i = 0; i < surface->h / 2; ++i) {
// get pointers to the two rows to swap
char* row1 = pixels + i * pitch;
char* row2 = pixels + (surface->h - i - 1) * pitch;
// swap rows
memcpy(temp, row1, pitch);
memcpy(row1, row2, pitch);
memcpy(row2, temp, pitch);
}
delete[] temp;
SDL_UnlockSurface(surface);
}
int main(int argc, char* argv[])
{
SDL_Init(SDL_INIT_VIDEO);
SDL_Surface* surface = IMG_Load("image.png");
flip_surface(surface);
IMG_SavePNG(surface, "result.png");
SDL_Quit();
return 0;
}
I have an image, as a cv::Mat. I am getting the raw data from this, with:
uchar* data = (uchar *)pImg.data;
I need to pass this data to a function, then cycle through each pixel of the image. I would have done:
for (int i = 0; i < image.rows; ++i)
{
for (int j = 0; j < image.cols; ++j)
{
//pixel = cv::Point(i,j);
}
}
What is the equivalent of this, using the uchar* data?
It is pretty easy but you need to remember one thing, this image.elemSize() indicates how many bytes there are per pixel (this function is taken from OpenCV mat). So this loop will look little bit different for different image formats. There is a example inside the loop
for (auto i = 0; i < image.rows * image.cols; i+=image.elemSize())
{
//for CV_8UC1
//auto pixel = *(image.data + i)
//for RGB as CV_8UC3
auto r = *(image.data + i)
auto g = *(image.data + i + 1)
auto b = *(image.data + i + 2)
}
The correct pixel value can be accessed from the raw data provided the following parameters are known:
X coordinate of pixel ( column number )
Y coordinate of pixel ( row number )
Image depth (actual data type of a single pixel i.e. uchar, ushort, float etc)
Number of channels of the image
Image step in bytes
Given the above information, the pixel can be accessed as follows (for CV_8UC3 type):
uchar* data = (uchar *)pImg.data;
for (int i = 0; i < image.rows; ++i)
{
for (int j = 0; j < image.cols; ++j)
{
uchar b = data[i * pImg.step + pImg.channels() * j + 0];
uchar g = data[i * pImg.step + pImg.channels() * j + 1];
uchar r = data[i * pImg.step + pImg.channels() * j + 2];
}
}
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.
I am actually working on a framework which implements a Path Tracer. I am having issues at understanding how the final image is written. The result is correct and the image looks nice (low number of samples):
but I have to understand how the code works since (according to me) something works is weird with the indices. This is the code in short:
struct Vec {
double x, y, z; // position, also color (r,g,b)
Vec(double x_ = 0, double y_ = 0, double z_ = 0){ x = x_; y = y_; z = z_; }
};
Vec *c = new Vec[width * height];
for (int y = 0; y<height; y++){// Loop over image rows
for (unsigned short x = 0; x<width; x++) { // Loop cols
Vec r = calculatePixelColor(x,y);
int i = (height - y - 1) * width + x;
c[i] = c[i] + r;
}
}
FILE *ff = fopen("image.ppm", "w"); // Write image to PPM file.
fprintf(ff, "P3\n%d %d\n%d\n", width, height, 255);
for (int y = 0; y < height; y++) for (int x = 0; x < width; x++){
Vec pixel = c[x + y * width];
int red = CLAMP((int)(sqrtf(pixel.x) * 255.0f), 0, 255);
int green = CLAMP((int)(sqrtf(pixel.y) * 255.0f), 0, 255);
int blue = CLAMP((int)(sqrtf(pixel.z) * 255.0f), 0, 255);
fprintf(ff, "%d %d %d ", (red), (green), (blue));
}
fclose(ff);
Now, we have a pointer to Vec named c which contains all the informations of the pixels. This info are stored according to the index i = (height - y - 1) * width + x; . It means that the Vec* c starts describing the image from the last row. So, the first Vec pointed by c is the pixel at the bottom-left corner of the image (if I am not wrong). Therefore, if I am right, this leads me asking: how does fprintf work? According to the documentation it just writes down the stream from the top to the bottom.. so in theory the image should be flipped. Where is the trick?