C ++:BMP旋转图像

时间:2016-11-18 13:02:57

标签: c++ image rotation

好的,这是我第三次发布相同的问题(以前是herehere)。

现在我将尝试解释我的问题:

  1. 首先,我需要旋转一个 .bmp 图像并且它不能正确旋转。但我不需要旋转扩展名为 .bmp 的随机图片,我需要旋转this one。我已尝试过许多其他图像,除了我的所有图像都旋转正确。
  2. 在这一刻,我的代码只适用于 180度,怎么能让它适用于任何90度的倍数(我需要用 90,180或270度,而不是更多)。
  3. 此代码不需要任何类型的外部库,如 CImage OpenCV ImageMagik 等等......我需要使这段代码有效。
  4. 所以,是的,就是这样。在这里你可以找到我的实际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;
    }
    

2 个答案:

答案 0 :(得分:1)

BMP文件格式是一个复杂的,复杂的野兽,并没有“简单”的BMP文件阅读器。你在那里的代码对你试图读取的文件(24bpp真彩色,紧密压缩,没有压缩)做出某些硬编码假设,当它遇到任何不是特定格式的东西时,它会变平(在它的脸上) 。不幸的是,对你来说,那里的大部分BMP文件都不是那种。为了让您了解完全符合BMP读卡器必须支持的内容,请查看此页面:

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

你在那里的代码甚至没有检查是否有有效的文件魔术字节签名以及标头是否有效。那就是你的问题:你没有BMP文件阅读器。如果你足够幸运的话,你有一些实际上吐出像素的东西,那么偶然的东西恰好是正确的格式。

答案 1 :(得分:1)

您有重大内存泄漏。必须释放pixels = new unsigned char[size];,否则每次轮换都可能泄漏几兆字节。您必须重写该函数以跟踪内存分配。

当您将图像旋转90或270图像时,图像的宽度/高度会发生变化。由于填充,大小也可能会发生变化。新维度必须记录在头文件中。

在C ++中,您可以使用fopen,但首选std::fstream

以下示例仅适用于Windows中的24位图像。在Big-endian系统中,您不能按照我在下面使用它的方式使用memcpy

注意,这仅适用于练习。正如@datenwolf所解释的那样,你应该使用库来实际应用。大多数标准库如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;
}