I'm trying to flip an image vertically, after retrieving the buffer from openGL. It seems to be outputting an incorrect image with the following code:
const int width = 100;
const int height = width;
const int components = 3;
unsigned char pixels[width * height * components];
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels);
unsigned char flipPixels[width * height * components];
for (int i = 0; i < width; ++i) {
for (int j = 0; j < height; ++j) {
for (int k = 0; k < components; ++k) {
flipPixels[i + j * width + k] = pixels[(height) * (width) - ((j+1) * width) + i + k];
}
}
}
I know I can only iterate half the height and achieve the same, but I want to implement it by going through the complete height of the image. I can't seem to figure out what's wrong with the code. Any help would be appreciated.
I'm not sure how the image is stored but your indices i and k are given the same stride which is suspicious. Maybe you want i * components and j * width * components. After that, inverting vertically you should only have to change j to (height - j - 1).
flipPixels[(i + j * width) * components + k] = pixels[(i + (height - 1 - j) * width) * components + k];
I had the same issue, the pixels returned by OpenGL resulten in an upside down bitmap. so I flipped them like this: but the bitmap is still flipped left to right...
void Flip(GLubyte* pixels, int pixelbuffersize)
{
// basically rewrites from bottom up...
std::vector<GLubyte> flipped_pixels(pixels, pixels+pixelbuffersize);
auto count = flipped_pixels.size();
std::reverse(flipped_pixels.begin(), flipped_pixels.end());
GLubyte* buff = (reinterpret_cast<GLubyte*>(&flipped_pixels[0]));
const void * pnewdata = (const void *)buff;
memcpy(pixels, pnewdata, count);
}
Related
in SDL we're trying to find the average colour of the screen. To do so we're reading all the pixel colour values and putting them into an array (Performance is not of concern), for some reason however, GetPixel always returns a colour (0,0,0,0). Ive already established that the RenderReadPixels works correctly since saving a screenshot works just fine.
const Uint32 format = SDL_PIXELFORMAT_ARGB8888;
SDL_Surface* surface = SDL_CreateRGBSurfaceWithFormat(0, width, height, 32, format);
SDL_RenderReadPixels(renderer, NULL, format, surface->pixels, surface->pitch);
float* coverage = new float[width*height]; // * allocates memory
coverage[0] = 1;
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
SDL_Color col;
col = GetPixel(surface, i, j);
coverage[i * height + j] = (1/3)(col.r + col.b + col.g); //Return coverage value at i, j
std::cout << coverage[i * height + j]; //Always returns 0
std::cout << "\n";
}
}
SDL_Color GetPixel(SDL_Surface* srf, int x, int y)
{
SDL_Color color;
SDL_GetRGBA(get_pixel32(srf, x, y), srf->format, &color.r, &color.g, &color.b, &color.a);
return color;
}
Uint32 get_pixel32(SDL_Surface* surface, int x, int y)
{
//Convert the pixels to 32 bit
Uint32* pixels = (Uint32*)surface->pixels;
//Get the requested pixel
return pixels[(y * surface->w) + x];
}
1/3 is always 0 because of the way number promotion works in C++.
Best be explicit about what you want:
coverage[i * height + j] = float(col.r + col.b + col.g) / 3.0;
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;
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I'm trying to rotate an image pixel by pixel by 90 degrees, It seems there is a math problem that I couldn't figure out...and array out of bounds exception
here is my attempt
const unsigned char *srcData = source.getData();
unsigned char *dstData = new unsigned char[width * height * bpp];
size_t srcPitch = source.getRowSpan();
size_t dstPitch = width * bpp;
for(int i=0; i<height; i++)
{
for(int j=0; j<width * bpp; j++)
{
rotatedData[(i * dstPitch) + j]= dstData[(height-i) * dstPitch + j];
}
}
First, let's build an image descriptor to keep track of the dimensions.
struct ImageDescriptor {
std::size_t width;
std::size_t height;
std::size_t channels;
std::size_t stride() const { return width * channels; }
std::size_t offset(std::size_t row, std::size_t col, std::size_t chan) {
assert(0 <= row && row < height);
assert(0 <= col && col < width);
assert(0 <= chan && chan < channels);
return row*stride() + col*channels + chan;
// or, depending on your coordinate system ...
// return (height - row - 1)*stride() + col*channels + chan;
}
std::size_t size() const { return height * stride(); }
};
Now we'll need two ImageDescriptors to keep track of the dimensions of our two images. Note that, unless the original image is square, the rotated image will have a different width and height (and thus stride). Specifically, the width of the rotated image will be the height of the source image (and vice versa).
const ImageDescriptor source(width, height, bpp);
ImageDescriptor target(height, width, bpp); // note width/height swap
A common way to do a transformation is to loop over the destination pixels and look up the source pixels.
unsigned char *rotated = new[target.size()];
for (std::size_t row = 0; row < target.height; ++row) {
for (std::size_t col = 0; col < target.width; ++col) {
for (std::size_t chan = 0; chan < target.channels; ++chan) {
rotated[target.offset(row, col, chan)] =
original[source.offset(col, row, chan)];
}
}
}
Once you get it right, you can work to eliminate unnecessary computation. The first opportunity is to just step our way through the destination image, since all that is in memory order. The second opportunity is, to hoist the source offset calculation out of the channel loop. Finally, if bpp is a constant, you can unroll the innermost loop.
unsigned char *p = rotated;
for (std::size_t row = 0; row < target.height; ++row) {
for (std::size_t col = 0; col < target.width; ++col) {
const std::size_t base = source.offset(col, row, 0);
for (std::size_t chan = 0; chan < target.channels; ++chan) {
*p++ = original[base + chan];
}
}
}
Try this one:
for (int j = 0; j<width * bpp; j++)
{
for (int i = 0 ; i<height; i++)
{
rotatedData[(height)*(dstPitch - j - 1) + i] = dstData[(i * dstPitch) + j];
}
}
and if dstData isn't squared:
//define rotatedData_height before.
rotatedData[(rotatedData_height)*(dstPitch - j - 1) + i] = dstData[(i * dstPitch) + j];
after trying to implement a Gaussian blur for an image i have ran into a problem where the output image looks like multiple blurred versions of the original image (input image)
I have too low of a reputation to post images so have no idea how to fully show you what is happening however, i can post a gyazo link to the image:
https://gyazo.com/38fbe1abd442a3167747760866584655 - Original,
https://gyazo.com/471693c49917d3d3e243ee4156f4fe12 - Output
Here is some code:
int kernel[3][3] = { 1, 2, 1,
2, 4, 2,
1, 2, 1 };
void guassian_blur2D(unsigned char * arr, unsigned char * result, int width, int height)
{
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
for (int k = 0; k < 3; k++)
{
result[3 * row * width + 3 * col + k] = accessPixel(arr, col, row, k, width, height);
}
}
}
}
int accessPixel(unsigned char * arr, int col, int row, int k, int width, int height)
{
int sum = 0;
int sumKernel = 0;
for (int j = -1; j <= 1; j++)
{
for (int i = -1; i <= 1; i++)
{
if ((row + j) >= 0 && (row + j) < height && (col + i) >= 0 && (col + i) < width)
{
int color = arr[(row + j) * 3 * width + (col + i) * 3 + k];
sum += color * kernel[i + 1][j + 1];
sumKernel += kernel[i + 1][j + 1];
}
}
}
return sum / sumKernel;
}
Image is saved:
guassian_blur2D(inputBuffer, outputBuffer, width, height);
//Save the processed image
outputImage.convertToType(FREE_IMAGE_TYPE::FIT_BITMAP);
outputImage.convertTo24Bits();
outputImage.save("appleBlur.png");
cout << "Blur Complete" << endl;
Any help would be great, if this also helps i am trying to store the image as a grey-scale so that no colour is saved.
Looks like the problem is not within your blurring code, and is related to saving or accessing image data.
I have used OpenCV to read/save images, and got expected result. Here's a snippet:
cv::Mat3b img = cv::imread("path_to_img.png");
cv::Mat3b out = img.clone();
guassian_blur2D(img.data, out.data, img.cols, img.rows);
cv::imshow("img", img);
cv::imshow("out", out);
cv::waitKey(0);
And here are input and output images:
The blur is not very noticeable (due to high image resolution and small kernel), but if you look carefully - it looks correct.
I am trying to flip a buffer, but the buffer doesn't get fully processed.
Is a buffer of pixels and I need basically to flip it vertically.
Can anyone spot what am I doing wrong? Thanks in advance.
void flipVertically(unsigned int* buffer, const unsigned int width, const unsigned int height)
{
const unsigned int rowWidth = width; // Length of a row
const unsigned int rows = height / 2; // Iterate only half the buffer to get a full flip
unsigned int* tempRow = (unsigned int*)malloc(rowWidth);
for (int rowIndex = 0; rowIndex < rows; rowIndex++)
{
memcpy(tempRow, buffer + (rowIndex * rowWidth), rowWidth);
memcpy(buffer + (rowIndex * rowWidth), buffer + (height - rowIndex - 1) * rowWidth, rowWidth);
memcpy(buffer + (height - rowIndex - 1) * rowWidth, tempRow, rowWidth);
}
free(tempRow);
}
Will this work?
void flip(unsigned* buffer, unsigned width, unsigned height)
{
unsigned rows = height / 2; // Iterate only half the buffer to get a full flip
unsigned* tempRow = (unsigned*)malloc(width * sizeof(unsigned));
for (unsigned rowIndex = 0; rowIndex < rows; rowIndex++)
{
memcpy(tempRow, buffer + rowIndex * width, width * sizeof(unsigned));
memcpy(buffer + rowIndex * width, buffer + (height - rowIndex - 1) * width, width * sizeof(unsigned));
memcpy(buffer + (height - rowIndex - 1) * width, tempRow, width * sizeof(unsigned));
}
free(tempRow);
}