简体   繁体   English

C ++:BMP旋转图像

[英]C++: BMP rotate image

Ok guys, it's the third time I'm posting the same question (previous are here and here ). 好的,这是我第三次发布相同的问题(以前在这里这里 )。

Now at this time I will try to explain what's my problem: 现在,这时我将尝试解释我的问题:

  1. So first them all, I need to rotate a .bmp image and it's not rotate correctly. 所以首先,我需要旋转一个.bmp图像,但它旋转不正确。 But I don't need to rotate a random image with extension .bmp , I need to rotate this one . 但是我不需要旋转扩展名为.bmp的随机图像,我需要旋转图像。 I've tried with many other images and all of them was rotated correctly, except mine. 我尝试了许多其他图像,但除我之外,所有图像均已正确旋转。
  2. In this moment my code it works just for 180-degree , how could make it to works on any degree which is multiple of 90-degree (I need to rotate my image just with 90, 180 or 270 degrees , not more). 在这一刻,我的代码仅适用于180度 ,如何使其能够适用于90度的倍数(我需要将图像仅旋转90度180度或270度 ,而不是更多)。
  3. I don't need any kind of external library for this code like CImage , OpenCV , ImageMagik and so on... I need to make this code to work. 对于此代码,我不需要任何外部库,例如CImageOpenCVImageMagik等...我需要使此代码正常工作。

So yeh, that's it. 是的,就是这样。 And here you can find my actual result . 在这里您可以找到我的实际结果

CODE: 码:

#include <array>

using namespace std;

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");

    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;
            }
        }
    }
    fclose(in);
    return image;
}

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

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

int main() {

    BMP image = readBMP("Input-1.bmp");
    image = rotate(image, 180);
    writeBMP("Output.bmp", image);

    return 0;
}

The BMP file format is a complicated, convoluted beast and there's no such thing as a "simple" BMP file reader. BMP文件格式是一种复杂的,令人费解的野兽,没有“简单的” BMP文件读取器之类的东西。 The code you have there makes certain hard coded assumptions on the files you're trying to read (24bpp true color, tightly packed, no compression) that it will flat (on its face) when it encounters anything that isn't that specific format. 您所拥有的代码会对要读取的文件进行某些硬编码的假设(24bpp真实色彩,紧密包装,无压缩),当遇到任何非特定格式的文件时,它将平坦(表面) 。 Unfortunately, for you, the majority of BMP files out there is not of that kind. 不幸的是,对您而言,大多数BMP文件都不是这种类型。 To give you an idea of what a fully conforming BMP reader must support have a look at this page: 为了让您了解完全兼容的BMP阅读器必须支持什么,请查看以下页面:

http://entropymine.com/jason/bmpsuite/bmpsuite/html/bmpsuite.html http://entropymine.com/jason/bmpsuite/bmpsuite/html/bmpsuite.html

And the code you have up there does not even check if there's a valid file magic bytes signature and if the header is valid. 并且您那里的代码甚至不会检查是否存在有效的文件魔术字节签名以及标头是否有效。 So that's your problem right there: You don't have a BMP file reader. 这就是您的问题所在:您没有BMP文件阅读器。 You have something that actually spits out pixels if you're lucky enough the feed it something that by chance happens to be in the right format. 如果您足够幸运的话,您实际上有一些东西会吐出像素,而碰巧是正确格式的东西。

You have major memory leak. 您有大量内存泄漏。 pixels = new unsigned char[size]; must be freed otherwise there is potentially several megabytes leak with every rotation. 必须释放,否则每次旋转可能会泄漏几兆字节。 You have to rewrite the function to keep track of memory allocations. 您必须重写该函数以跟踪内存分配。

When you rotate the image by 90 or 270 of the image, the widht/height of image changes. 将图像旋转图像的90或270度时,图像的宽度/高度会改变。 The size may change too because of padding. 大小也可能由于填充而改变。 The new dimension has to be recorded in header file. 新维度必须记录在头文件中。

In C++ you can use fopen , but std::fstream is preferred. 在C ++中,可以使用fopen ,但首选std::fstream

Here is an example which works in Windows for 24bit images only. 这是一个仅在Windows中适用于24位图像的示例。 In Big-endian systems you can't use memcpy the way I used it below. 在Big-endian系统中,您不能像下面那样使用memcpy

Note, this is for practice only . 请注意, 这仅用于练习 As @datenwolf explained you should use a library for real applications. 正如@datenwolf解释的那样,您应该将库用于实际应用程序。 Most standard libraries such Windows GDI library (basic drawing functions) offer solution for these common tasks. 大多数标准库,例如Windows GDI库(基本绘图功能),为这些常见任务提供了解决方案。

#include <iostream>
#include <fstream>
#include <string>
#include <Windows.h>

bool rotate(char *src, char *dst, BITMAPINFOHEADER &bi, int angle)
{
    //In 24bit image, the length of each row must be multiple of 4
    int padw = 4 - ((bi.biWidth * 3) % 4);
    if(padw == 4) padw = 0;

    int padh = 4 - ((bi.biHeight * 3) % 4);
    if(padh == 4) padh = 0;

    int pad2 = 0;
    if(padh == 1 || padh == 3) pad2 = 2;

    bi.biHeight += padh;

    int w = bi.biWidth;
    int h = bi.biHeight;
    if(angle == 90 || angle == 270)
    {
        std::swap(bi.biWidth, bi.biHeight);
    }
    else
    {
        bi.biHeight -= padh;
    }

    for(int row = 0; row < h; row++)
    {
        for(int col = 0; col < w; col++)
        {
            int n1 = 3 * (col + w  * row) + padw * row;
            int n2 = 0;

            switch(angle)
            {
            case 0:   n2 = 3 * (col + w * row) + padw * row; break;
            case 90:  n2 = 3 * ((h - row - 1) + h * col) + pad2 * col; break;
            case 180: n2 = 3 * (col + w * (h - row - 1)) + padw * (h - row - 1); break;
            case 270: n2 = 3 * (row + h * col) + pad2 * col; break;
            }

            dst[n2 + 0] = src[n1 + 0];
            dst[n2 + 1] = src[n1 + 1];
            dst[n2 + 2] = src[n1 + 2];
        }
    }

    for(int row = 0; row < bi.biHeight; row++)
        for(int col = 0; col < padw; col++)
            dst[bi.biWidth * 3 + col] = 0;

    bi.biSizeImage = (bi.biWidth + padw) * bi.biHeight * 3;

    return true;
}

int main()
{
    std::string input = "input.bmp";
    std::string output = "output.bmp";

    BITMAPFILEHEADER bf = { 0 };
    BITMAPINFOHEADER bi = { sizeof(BITMAPINFOHEADER) };

    std::ifstream fin(input, std::ios::binary);
    if(!fin) return 0;

    fin.read((char*)&bf, sizeof(bf));
    fin.read((char*)&bi, sizeof(bi));

    int size = 3 * (bi.biWidth + 3) * (bi.biHeight + 3);
    char *src = new char[size];
    char *dst = new char[size];

    fin.read(src, bi.biSizeImage);

    //use 0, 90, 180, or 270 for the angle
    if(rotate(src, dst, bi, 270))
    {
        bf.bfSize = 54 + bi.biSizeImage;
        std::ofstream fout(output, std::ios::binary);
        fout.write((char*)&bf, 14);
        fout.write((char*)&bi, 40);
        fout.write((char*)dst, bi.biSizeImage);
    }

    delete[]src;
    delete[]dst;

    return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM