-1

I have some serious problems with this stupid rotation function of a BMP image... I've already post the same question now 2 days ago, I know... But no one could make me to understand what's happening here and how can I solve this issue.

EDIT: that image will be rotated just with 90 degrees multipler.

In this moment I'm trying to rotate this image with an 180 degree

enter image description here

And here it's my result until this moment

enter image description here

Here it's my code:

#include <algorithm>
#include <fstream>
#include <math.h>
#include <vector>
#include <iostream>

using namespace std;

double PI = 3.141592653589793238462643383279;
struct BMP {
    int width;
    int height;
    unsigned char header[54];
    unsigned char *pixels;
    int row_padded;
    int size_padded;
};

void writeBMP(string filename, BMP image) {
    string fileName = "Output Files\\" + filename;
    FILE *out = fopen(fileName.c_str(), "wb");
    fwrite(image.header, sizeof(unsigned char), 54, out);

    unsigned char tmp;
    for (int i = 0; i < image.height; i++) {
        for (int j = 0; j < image.width * 3; j += 3) {
            //Convert(B, G, R) to(R, G, B)
            tmp = image.pixels[j];
            image.pixels[j] = image.pixels[j + 2];
            image.pixels[j + 2] = tmp;
        }
    }
    fwrite(image.pixels, sizeof(unsigned char), image.size_padded, out);
    fclose(out);
}

BMP readBMP(string filename) {
    BMP image;
    string fileName = "Input Files\\" + filename;
    FILE *in = fopen(fileName.c_str(), "rb");

    if (in == NULL)
        throw "Argument Exception";

    fread(image.header, sizeof(unsigned char), 54, in); // read the 54-byte header

    // extract image height and width from header
    image.width = *(int *) &image.header[18];
    image.height = *(int *) &image.header[22];

    image.row_padded = (image.width * 3 + 3) & (~3);     // ok size of a single row rounded up to multiple of 4
    image.size_padded = image.row_padded * image.height;  // padded full size
    image.pixels = new unsigned char[image.size_padded];  // yeah !

    if (fread(image.pixels, sizeof(unsigned char), image.size_padded, in) == image.size_padded) {
        unsigned char tmp;
        for (int i = 0; i < image.height; i++) {
            for (int j = 0; j < image.width * 3; j += 3) {
                // Convert (B, G, R) to (R, G, B)
                tmp = image.pixels[j];
                image.pixels[j] = image.pixels[j + 2];
                image.pixels[j + 2] = tmp;
            }
        }
    } else {
        cout << "Error: all bytes couldn't be read" << endl;
    }

    fclose(in);
    return image;
}

BMP rotate(BMP image, double degree) {
    BMP newImage = image;
    unsigned char *pixels = new unsigned char[image.size_padded];

    double radians = (degree * PI) / 180;
    int sinf = (int) sin(radians);
    int cosf = (int) cos(radians);

    double x0 = 0.5 * (image.width - 1);     // point to rotate about
    double y0 = 0.5 * (image.height - 1);     // center of image

    // rotation
    for (int x = 0; x < image.height; x++) {
        for (int y = 0; y < image.width * 3; y += 3) {
            long double a = x - x0;
            long double b = y - y0;
            int xx = (int) (+a * cosf - b * sinf + x0);
            int yy = (int) (+a * sinf + b * cosf + y0);

            if (xx >= 0 && xx < image.width && yy >= 0 && yy < image.height) {
                pixels[(y * image.width + x) * 3 + 0] = image.pixels[(yy * image.width + xx) * 3 + 0];
                pixels[(y * image.width + x) * 3 + 1] = image.pixels[(yy * image.width + xx) * 3 + 1];
                pixels[(y * image.width + x) * 3 + 2] = image.pixels[(yy * image.width + xx) * 3 + 2];
            }
        }
    }
    newImage.pixels = pixels;
    return newImage;
}

int main() {
    BMP image = readBMP("Image.bmp");
    image = rotate(image, 180);
    writeBMP("Output.bmp", image);
    return 0;
}

So please guys, I beg you... Could one of you help me to solve this stupid problem who kill me 2 days for almost nothing :/

Thanks

Mircea
  • 1,671
  • 7
  • 25
  • 41
  • Well, you've gotten farther ahead than before. Before you got utter garbage. Now you have something that kind of resembles something. One very likely reason for both the color shift and the misalignment is that in the rotation function `y` should be multipled by `row_padded`, and not `height`. Think about it. But the repetitive calculation of pixel addresses is duplicated code, and adds a lot of clutter to the code, muddying the overall algorithm and making it easier for bugs to hide. Factor it out into a single, standalone function: given y and x, and BMP, return a pointer to the pixel. – Sam Varshavchik Nov 18 '16 at 03:00
  • @SamVarshavchik I've no idea what I'm doing wrong here because this code it's functionally on other images except that I need... [Here](https://www.dropbox.com/s/aggfmos2umttac2/Input-1.bmp?dl=0) you can find the original image I need. Could you help me to make this code to work please? – Mircea Nov 18 '16 at 12:00

1 Answers1

2

EDIT_2:

Check full code:

#include <algorithm>
#include <fstream>
#include <math.h>
#include <vector>
#include <string>
#include <iostream>
#include <chrono>
//#include "operation_parser.h"

using namespace std;
using namespace std::chrono;

double PI = 3.141592653589793238462643383279;
struct BMP {
    int width;
    int height;
    unsigned char header[54];
    unsigned char *pixels;
    int size;
    int row_padded;
    long long int size_padded;
};

void writeBMP(string filename, BMP image) {
    string fileName = filename;
    FILE *out = fopen(fileName.c_str(), "wb");
    fwrite(image.header, sizeof(unsigned char), 54, out);

    unsigned char tmp;
    for (int i = 0; i < image.height; i++) {
        for (int j = 0; j < image.width * 3; j += 3) {
            //Convert(B, G, R) to(R, G, B)
            tmp = image.pixels[j];
            image.pixels[j] = image.pixels[j + 2];
            image.pixels[j + 2] = tmp;
        }
    }
    fwrite(image.pixels, sizeof(unsigned char), image.size_padded, out);
    fclose(out);
}

BMP readBMP(string filename) {
    BMP image;
    string fileName = filename;
    FILE *in = fopen(fileName.c_str(), "rb");

    if (in == NULL)
        throw "Argument Exception";

    fread(image.header, sizeof(unsigned char), 54, in); // read the 54-byte header

    // extract image height and width from header
    image.width = *(int *) &image.header[18];
    image.height = *(int *) &image.header[22];

    image.row_padded = (image.width * 3 + 3) & (~3);     // ok size of a single row rounded up to multiple of 4
    image.size_padded = image.row_padded * image.height;  // padded full size
    image.pixels = new unsigned char[image.size_padded];  // yeah !

    if (fread(image.pixels, sizeof(unsigned char), image.size_padded, in) == image.size_padded) {
        unsigned char tmp;
        for (int i = 0; i < image.height; i++) {
            for (int j = 0; j < image.width * 3; j += 3) {
                // Convert (B, G, R) to (R, G, B)
                tmp = image.pixels[j];
                image.pixels[j] = image.pixels[j + 2];
                image.pixels[j + 2] = tmp;
            }
        }
    } else {
        cout << "Error: all bytes couldn't be read" << endl;
    }

    fclose(in);
    return image;
}

BMP rotate180Degree(BMP image, double degree) {
    _ASSERTE(degree == 180.0);

    BMP newImage = image;
    unsigned char *pixels = new unsigned char[image.size_padded];

    int H = image.height, W = image.width;
    for (int x = 0; x < H; x++) {
        for (int y = 0; y < W;y ++) {
            pixels[(x * W + y) * 3 + 0] = image.pixels[((H - 1 - x) * W + (W - 1 - y)) * 3 + 0];
            pixels[(x * W + y) * 3 + 1] = image.pixels[((H - 1 - x) * W + (W - 1 - y)) * 3 + 1];
            pixels[(x * W + y) * 3 + 2] = image.pixels[((H - 1 - x) * W + (W - 1 - y)) * 3 + 2];
        }
    }

    newImage.pixels = pixels;
    return newImage;
}

int main() {
    BMP image = readBMP("test.bmp");
    image = rotate180Degree(image, 180);
    writeBMP("Output.bmp", image);
    return 0;
}

EDIT_1:

If only 0,90,180,270 degrees to rotate, here is the example of rotate 180 degrees, modify it if you want to do it with 0,90,270(image width and height change when rotate_degree = 90 or 270):

BMP rotate180Degree(BMP image, double degree) {
    _ASSERTE(degree == 180.0);

    BMP newImage = image;
    unsigned char *pixels = new unsigned char[image.size_padded];

    int H = image.height, W = image.width;
    for (int x = 0; x < H; x++) {
        for (int y = 0; y < W;y ++) {
            pixels[(x * W + y) * 3 + 0] = image.pixels[((H - 1 - x) * W + (W - 1 - y)) * 3 + 0];
            pixels[(x * W + y) * 3 + 1] = image.pixels[((H - 1 - x) * W + (W - 1 - y)) * 3 + 1];
            pixels[(x * W + y) * 3 + 2] = image.pixels[((H - 1 - x) * W + (W - 1 - y)) * 3 + 2];
        }
    }

    newImage.pixels = pixels;
    return newImage;
}

Another solution : We can use CImage from atlimage.h:

std::shared_ptr<CImage> OriginalImg = std::make_shared<CImage>();
OriginalImg->Load("test.png");

int W = OriginalImg->GetWidth(), H = OriginalImg->GetHeight();

std::shared_ptr<CImage> RotatedImg = std::make_shared<CImage>();
RotatedImg->Create(W, H, OriginalImg->GetBPP());

for (unsigned int x = 0; x < W; ++x)
{
    for (unsigned int y = 0; y < H; ++y)
    {
        RotatedImg->SetPixel(x, y, OriginalImg->GetPixel(W - x - 1, H - y - 1));
    }
}

RotatedImg->Save("rotatedImage.png");

Result:

Rotated Image

xtluo
  • 1,961
  • 18
  • 26
  • Sorry.. but I need to use this kind of code because I need best performance I can get... could you help me to make my code work please? – Mircea Nov 18 '16 at 09:40
  • It seems that you included an extra header file("operation_parser.h") which is not provided. – xtluo Nov 18 '16 at 09:43
  • that just to for me to parse a text file because I will need it for me rotate image later. Inside of that text file is how many degrees my function should to used – Mircea Nov 18 '16 at 09:46
  • Please provide **reproducible** example for your question, cause in your code above, specificall in the function `rotate()`: `unsigned char *pixels = new unsigned char[image.size];` where `image.size` is not set, which can be easily fixed by replacing with `image.size_padded`. But who knows where else it goes wrong ? – xtluo Nov 18 '16 at 10:12
  • Also, if you want to rotate a Image by **any** degree(s), the first thing I think you need to do is to test whether you've got the right result by rotating **0/360** degree, then **180**, then **90, 270**, and last try degrees like **23/45/78**...But you haven't got **0** work so far, am I right ? – xtluo Nov 18 '16 at 10:18
  • Sorry for that, ok now I've edited my question and this code should be reproducible now – Mircea Nov 18 '16 at 10:18
  • No, the image will be rotated just with 90 degree multiples, like 0,90,180 and 270 – Mircea Nov 18 '16 at 10:19
  • Clockwise or Counterclockwise? – xtluo Nov 18 '16 at 10:22
  • Clockwise, and yes, I've test my image and on different angle.. but is not rotate correctly... for example here it's a [result of 90 degree](http://imgur.com/IZHP35n) – Mircea Nov 18 '16 at 10:25
  • Check my update, I think simply set the location without other calculation would be better, which you can use a few **if else statement**, cause also image width and height change when rotate 90 or 270. – xtluo Nov 18 '16 at 11:03
  • With your function I've obtain the same result like in my image from post but just in black and white, [the new result it's here](http://imgur.com/Vsai1BF) – Mircea Nov 18 '16 at 11:10
  • You should invoke `rotate180Degree(image, 180)` in `main()` instead. – xtluo Nov 18 '16 at 11:14
  • I've do this... and, if I'm comment this part of my code `unsigned char tmp; for (int i = 0; i < image.height; i++) { for (int j = 0; j < image.width * 3; j += 3) { // Convert (B, G, R) to (R, G, B) tmp = image.pixels[j]; image.pixels[j] = image.pixels[j + 2]; image.pixels[j + 2] = tmp; } }` which make me a conversion from BGR in RGB I will obtain exaclty the same result like in my post. – Mircea Nov 18 '16 at 11:18
  • In the EDIT_2 you just make a call of that function in main... I've done this already, the result it's exactly the same like I've showed you already... – Mircea Nov 18 '16 at 11:30
  • It works for me. Comment your full code, replace with EDIT_2, check the result. – xtluo Nov 18 '16 at 11:32
  • I've do this... your bmp image it's with bit depth 24? because for me it's not working – Mircea Nov 18 '16 at 11:34
  • Give me 2min, I will sent you the original image and please tell me if it's working for you on that – Mircea Nov 18 '16 at 11:35
  • Here it's the [original image](https://www.dropbox.com/s/j7cuegtmy6akgtp/Input-1.zip?dl=0) I've made it like a zip because the image it's 20Mb and to be easy to transfer – Mircea Nov 18 '16 at 11:39
  • Or if you prefer just the image without zip, it's [here](https://www.dropbox.com/s/aggfmos2umttac2/Input-1.bmp?dl=0) – Mircea Nov 18 '16 at 11:44
  • So is your example reproducible ? – xtluo Nov 18 '16 at 11:46
  • What do you mean..? – Mircea Nov 18 '16 at 11:51
  • And yes, it's true, you code it works but on any kind of other images, not on mines... So please if you can help me to make this code to work on my images. – Mircea Nov 18 '16 at 11:53
  • `CImage` is a wrapper for GDI+, you can just use GDI+ functions to rotate the image in place, it will use advanced optimization techniques with assembly code etc. – Barmak Shemirani Nov 19 '16 at 06:20