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;
}
Related
I already know how to flip an image vertically or horizontally. I have the following code that does so horizontally. The image data here is stored in a QImage as I was working with Qt here.
QImage image(imageFileName);
QImage newImage(image);
if(image.depth() > 8)
{
for (int idx_Y = 0; idx_Y < image.height(); idx_Y++)
{
for (int idx_X = 0; idx_X < image.width(); idx_X++)
{
QRgb rgb = image.pixel(image.width() - 1 - idx_X, idx_Y);
newImage.setPixel(idx_X, idx_Y, rgb);
}
}
}
I'm sure there are faster methods to get it done. However, I don't want any memory allocation on the heap. Could you please tell me what other much faster algorithms there could be?
Thank you.
Elaborating on #Spektres hint
2 nested for loops are not the problem... the setPixel and pixel functions are usually crawlingly slooow on most gfx APIs. Using direct pixel access instead usually boost speed ~1000 times or more ...
This could look like:
QImage image(imageFileName);
QImage newImage(image);
if (image.depth() >= 8) {
const int bytesPerPixel = image.depth() / 8;
for (int y = 0; y < image.height(); ++y) {
char *dataSrc = image.bits() + y * image.bytesPerLine();
char *dataDst = newImage.bits() + y * newImage.bytesPerLine()
+ (newImage.width() - 1) * bytesPerPixel;
for (int i = image.width(); i--;
dataSrc += bytesPerPixel, dataDst -= bytesPerPixel) {
for (int i = 0; i < bytesPerPixel; ++i) dataDst[i] = dataSrc[i];
}
}
}
Please, note that I changed image.depth() > 8 into image.depth() >= 8. (I saw no reason to exclude e.g. QImage::Format_Grayscale8.)
A slightly modified version for mirroring the QImage newImage in-place (considering that it is already copied):
QImage image(imageFileName);
QImage newImage(image);
if (newImage.depth() >= 8) {
const int bytesPerPixel = newImage.depth() / 8;
for (int y = 0; y < image.height(); ++y) {
char *dataL = newImage.bits() + y * newImage.bytesPerLine();
char *dataR = dataL + (newImage.width() - 1) * bytesPerPixel;
for (; dataL < dataR; dataL += bytesPerPixel, dataR -= bytesPerPixel) {
for (int i = 0; i < bytesPerPixel; ++i) std::swap(dataL[i], dataR[i]);
}
}
}
Concerning QImage and qRgb(), you may also notice that Qt supports QImages with 16 bits per component (since Qt 5.12).
I fiddled a bit with this in
SO: Set pixel value of 16 bit grayscale QImage
which might be interesting as well.
I am new with OpenCV. I am working on Visual Studio 2017 and use the plugin Image Watch to see Mat file of openCV.
What I've done:
I have to read a binary file to get 1000 images (256*320 pixels uint16 so 2 octets by pixel) in an array of double. After this, I wanted to see with Image Watch my data to be sure all is okay. So I convert the first image into a uchar on 8 bit to visualise it. I add my code (most part don't read it, just go to the end) :
#include "stdafx.h"
#include <iostream>
#include "stdio.h"
#include <fstream>
#include <stdint.h>
#include "windows.h"
#include <opencv2/core/core.hpp> // cv::Mat
#include <math.h>
#include <vector>
using namespace std;
using namespace cv;
template<class T>
T my_ntoh_little(unsigned char* buf) {
const auto s = sizeof(T);
T value = 0;
for (unsigned i = 0; i < s; i++)
value |= buf[i] << CHAR_BIT * i;
return value;
}
int main()
{
ifstream is("Filename", ifstream::binary);
if (is) {
// Reading size of the file and initialising variables
is.seekg(0, is.end);
int length = is.tellg();
int main_header_size = 3000;
int frame_header_size = 1000;
int width = 320, height = 256, count_frames = 1000;
int buffer_image = width * height * 2;
unsigned char *data_char = new unsigned char[length]; // Variable which will contains all the data
// Initializing 3D array for stocking all images
double ***data;
data = new double**[count_frames];
for (unsigned i = 0; i < count_frames; i++) {
data[i] = new double*[height];
for (unsigned j = 0; j < height; j++)
data[i][j] = new double[width];
}
// Reading the file once
is.seekg(0, is.beg);
is.read(reinterpret_cast<char*>(data_char), length);
// Convert pixel by pixel uchar into uint16 (using pointer on data_char)
int indice, minid = 65536.0, maxid = 0.0;
for (unsigned count = 0; count < count_frames; count++) {
// Initialize pointer address
indice = main_header_size + count * (frame_header_size + buffer_image) + frame_header_size;
for (unsigned i = 0; i < height; i++) {
for (unsigned j = 0; j < width; j++) {
data[count][i][j] = my_ntoh_little<uint16_t>(data_char + indice);
// Search for min/max for normalize after
if (data[count][i][j] < minid and count == 0)
minid = data[count][i][j];
if (data[count][i][j] > maxid and count == 0)
maxid = data[count][i][j];
// Updating pointer to next pixel
indice += 2;
}
}
}
// Get back first image, normalize between 0-255, cast into uchar to the future Mat object
uchar *dataImRGB = new uchar[width * height * 3];
int image_display = 900;
int pixel_norm;
for (unsigned i = 0; i < height; i++) {
for (unsigned j = 0; j < width; j++) {
pixel_norm = round((data[image_display][i][j] - double(minid)) / double(maxid - minid) * 255);
dataImRGB[i * 320 * 3 + 3 * j] = static_cast<uchar>(pixel_norm);
dataImRGB[i * 320 * 3 + 3 * j + 1] = static_cast<uchar>(pixel_norm);
dataImRGB[i * 320 * 3 + 3 * j + 2] = static_cast<uchar>(pixel_norm);
}
}
// Create Mat object (it is imageRGB8 I can see on Image watch)
Mat imageRGB8 = Mat(width, height, CV_8UC3, dataImRGB);
// Creating a list of Map and add first Mat
vector<Mat> listImages;
listImages.push_back(imageRGB8);
// -----------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------
// Future : directly keep the uchar read in the original file and import it on a Mat object
// But how to get the pixel at (0,0) of the first Mat on the vector ?
// -----------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------
// De-Allocate memory to prevent memory leak
for (int i = 0; i < count_frames; ++i) {
for (int j = 0; j < height; ++j)
delete[] data[i][j];
delete[] data[i];
}
delete[] data;
}
return 0;
}
Where I am stuck:
I don't know how to work with this vector, how to manipulate the data. For example, if i want to do the mean of all images, so the mean of all Mat objects in the vector, how to do this ? Or just how to get the first pixel of the third image in the vector ? These examples have for aim to explain me the slicing with such type of data because I know how it works with vector of double, but not with openCv object.
Thank you in advance for any help/advice.
Assuming that you have got all of your images properly packed into your image list you can do the following:
This will get the mean of all images in your list:
cv::Scalar meansum(0.0f,0.0f,0.0f);
size_t length = listImages.size();
for (size_t i = 0; i < length; i++){
//mu == mean of current image
cv::Scalar mu = cv::mean(listImages[i]);
meansum += mu;
}
float means[3] = { meansum[0] / length, meansum[1] / length, meansum[2] / length };
std::cout << "Means " << means[0] << " " << means[1] << " " << means[2] << std::endl;
To get the first pixel in your third image you can use the at() method or a row pointer. (Row pointers are faster, but don't have any guards against accessing out of bounds memory locations.)
Mat third_image = list_images[2];
//using at()
uchar first_pixel_blue_value = third_image.at<uchar>(0,0,0);
std::cout<<(int)first_pixel_blue_value<<std::endl;
//using row pointer
uchar* row = third_image.ptr<uchar>(0); //pointer to row 0
std::cout<<"blue: " <<(int)row[0];
std::cout<<" green: "<<(int)row[1];
std::cout<<" red: " <<(int)row[2];
More info can be found here:
https://docs.opencv.org/3.1.0/d2/de8/group__core__array.html (under functions)
and here:
https://docs.opencv.org/trunk/d3/d63/classcv_1_1Mat.html
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];
I have this code that implements Prewitt edge detection. What I need to do is to implement it with only one buffer, meaning, I will not create copy of the image but edit original image. So if i want to change pixel with value 78, I cant put the new value e.g. 100 until all surrounding pixels have read value 78. Color values of the pixels. I have tried all day to figure it out but couldn't, if someone would write me some kind of pseudocode I would be very grateful
void filter_serial_prewitt(int *inBuffer, int *outBuffer, int width, int height){
for (int i = 1; i < width - 1; i ++) {
for (int j = 1; j < height - 1; j ++) {
int Fx = 0;
int Fy = 0;
int F = 0;
for (int m = -1; m <= 1; m++) {
for (int n = -1; n <= 1; n++) {
Fx += inBuffer[(j + n) * width + (i + m)] * n;
Fy += inBuffer[(j + n) * width + (i + m)] * m;
}
}
F = abs(Fx) + abs(Fy);
if (F < THRESHOLD){
outBuffer[j * width + i] = 255;
} else{
outBuffer[j * width + i] = 0;
}
}
}
}
One thing to know about a Prewitt operator is that it is separable. See the Wikipedia article for details.
To calculate a single output row, you need to do the following (pseudocode):
int* buffer = malloc (sizeof(int) * width);
for (int i = 0; i < width; i++)
{
// Do the vertical pass of the convolution of the first 3 rows into
// the buffer.
buffer [ i ] = vertical_convolve(inBuffer [ i ], vertical_kernel);
}
// Next, do the horizontal convolution of the first row. We need to
// keep the previous value in a temp buffer while we work
int temp0 = horizontal_convolve(buffer [ 0 ], horizontal_kernel);
for (int i = 1; i < width; i++)
{
int temp1 = horizontal_convolve(buffer[ i ], horizontal_kernel);
inBuffer [ i - 1 ] = temp0;
temp0 = temp1;
}
That requires a buffer that is 1 pixel tall and the width of the image.
To work on the whole image, you need to keep 2 of the above buffers around and after you calculate a pixel on the third line, you can replace the first pixel of the first line of the image with the first pixel of the first buffer. Then you can put the newly calculated value into the buffer.
So in this scenario, you won't keep around an entire second image, but will need to keep around 2 1-pixel tall buffers that are as wide as the image.
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);
}