Saving part of screen to file (SOIL and glReadPixels) - c++

I'm trying to save an image of size 5x5 pixels, read with glReadPixels into a file using SOIL.
I read the pixels:
int x = 400;
int y = 300;
std::vector< unsigned char* > rgbdata(4*5*5);
glReadPixels(x, y, 5, 5,GL_RGBA,GL_UNSIGNED_BYTE, &rgbdata[0]);
Then I try saving the read data with SOIL's save image function
int save_result = SOIL_save_image
(
"image_patch.bmp",
SOIL_SAVE_TYPE_BMP,
5, 5, 4,
rgbdata[0]
);
But when trying to save the image, I get an unhandled exception.
Solution (by Christian Rau)
int x = 400;
int y = 300;
std::vector< unsigned char > rgbdata(4*5*5);
glReadPixels(x-(5/2), y-(5/2), 5, 5,GL_RGBA,GL_UNSIGNED_BYTE, &rgbdata[0]);
int save_result = SOIL_save_image
(
"image_patch.bmp",
SOIL_SAVE_TYPE_BMP,
5, 5, 4,
rgbdata.data()
);

You are creating a vector of pointers to unsigned char (std::vector<unsigned char*>, but what you want is just a vector to unsigned char (std::vector<unsigned char>).
And in the call to SOIL_save_image you don't have to give it rgbdata[0], which would be a single unsigned char (and with your incorrect vector type an uninitialized pointer, likely resulting in some memory access error), but a pointer to the complete data and thus rgbdata.data() (or &rgbdata[0] if you don't have C++11).

Also notice :
GL's default pack/unpack image width should be multiples of 4, that is to say, width in glReadPixels(x, y, width, height, format, type, data) should meet the condition width % 4 == 0.
If width % 4 != 0(in your case 5 % 4 != 0), it may lead to unexpected results. So you also have to avoid these problems and here is the solution:
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
glPixelStorei(GL_PACK_ALIGNMENT,1);

Related

Why do bitmap images not divisible by four and larger than 32 pixels in width cause erroneous line scanning when creating an array of the pixel data?

I found a tutorial by Alejandro Rodriguez about how to create a simple bitmap reader. It uses fseek and fread to read through the header sections of a 24bit bitmap, find the pertinent information such as width, height, and dataoffset, and scan the data section of a bitmap. https://elcharolin.wordpress.com/2018/11/28/read-and-write-bmp-files-in-c-c/
It works, and it's lightweight, but begins to fail when bitmap image sizes exceed 32 pixels && contain more than 1 row && the width is not divisible by 4. Bitmaps are padded to be divisible to 4 bytes for storage efficiency. When the width is divisible by four the problem does not exist, however... If I give the function a bitmap that contains for example an image of 42 pixels in width, and 2 pixels in height, the first scan line reads correctly, but sequential rows begin to pick up unwanted garbage padding that should be skipped.
An image of 42 pixels would have 2 pixels worth of padding at the end of each row. This function works with 24-bit depth pixels, meaning 3 bytes per pixel, 6 bytes total padding for this example case.
The information parsed from the headers of the imageFile seems to be consistently correct. I've double-checked that the calculated value of paddedRowSize is correct, and it always shows the correct theoretically calculated value. I've been chewing on this problem for a couple of weeks now, and can't figure it out. I'm hoping someone could shed some insight into what's going wrong...
In the for loop that iterates through the file using fseek and fread to scan the data section of the imageFile, there must be an issue causing some of the padded bytes to be picked up when being read into the currentRowPointer. Why is this happening? Please shed your sagely wisdom.
Here is the function in question, along with particulars:
#include <stdio.h>
#include <stdlib.h>
#define DATA_OFFSET_OFFSET 0x000A
#define WIDTH_OFFSET 0x0012
#define HEIGHT_OFFSET 0x0016
#define BITS_PER_PIXEL_OFFSET 0x001C
typedef unsigned int int32;
typedef short int16;
typedef unsigned char byte;
void ReadImage(const char *fileName,byte **pixels, int32 *width, int32 *height, int32 *bytesPerPixel)
{
FILE *imageFile = fopen(fileName, "rb");
int32 dataOffset;
fseek(imageFile, DATA_OFFSET_OFFSET, SEEK_SET);
fread(&dataOffset, 4, 1, imageFile);
fseek(imageFile, WIDTH_OFFSET, SEEK_SET);
fread(width, 4, 1, imageFile);
fseek(imageFile, HEIGHT_OFFSET, SEEK_SET);
fread(height, 4, 1, imageFile);
int16 bitsPerPixel;
fseek(imageFile, BITS_PER_PIXEL_OFFSET, SEEK_SET);
fread(&bitsPerPixel, 2, 1, imageFile);
*bytesPerPixel = ((int32)bitsPerPixel) / 8;
// This is the line of line of code causing my issuesint paddedRowSize =
//4 * (((*width) + 3) / 4) * (*bytesPerPixel);
//Correct line of code that makes it all work:
int paddedRowSize = (((*width) * (*bytesPerPixel) + 3) / 4) * 4;
int unpaddedRowSize = (*width)*(*bytesPerPixel);
int totalSize = unpaddedRowSize*(*height);
*pixels = (byte*)malloc(totalSize);
int i = 0;
byte *currentRowPointer = *pixels+((*height-1)*unpaddedRowSize);
for (i = 0; i < *height; i++)
{
fseek(imageFile, dataOffset+(i*paddedRowSize), SEEK_SET);
fread(currentRowPointer, 1, unpaddedRowSize, imageFile);
currentRowPointer -= unpaddedRowSize;
}
fclose(imageFile);
}
int main() {
uint32_t width;
uint32_t height;
uint32_t bytesPerPixel;
byte *pixels;
ReadImage("please_work.bmp", &pixels, &width, &height, &bytesPerPixel);
}

C++AMP Computing gradient using texture on a 16 bit image

I am working with depth images retrieved from kinect which are 16 bits. I found some difficulties on making my own filters due to the index or the size of the images.
I am working with Textures because allows to work with any bit size of images.
So, I am trying to compute an easy gradient to understand what is wrong or why it doesn't work as I expected.
You can see that there is something wrong when I use y dir.
For x:
For y:
That's my code:
typedef concurrency::graphics::texture<unsigned int, 2> TextureData;
typedef concurrency::graphics::texture_view<unsigned int, 2> Texture
cv::Mat image = cv::imread("Depth247.tiff", CV_LOAD_IMAGE_ANYDEPTH);
//just a copy from another image
cv::Mat image2(image.clone() );
concurrency::extent<2> imageSize(640, 480);
int bits = 16;
const unsigned int nBytes = imageSize.size() * 2; // 614400
{
uchar* data = image.data;
// Result data
TextureData texDataD(imageSize, bits);
Texture texR(texDataD);
parallel_for_each(
imageSize,
[=](concurrency::index<2> idx) restrict(amp)
{
int x = idx[0];
int y = idx[1];
// 65535 is the maxium value that can take a pixel with 16 bits (2^16 - 1)
int valX = (x / (float)imageSize[0]) * 65535;
int valY = (y / (float)imageSize[1]) * 65535;
texR.set(idx, valX);
});
//concurrency::graphics::copy(texR, image2.data, imageSize.size() *(bits / 8u));
concurrency::graphics::copy_async(texR, image2.data, imageSize.size() *(bits) );
cv::imshow("result", image2);
cv::waitKey(50);
}
Any help will be very appreciated.
Your indexes are swapped in two places.
int x = idx[0];
int y = idx[1];
Remember that C++AMP uses row-major indices for arrays. Thus idx[0] refers to row, y axis. This is why the picture you have for "For x" looks like what I would expect for texR.set(idx, valY).
Similarly the extent of image is also using swapped values.
int valX = (x / (float)imageSize[0]) * 65535;
int valY = (y / (float)imageSize[1]) * 65535;
Here imageSize[0] refers to the number of columns (the y value) not the number of rows.
I'm not familiar with OpenCV but I'm assuming that it also uses a row major format for cv::Mat. It might invert the y axis with 0, 0 top-left not bottom-left. The Kinect data may do similar things but again, it's row major.
There may be other places in your code that have the same issue but I think if you double check how you are using index and extent you should be able to fix this.

accessing image pixels as float array

I want to access image pixels as float array in opencv. Ive done the following:
Mat input = imread("Lena.jpg",CV_LOAD_IMAGE_GRAYSCALE);
int height = input.rows;
int width = input.cols;
Mat out;
input.convertTo(input, CV_32FC1);
copyMakeBorder(input, input, 3, 3, 3, 3, 0);
out = Mat(height, width, input.type());
float *outdata = (float*)out.data;
float *indata = (float*)input.data;
for(int j = 0; j < height; j++){
for(int i =0; i < width; i++){
outdata[j*width + i] = indata[(j* width + i)];
}
}
normalize(out, out,0,255,NORM_MINMAX,CV_8UC1);
imshow("output", out);
waitKey();
This should return the original image in "out", however, I'm getting some weird image. Can anyone explain whats wrong with the code. I think i need to use some step size (widthStep). Thanks.
the line
copyMakeBorder(input, input, 3, 3, 3, 3, 0);
changes the dimensions of input, it adds 6 rows and 6 columns to the image. That means your height and width variables are holding the wrong values when you define out and try to loop over the values on input.
if you change the order to
copyMakeBorder(input, input, 3, 3, 3, 3, 0);
int height = input.rows;
int width = input.cols;
it should work fine.
Some ideas:
Something like outdata[j*width + i] is a more standard pattern for this sort of thing.
According to the opencv documentation, there is a templated Mat::at(int y, int x) method that allows you to access individual elements of a matrix.
float f = input.at<float>(0, 0);
Note that this requires that your underlying matrix type is float -- it won't do a conversion for you.
Alternatively, you could access the data row-by-row, as in this example that sums up the positive elements of a matrix M of type double:
double sum=0;
for(int i = 0; i < M.rows; i++)
{
const double* Mi = M.ptr<double>(i);
for(int j = 0; j < M.cols; j++)
sum += std::max(Mi[j], 0.);
}
If none of these work, I'd suggest creating a small matrix with known values (e.g. a 2x2 matrix with 1 black pixel and 3 white pixels) and use that to help debug your code.
To really make it apparent what the problem is, imagine a 16 by 16 image. Now think of pixel number 17 in the linear representation.
17 is a prime number. There is no j*i that will index your source image at pixel 17 if the row or column width is 16. Thus elements like 17, 19, 23 and so on will be uninitialized or at best 0, resulting in a "weird" output.
How about pixel 8 in the linear representation? that one in contrast will get hit by your loop four times, i.e. by 1x8, 2x4, 4x2, and 8x1!
The indexing #NateKohl presents in his answer will fix it since he multiplies a row position by the length of the row and then simply walks along the columns.
You can try this loop...
for(int row=0;row<height;row++)
{
for(int col=0;col<width;col++)
{
float float_data = input.at<float>(row,col);
// do some processing with value of float_data
out.at<float>(row,col) = float_data;
}
}
Is there a need to cast the uchar pointers of input and out Mats to float pointers?

reading TGA files in OpenGl to create a 3d ouse

I have a TGA file and a library that allready has everything that I need to read TGA and use them.
This class has a method called pixels(), that returns a pointer that is pointed to the memory area where pixel are stored as RGBRGBRGB...
My question is, how can I take the pixel value?
Cause if I make something like this:
img.load("foo.tga");
printf ("%i", img.pixels());
It gives back to me what is proprably the address.
I've found this code on this site:
struct Pixel2d
{
static const int SIZE = 50;
unsigned char& operator()( int nCol, int nRow, int RGB)
{
return pixels[ ( nCol* SIZE + nRow) * 3 + RGB];
}
unsigned char pixels[SIZE * SIZE * 3 ];
};
int main()
{
Pixel2d p2darray;
glReadPixels(50,50, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &p.pixels);
for( int i = 0; i < Pixel2d::SIZE ; ++i )
{
for( int j = 0; j < Pixel2d::SIZE ; ++j )
{
unsigned char rpixel = p2darray(i , j , 0);
unsigned char gpixel = p2darray(i , j , 1);
unsigned char bpixel = p2darray(i , j , 2);
}
}
}
I think that It can work great for me, but how can I tell the program to read from my img?
Tga supports different pixel depths. And we don't know what library you're using. But generally speaking pixels() should return a pointer to a buffer containing pixels. Say for sake of argument it unpacks the pixels into 8-bit per channel subpixels, then each pixel is represented by 3 bytes.
So to access a pixel at a given offset in the buffer:
const u8* pixelBuffer = img.pixels():
u8 red = pixelBuffer[(offset*3)+0];
u8 green = pixelBuffer[(offset*3)+1];
u8 blue = pixelBuffer[(offset*3)+2];
If you know the width of the image buffer then you can get a pixel by its x and y coordinates:
u8 red = pixelBuffer[((x+(y*width))*3)+0];

glReadPixels store x, y values

I'm trying to store pixel data by using glReadPixels, but so far I managed to only store it one pixel at a time. I'm not sure if this is the way to go. I currently have this:
unsigned char pixels[3];
glReadPixels(50,50, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, pixels);
What would be a good way to store it in an array, so that I can get the values like this:
pixels[20][50][0]; // x=20 y=50 -> R value
pixels[20][50][1]; // x=20 y=50 -> G value
pixels[20][50][2]; // x=20 y=50 -> B value
I guess I could simple put it in a loop:
for ( all pixels on Y axis )
{
for ( all pixels in X axis )
{
unsigned char pixels[width][height][3];
glReadPixels(x,y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, pixels[x][y]);
}
}
But I have the feeling that there must be a much better way to do this. But I do however need my array to be like I described above the code. So would the for loop idea be good, or is there a better way?
glReadPixels simply returns bytes in the order R, G, B, R, G, B, ... (based on your setting of GL_RGB) from the bottom left of the screen going up to the top right. From the OpenGL documentation:
glReadPixels returns pixel data from the frame buffer, starting with
the pixel whose lower left corner is at location (x, y), into client
memory starting at location data. Several parameters control the
processing of the pixel data before it is placed into client memory.
These parameters are set with three commands: glPixelStore,
glPixelTransfer, and glPixelMap. This reference page describes the
effects on glReadPixels of most, but not all of the parameters
specified by these three commands.
The overhead of calling glReadPixels thousands of times will most likely take a noticeable amount of time (depends on the window size, I wouldn't be surprised if the loop took 1-2 seconds).
It is recommended that you only call glReadPixels once and store it in a byte array of size (width - x) * (height - y) * 3. From there you can either reference a pixel's component location with data[(py * width + px) * 3 + component] where px and py are the pixel locations you want to look up, and component being the R, G, or B components of the pixel.
If you absolutely must have it in a 3-dimensional array, you can write some code to rearrange the 1d array after the glReadPixels call.
If you'll define pixel array like: this:
unsigned char pixels[MAX_Y][MAX_X][3];
And the you'll access it like this:
pixels[y][x][0] = r;
pixels[y][x][1] = g;
pixels[y][x][2] = b;
Then you'll be able to read pixels with one glReadPixels call:
glReadPixels(left, top, MAX_Y, MAX_X, GL_RGB, GL_UNSIGNED_BYTE, pixels);
What you can do is declare a simple one dimensional array in a struct and use operator overloading for convenient subscript notation
struct Pixel2d
{
static const int SIZE = 50;
unsigned char& operator()( int nCol, int nRow, int RGB)
{
return pixels[ ( nCol* SIZE + nRow) * 3 + RGB];
}
unsigned char pixels[SIZE * SIZE * 3 ];
};
int main()
{
Pixel2d p2darray;
glReadPixels(50,50, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &p.pixels);
for( int i = 0; i < Pixel2d::SIZE ; ++i )
{
for( int j = 0; j < Pixel2d::SIZE ; ++j )
{
unsigned char rpixel = p2darray(i , j , 0);
unsigned char gpixel = p2darray(i , j , 1);
unsigned char bpixel = p2darray(i , j , 2);
}
}
}
Here you are reading a 50*50 pixel in one shot and using operator()( int nCol, int nRow, int RGB) operator provides the needed convenience. For performance reasons you don't want to make too many glReadPixels calls