0

EasyBMP を使用して bmp 画像を回転させようとしています。角度が 0 ~ 90 または 270 ~ 360 の場合、回転は問題ありません。しかし、180 から 270 の間で境界長方形が引き伸ばされ、角度が 90 から 180 の間の場合、セグメンテーション エラーが発生します。問題の原因は次のとおりであると確信しています。

int width = image.TellWidth();
int height = image.TellHeight();

float sine= sin(angle);
float cosine=cos(angle);

float x1=-height*sine;
float y1=height*cosine;
float x2=width*cosine-height*sine;
float y2=height*cosine+width*sine;
float x3=width*cosine;
float y3=width*sine;

float minx=min(0,min(x1,min(x2,x3)));
float miny=min(0,min(y1,min(y2,y3)));
float maxx=max(x1,max(x2,x3));
float maxy=max(y1,max(y2,y3));

int outWidth;
int outHeight;


     outWidth=(int)ceil(fabs(maxx)-minx);
     outHeight=(int)ceil(fabs(maxy)-miny);
output.SetSize(outHeight,outWidth);

for(int x=0; x<outWidth; x++)
{
    for(int y=0; y<outHeight; y++)
    {
        int srcX=(int)((x+minx)*cosine+(y+miny)*sine); 
            int srcY=(int)((y+miny)*cosine-(x+minx)*sine);
        if(srcX>=0 &&srcX<width && srcY>=0 && srcY<height)
        {
            output.SetPixel(x,y,image.GetPixel(srcX,srcY));
        }
    }
}
4

1 に答える 1

0

以下は、私がこれを解決した方法です。TL;DR: 回転変換は 0, 0 を中心に行われるため、画像座標が 0,0 を左下に設定している場合は、最初に画像を 0,0 の中心に移動する必要があります。また、sin と cos は度ではなくラジアンを想定しているため、最初に変換することを忘れないでください。

長い道のり: 私は、問題がどこで起こっているのかを見つけるために、答えを簡単に検証できる単純なプログラムを作成することから始めました。

最初に気付いたのは、90.0f では何も出力されないということでした。それは奇妙に思えたので、「出力イメージ サイズ」printf に割り込んで、出力の高さが -87 として計算されていることに気付きました。明らかにそれは正しくないので、なぜそうなるのか見てみましょう。

少し上に行くoutHeight=(int)ceil(fabs(maxy)-miny);ので、maxy と miny を差し引いたときに出力の高さが負になる理由を考えてみましょう。maxy は -0.896... で、miny は 88.503 のように見えます... しかし、maxy の絶対値は miny を引く前に取られます。つまり、0.896 - 88.503 になります。おっと、それは良くありません。引き算をしてから絶対値を取ってみましょう。

幅と高さの両方をそのまま再コンパイル: outWidth=(int)ceil(fabs(maxx-minx)); outHeight=(int)ceil(fabs(maxy-miny));

はるかに優れた値を取得します。現在、outWidth と outHeight はそれぞれ 2 と 90 です。これは大幅に改善されましたが、高さは 100 にする必要があります。これについては後で説明します。

数学のどこが間違っているのかを理解するために、x と x、y と y のように用語を再編成します。次に、スペースを調整し、かっこを追加して、読みやすくし、操作の順序を確保しました (OoO テーブルを見ようとするのは確実です ;))。回転行列の乗算を分解しているのは明らかなので、x1、x2 などよりももう少し直感的な名前を変数に付けます。これからは、x1 は topLeftTransformedX、x2 は topRightTransformedX、x3 は bottomLeftTransformedX として存在します。 (常に 0)、x4 は bottomRightTransformedX になります。Y も同じです。

これを使用すると、この時点で、あなたが行うのと同じことがわかります...そして、このよりクリーンなコードから見た数字に基づいて、何かを思い出しました(あなたのものと同じ数学ですが、デバッグはまだ簡単です)。

突然、X の計算は次のようになります。 // x = x cos - y sin float topLeftTransformedX = (-midX * cosine) - (midY * sine); float topRightTransformedX = (midX * コサイン) - (midY * サイン); float bottomLeftTransformedX = (-midX * コサイン) - (-midY * サイン); float bottomRightTransformedX = (midX * コサイン) - (-midY * サイン);

回転行列は中心点を中心に回転します。適切に回転させるには、画像をその中心に移動する必要があります。

次に、これが値を与える理由を理解しようとすると、別のことを思い出しました-角度はラジアンである必要があります。

突然、ほとんどすべてが機能します。やるべきことはまだいくつかありますが、これで 95% 以上は達成できるはずです。それが役に立てば幸い!

// bmprotate.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <math.h>

#define min(x,y) x < y ? x : y
#define max(x,y) x > y ? x : y
#define PI 3.14159

void rotate(int width, int height, float angleInDeg)
{
    float angle = angleInDeg * (PI/180.0f);
    float midX = ((float)width) / 2.0f;
    float midY = ((float)height) / 2.0f;

    float sine = sin(angle);
    float cosine = cos(angle);

    // x = x cos - y sin
    float topLeftTransformedX = (-midX * cosine) - (midY * sine);
    float topRightTransformedX = (midX * cosine) - (midY * sine);
    float bottomLeftTransformedX  = (-midX * cosine) - (-midY * sine);
    float bottomRightTransformedX = (midX * cosine) - (-midY * sine);

    float minx = min( topLeftTransformedX, min(topRightTransformedX, min(bottomLeftTransformedX, bottomRightTransformedX)) );
    float maxx = max( topLeftTransformedX, max(topRightTransformedX, max(bottomLeftTransformedX, bottomRightTransformedX)) );

    // y = x sin + y cos
    float topLeftTransformedY = (-midX * sine) + (midY * cosine);
    float topRightTransformedY = (midX * sine) + (midY * cosine);
    float bottomLeftTransformedY  = (-midX * sine) + (-midY * cosine);
    float bottomRightTransformedY = (midX * sine) + (-midY * cosine);



    float miny = min( topLeftTransformedY, min(topRightTransformedY, min(bottomLeftTransformedY, bottomRightTransformedY)) );
    float maxy = max( topLeftTransformedY, max(topRightTransformedY, max(bottomLeftTransformedY, bottomRightTransformedY)) );

    int outWidth;
    int outHeight;

    printf("(%f,%f) , (%f,%f) , (%f,%f) , (%f,%f)\n", 
        topLeftTransformedX, topLeftTransformedY, 
        topRightTransformedX, topRightTransformedY, 
        bottomLeftTransformedX, bottomLeftTransformedY, 
        bottomRightTransformedX, bottomRightTransformedY);


    outWidth = (int) ceil( fabs(maxx) + fabs(minx));
    outHeight = (int) ceil( fabs(maxy) + fabs(miny) );

    printf("output image size: (%d,%d)\n",outWidth,outHeight);
    for(int x=0; x<outWidth; x++)
    {
        for(int y=0; y<outHeight; y++)
        {
            int srcX=(int)((x+minx)*cosine+(y+miny)*sine); 
            int srcY=(int)((y+miny)*cosine-(x+minx)*sine);

            if(srcX >=0 && srcX < width && srcY >= 0 && srcY < height)
            {
                printf("(x,y) = (%d,%d)\n",srcX, srcY);
            }
        }
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    rotate(100,2,90.0f);
    for (int i = 0; i < 360; i++)
    {

    }
    return 0;
}
于 2012-06-14T02:20:38.753 に答える