簡體   English   中英

不使用OpenCV函數在C ++中旋轉圖像

[英]Rotate an image in C++ without using OpenCV functions

說明:-我試圖在不使用C ++中使用OpenCV函數的情況下旋轉圖像。 旋轉中心不必是圖像的中心。 這可能是一個不同的點(與圖像中心的偏移)。 到目前為止,我跟着各種各樣的來源做圖像插值和我知道的一個來源 ,其完美地完成這項工作在MATLAB。 我試圖在沒有OpenCV函數的C ++中模仿相同的東西。 但是我沒有得到預期的旋轉圖像。 相反,我的輸出在屏幕上看起來像一條小水平線。

void RotateNearestNeighbor(cv::Mat src, double angle) {
int oldHeight = src.rows;
int oldWidth = src.cols;
int newHeight = std::sqrt(2) * oldHeight;
int newWidth = std::sqrt(2) * oldWidth;
cv::Mat output = cv::Mat(newHeight, newWidth, src.type());
double ctheta = cos(angle);
double stheta = sin(angle);

for (size_t i = 0; i < newHeight; i++) {
    for (size_t j = 0; j < newWidth; j++) {

        int oldRow = static_cast<int> ((i - newHeight / 2) * ctheta +
                                       (j - newWidth / 2) * stheta + oldHeight / 2);
        int oldCol = static_cast<int> (-(i - newHeight / 2) * stheta +
                                       (j - newWidth / 2) * ctheta + oldWidth / 2);

        if (oldRow > 0 && oldCol > 0 && oldRow <= oldHeight && oldCol <= oldWidth)
            output.at<cv::Vec3b>(i, j) = src.at<cv::Vec3b>(oldRow, oldCol);
        else
            output.at<cv::Vec3b>(i, j) = cv::Vec3b(0, 0, 0);
    }
}
cv::imshow("Rotated cat", output);
}

以下是我的輸入(左側)和輸出(右側)圖像

在此處輸入圖片說明


更新:-

在受到與此問題相關的許多答案以及下面最詳盡,最有用和最慷慨的答案的啟發之后,我可以修復我的OpenCV代碼以獲得所需的結果。

修改后的代碼:

// Trivial constant
constexpr double Pi = 3.1415926535897932384626433832795;

/*!
* \brief Function to generate transformation matrix
* \param angle is the angle of rotation from user input
* \param pivot is the amount of translation in x and y axes
* \return translation matrix
*/
cv::Mat CreateTransMat(double angle, std::pair<int, int> &pivot) {
    angle = Pi * angle / 180;
    return (cv::Mat_<double>(3, 3) << cos(angle), -sin(angle), pivot.first,
            sin(angle), cos(angle), pivot.second, 0, 0, 1);
}

/*!
* \brief Function to apply coordinate transform from destination to     source
* \param inv_mat being the inverse transformation matrix for the transform needed
* \return pos being the homogeneous coordinates for transformation
*/
cv::Mat CoordTransform(const cv::Mat &inv_mat, const cv::Mat &pos) {
    assert(inv_mat.cols == pos.rows);
    cv::Mat trans_mat = inv_mat * pos;
    return (cv::Mat_<double>(1, 2) <<
            trans_mat.at<double>(0, 0) / trans_mat.at<double>(0, 2),
            trans_mat.at<double>(0, 1) / trans_mat.at<double>(0, 2));
}

/*!
* \brief Function to transform an image based on a rotation angle and translation
         matrix. When rotation and translation happen at the same time, the
         two matrices can be combined
* \param src being source image
* \param dest being destination image
* \param trans_mat being the transformation (rotation/ translation) matrix
*/
void ImageTransform(const cv::Mat &src, const cv::Mat &trans_mat, cv::Mat &dest) {
    int src_rows = src.rows;
    int src_cols = src.cols;
    int dest_rows = dest.rows;
    int dest_cols = dest.cols;
    const cv::Mat inverse_mat = trans_mat.inv();
    //#pragma omp parallel for simd
    for (int row = 0; row < dest_rows; row++) {
        //#pragma omp parallel for simd
        for (int col = 0; col < dest_cols; col++) {
            cv::Mat src_pos = CoordTransform(inverse_mat,
                                         (cv::Mat_<double>(3, 1) << col, row, 1));
            const int x_actual = static_cast<int>(src_pos.at<double>(0, 0) + 0.5);
            const int y_actual = static_cast<int>(src_pos.at<double>(0, 1) + 0.5);

            if (x_actual >= 0 && x_actual < src_cols &&
                y_actual >= 0 && y_actual < src_rows)
                dest.at<cv::Vec3b>(row, col) = src.at<cv::Vec3b>(y_actual, x_actual);
            else
                dest.at<cv::Vec3b>(row, col) = cv::Vec3b(0, 0, 0);
        }
    }    
}

/*!
* \brief User manual for command-line args input
*/
void Usage() {
    std::cout << "COMMAND INPUT : - \n\n" <<
              "          ./ImageTransform <image> <rotation-angle>" <<
              std::endl;
}
/*!
* \brief main function to read a user input location for an image and then apply the
         required transformations (rotation / translation)
*/
int main(int argc, char *argv[])
{
    auto start = std::chrono::steady_clock::now();
    if (argc == 0 || argc < 3)
        Usage();
    else {
        double degree = std::stod(argv[2]);
        double angle = degree * CV_PI / 180.;
        cv::Mat src_img = cv::imread(argv[1]);
        std::pair<int, int> null_trans = std::make_pair(0, 0);
        std::pair<int, int> translation_initial =
            std::make_pair(src_img.cols / 2 + 1, src_img.rows / 2 + 1);
        std::pair<int, int> translation_final =
            std::make_pair(0, -src_img.rows / 2 - 4);
        if (!src_img.data)
        {
            std::cout << "image null" << std::endl;
            cv::waitKey(0);
        }
        cv::imshow("Source", src_img);
        cv::Mat dest_img = cv::Mat(static_cast<int>(2 * src_img.rows),
                                   static_cast<int>(2 * src_img.cols),
                                   src_img.type());
        cv::Mat trans_mat1 = CreateTransMat(degree, translation_initial);
        ImageTransform(src_img, trans_mat1, dest_img);
        cv::imshow("Interim", dest_img);
        cv::Mat interim_img = dest_img;
        dest_img.release();
        dest_img = cv::Mat(src_img.rows, src_img.cols, src_img.type());
        cv::Mat trans_mat2 = CreateTransMat(0, translation_final);
        ImageTransform(interim_img, trans_mat2, dest_img);
        cv::imshow("Final image", dest_img);
        cv::waitKey(10);
    }
    auto end = std::chrono::steady_clock::now();
    auto diff = end - start;
    std::cout << std::chrono::duration <double, std::milli> (diff).count() <<
              " ms" << std::endl;
}

輸入圖像

旋轉前的原始圖像

旋轉影像

旋轉后的圖像

首先,我必須承認我同意generic_opto_guy

使用循環的方法看起來不錯,因此我們需要檢查數學。 在我注意到的事情上:如果(oldRow> 0 && oldCol> 0 && oldRow <= oldHeight && oldCol <= oldWidth)表示您從1開始索引。我相信opencv從0開始索引。

對於所有這些,我都忍不住回答。 (可能是,這只是我的圖像階段。)

我建議不要使用sin()和cos(),而是建議使用矩陣變換。 乍一看,這可能看起來是過度設計的,但是稍后您將意識到它具有更大的靈活性。 使用轉換矩陣,您可以表達很多轉換(平移,旋轉,縮放,剪切,投影),以及將多個轉換組合到一個矩陣中。

(一個可能的預告片: SO:如何在2D中繪制/變形QImage?

在圖像中,像素可以由2d坐標尋址。 因此,想到了2×2矩陣,但是2×2矩陣無法表達翻譯。 為此,引入了齊次坐標 -通過將尺寸擴展一倍來處理同一空間中的位置和方向的數學技巧。

簡而言之,二維位置(x,y)具有齊次坐標(x,y,1)。

用轉換矩陣轉換的位置:

= M · v

這可能會或可能不會更改第三部分的值。 為了再次將齊次坐標轉換為2D位置,x和y必須除以3rd分量。

Vec2 transform(const Mat3x3 &mat, const Vec2 &pos)
{
  const Vec3 pos_ = mat * Vec3(pos, 1.0);
  return Vec2(pos_.x / pos_.z, pos_.y / pos_.z);
}

要將源圖像轉換為目標圖像,可以使用以下功能:

void transform(
  const Image &imgSrc, const Mat3x3 &mat, Image &imgDst,
  int rgbFail = 0x808080)
{
  const Mat3x3 matInv = invert(mat);
  for (int y = 0; y < imgDst.h(); ++y) {
    for (int x = 0; x < imgDst.w(); ++x) {
      const Vec2 pos = transform(matInv, Vec2(x, y));
      const int xSrc = (int)(pos.x + 0.5), ySrc = (int)(pos.y + 0.5);
      imgDst.setPixel(x, y,
        xSrc >= 0 && xSrc < imgSrc.w() && ySrc >= 0 && ySrc < imgSrc.h()
        ? imgSrc.getPixel(xSrc, ySrc)
        : rgbFail);
    }
  }
}

注意:

變換矩陣mat描述了源圖像坐標到目標圖像坐標的變換。 嵌套循環遍歷目標圖像。 因此,必須使用逆矩陣(代表逆變換)來獲取映射到當前目標坐標的相應源圖像坐標。

…以及旋轉的矩陣構造函數:

enum ArgInitRot { InitRot };

template <typename VALUE>
struct Mat3x3T {
  union {
    VALUE comp[3 * 3];
    struct {
      VALUE _00, _01, _02;
      VALUE _10, _11, _12;
      VALUE _20, _21, _22;
    };
  };

  // constructor to build a matrix for rotation
  Mat3x3T(ArgInitRot, VALUE angle):
    _00(std::cos(angle)), _01(-std::sin(angle)), _02((VALUE)0),
    _10(std::sin(angle)), _11( std::cos(angle)), _12((VALUE)0),
    _20(       (VALUE)0), _21(        (VALUE)0), _22((VALUE)1)
  { }

可用於構造一個angle旋轉(以度為單位):

Mat3x3T<double> mat(InitRot, degToRad(30.0));

注意:

我想強調一下如何使用轉換后的坐標:

      const Vec2 pos = transform(matInv, Vec2(x, y));
      const int xSrc = (int)(pos.x + 0.5), ySrc = (int)(pos.y + 0.5);

將結果取整以產生一個離散像素位置實際上就是所謂的“最近鄰居”。 可替代地,現在丟棄的小數部分可以用於相鄰像素之間的線性插值。


為了制作一個小樣本,我首先從我最近寫的另一個答案中復制了image.himage.ccimagePPM.himagePPM.cc (已使用PPM文件格式,因為它只需要用於文件I / O的最少代碼。)

接下來,我使用linMath.h (我的3D轉換最小數學集合)為2D轉換創建最小數學集合– linMath.h

#ifndef LIN_MATH_H
#define LIN_MATH_H

#include <iostream>
#include <cassert>
#include <cmath>

extern const double Pi;

template <typename VALUE>
inline VALUE degToRad(VALUE angle)
{
  return (VALUE)Pi * angle / (VALUE)180;
}

template <typename VALUE>
inline VALUE radToDeg(VALUE angle)
{
  return (VALUE)180 * angle / (VALUE)Pi;
}

enum ArgNull { Null };

template <typename VALUE>
struct Vec2T {
  typedef VALUE Value;
  Value x, y;
  // default constructor (leaving elements uninitialized)
  Vec2T() { }
  Vec2T(ArgNull): x((Value)0), y((Value)0) { }
  Vec2T(Value x, Value y): x(x), y(y) { }
};

typedef Vec2T<float> Vec2f;
typedef Vec2T<double> Vec2;

template <typename VALUE>
struct Vec3T {
  typedef VALUE Value;
  Value x, y, z;
  // default constructor (leaving elements uninitialized)
  Vec3T() { }
  Vec3T(ArgNull): x((Value)0), y((Value)0), z((Value)0) { }
  Vec3T(Value x, Value y, Value z): x(x), y(y), z(z) { }
  Vec3T(const Vec2T<Value> &xy, Value z): x(xy.x), y(xy.y), z(z) { }
  explicit operator Vec2T<Value>() const { return Vec2T<Value>(x, y); }
  const Vec2f xy() const { return Vec2f(x, y); }
  const Vec2f xz() const { return Vec2f(x, z); }
  const Vec2f yz() const { return Vec2f(y, z); }
};

typedef Vec3T<float> Vec3f;
typedef Vec3T<double> Vec3;

enum ArgInitIdent { InitIdent };
enum ArgInitTrans { InitTrans };
enum ArgInitRot { InitRot };
enum ArgInitScale { InitScale };
enum ArgInitFrame { InitFrame };

template <typename VALUE>
struct Mat3x3T {
  union {
    VALUE comp[3 * 3];
    struct {
      VALUE _00, _01, _02;
      VALUE _10, _11, _12;
      VALUE _20, _21, _22;
    };
  };

  // default constructor (leaving elements uninitialized)
  Mat3x3T() { }
  // constructor to build a matrix by elements
  Mat3x3T(
    VALUE _00, VALUE _01, VALUE _02,
    VALUE _10, VALUE _11, VALUE _12,
    VALUE _20, VALUE _21, VALUE _22):
    _00(_00), _01(_01), _02(_02),
    _10(_10), _11(_11), _12(_12),
    _20(_20), _21(_21), _22(_22)
  { }
  // constructor to build an identity matrix
  Mat3x3T(ArgInitIdent):
    _00((VALUE)1), _01((VALUE)0), _02((VALUE)0),
    _10((VALUE)0), _11((VALUE)1), _12((VALUE)0),
    _20((VALUE)0), _21((VALUE)0), _22((VALUE)1)
  { }
  // constructor to build a matrix for translation
  Mat3x3T(ArgInitTrans, const Vec2T<VALUE> &t):
    _00((VALUE)1), _01((VALUE)0), _02((VALUE)t.x),
    _10((VALUE)0), _11((VALUE)1), _12((VALUE)t.y),
    _20((VALUE)0), _21((VALUE)0), _22((VALUE)1)
  { }
  // constructor to build a matrix for rotation
  Mat3x3T(ArgInitRot, VALUE angle):
    _00(std::cos(angle)), _01(-std::sin(angle)), _02((VALUE)0),
    _10(std::sin(angle)), _11( std::cos(angle)), _12((VALUE)0),
    _20(       (VALUE)0), _21(        (VALUE)0), _22((VALUE)1)
  { }
  // constructor to build a matrix for translation/rotation
  Mat3x3T(ArgInitFrame, const Vec2T<VALUE> &t, VALUE angle):
    _00(std::cos(angle)), _01(-std::sin(angle)), _02((VALUE)t.x),
    _10(std::sin(angle)), _11( std::cos(angle)), _12((VALUE)t.y),
    _20(       (VALUE)0), _21(        (VALUE)0), _22((VALUE)1)
  { }
  // constructor to build a matrix for scaling
  Mat3x3T(ArgInitScale, VALUE sx, VALUE sy):
    _00((VALUE)sx), _01( (VALUE)0), _02((VALUE)0),
    _10( (VALUE)0), _11((VALUE)sy), _12((VALUE)0),
    _20( (VALUE)0), _21( (VALUE)0), _22((VALUE)1)
  { }
  // operator to allow access with [][]
  VALUE* operator [] (int i)
  {
    assert(i >= 0 && i < 3);
    return comp + 3 * i;
  }
  // operator to allow access with [][]
  const VALUE* operator [] (int i) const
  {
    assert(i >= 0 && i < 3);
    return comp + 3 * i;
  }

  // multiply matrix with matrix -> matrix
  Mat3x3T operator * (const Mat3x3T &mat) const
  {
    return Mat3x3T(
      _00 * mat._00 + _01 * mat._10 + _02 * mat._20,
      _00 * mat._01 + _01 * mat._11 + _02 * mat._21,
      _00 * mat._02 + _01 * mat._12 + _02 * mat._22,
      _10 * mat._00 + _11 * mat._10 + _12 * mat._20,
      _10 * mat._01 + _11 * mat._11 + _12 * mat._21,
      _10 * mat._02 + _11 * mat._12 + _12 * mat._22,
      _20 * mat._00 + _21 * mat._10 + _22 * mat._20,
      _20 * mat._01 + _21 * mat._11 + _22 * mat._21,
      _20 * mat._02 + _21 * mat._12 + _22 * mat._22);
  }
  // multiply matrix with vector -> vector
  Vec3T<VALUE> operator * (const Vec3T<VALUE> &vec) const
  {
    return Vec3T<VALUE>(
      _00 * vec.x + _01 * vec.y + _02 * vec.z,
      _10 * vec.x + _11 * vec.y + _12 * vec.z,
      _20 * vec.x + _21 * vec.y + _22 * vec.z);
  }
};

typedef Mat3x3T<float> Mat3x3f;
typedef Mat3x3T<double> Mat3x3;

template <typename VALUE>
std::ostream& operator<<(std::ostream &out, const Mat3x3T<VALUE> &m)
{
  return out
    << m._00 << '\t' << m._01 << '\t' << m._02 << '\n'
    << m._10 << '\t' << m._11 << '\t' << m._12 << '\n'
    << m._20 << '\t' << m._21 << '\t' << m._22 << '\n';
}

/* computes determinant of a matrix.
 *
 * det = |M|
 *
 * mat ... the matrix
 */
template <typename VALUE>
VALUE determinant(const Mat3x3T<VALUE> &mat)
{
  return mat._00 * mat._11 * mat._22
    + mat._01 * mat._12 * mat._20
    + mat._02 * mat._10 * mat._21
    - mat._20 * mat._11 * mat._02
    - mat._21 * mat._12 * mat._00
    - mat._22 * mat._10 * mat._01;
}

/* returns the inverse of a regular matrix.
 *
 * mat matrix to invert
 * eps epsilon for regularity of matrix
 */
template <typename VALUE>
Mat3x3T<VALUE> invert(
  const Mat3x3T<VALUE> &mat, VALUE eps = (VALUE)1E-10)
{
  assert(eps >= (VALUE)0);
  // compute determinant and check that it its unequal to 0
  // (Otherwise, matrix is singular!)
  const VALUE det = determinant(mat);
  if (std::abs(det) < eps) throw std::domain_error("Singular matrix!");
  // reciproke of determinant
  const VALUE detInvPos = (VALUE)1 / det, detInvNeg = -detInvPos;
  // compute each element by determinant of sub-matrix which is build
  // striking out row and column of pivot element itself
  // BTW, the determinant is multiplied with -1 when sum of row and column
  // index is odd (chess board rule)
  // (This is usually called cofactor of related element.)
  // transpose matrix and multiply with 1/determinant of original matrix
  return Mat3x3T<VALUE>(
    detInvPos * (mat._11 * mat._22 - mat._12 * mat._21),
    detInvNeg * (mat._01 * mat._22 - mat._02 * mat._21),
    detInvPos * (mat._01 * mat._12 - mat._02 * mat._11),
    detInvNeg * (mat._10 * mat._22 - mat._12 * mat._20),
    detInvPos * (mat._00 * mat._22 - mat._02 * mat._20),
    detInvNeg * (mat._00 * mat._12 - mat._02 * mat._10),
    detInvPos * (mat._10 * mat._21 - mat._11 * mat._20),
    detInvNeg * (mat._00 * mat._21 - mat._01 * mat._20),
    detInvPos * (mat._00 * mat._11 - mat._01 * mat._10));
}

#endif // LIN_MATH_H

以及linMath.ccPi的定義:

#include "linmath.h"

const double Pi = 3.1415926535897932384626433832795;

在擁有所有可用工具的情況下,我制作了示例應用程序xformRGBImg.cc

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>

#include "linMath.h"
#include "image.h"
#include "imagePPM.h"

typedef unsigned int uint;

struct Error {
  const std::string text;
  Error(const char *text): text(text) { }
};

const char* readArg(int &i, int argc, char **argv)
{
  ++i;
  if (i >= argc) throw Error("Missing argument!");
  return argv[i];
}

uint readArgUInt(int &i, int argc, char **argv)
{
  const char *arg = readArg(i, argc, argv); char *end;
  const unsigned long value = strtoul(arg, &end, 0);
  if (arg == end || *end) throw Error("Unsigned integer value expected!");
  if ((uint)value != value) throw Error("Unsigned integer overflow!");
  return (uint)value;
}

double readArgDouble(int &i, int argc, char **argv)
{
  const char *arg = readArg(i, argc, argv); char *end;
  const double value = strtod(arg, &end);
  if (arg == end || *end) throw Error("Floating point value expected!");
  return value;
}

std::pair<uint, uint> resize(int &i, int argc, char **argv)
{
  const uint w = readArgUInt(i, argc, argv);
  const uint h = readArgUInt(i, argc, argv);
  return std::make_pair(w, h);
}

Mat3x3 translate(int &i, int argc, char **argv)
{
  const double x = readArgDouble(i, argc, argv);
  const double y = readArgDouble(i, argc, argv);
  return Mat3x3(InitTrans, Vec2(x, y));
}

Mat3x3 rotate(int &i, int argc, char **argv)
{
  const double angle = readArgDouble(i, argc, argv);
  return Mat3x3(InitRot, degToRad(angle));
}

Mat3x3 scale(int &i, int argc, char **argv)
{
  const double x = readArgDouble(i, argc, argv);
  const double y = readArgDouble(i, argc, argv);
  return Mat3x3(InitScale, x, y);
}

Vec2 transform(const Mat3x3 &mat, const Vec2 &pos)
{
  const Vec3 pos_ = mat * Vec3(pos, 1.0);
  return Vec2(pos_.x / pos_.z, pos_.y / pos_.z);
}

void transform(
  const Image &imgSrc, const Mat3x3 &mat, Image &imgDst,
  int rgbFail = 0x808080)
{
  const Mat3x3 matInv = invert(mat);
  for (int y = 0; y < imgDst.h(); ++y) {
    for (int x = 0; x < imgDst.w(); ++x) {
      const Vec2 pos = transform(matInv, Vec2(x, y));
      const int xSrc = (int)(pos.x + 0.5), ySrc = (int)(pos.y + 0.5);
      imgDst.setPixel(x, y,
        xSrc >= 0 && xSrc < imgSrc.w() && ySrc >= 0 && ySrc < imgSrc.h()
        ? imgSrc.getPixel(xSrc, ySrc)
        : rgbFail);
    }
  }
}

const char *const usage =
  "Usage:\n"
  "  xformRGBImg IN_FILE OUT_FILE [[CMD]...]\n"
  "\n"
  "Commands:\n"
  "  resize W H\n"
  "  translate X Y\n"
  "  rotate ANGLE\n"
  "  scale SX SY\n";

int main(int argc, char **argv)
{
  // read command line arguments
  if (argc <= 2) {
    std::cerr << "Missing arguments!\n";
    std::cout << usage;
    return 1;
  }
  const std::string inFile = argv[1];
  const std::string outFile = argv[2];
  std::pair<uint, uint> sizeOut(0, 0);
  Mat3x3 mat(InitIdent);
  for (int i = 3; i < argc; ++i) try {
    const std::string cmd = argv[i];
    if (cmd == "resize") sizeOut = resize(i, argc, argv);
    else if (cmd == "translate") mat = translate(i, argc, argv) * mat;
    else if (cmd == "rotate") mat = rotate(i, argc, argv) * mat;
    else if (cmd == "scale") mat = scale(i, argc, argv) * mat;
    else {
      std::cerr << "Wrong command!\n";
      std::cout << usage;
      return 1;
    }
  } catch (const Error &error) {
    std::cerr << "Wrong argument at $" << i << "\n"
      << error.text << '\n';
    std::cout << usage;
    return 1;
  }
  // read image
  Image imgSrc;
  { std::ifstream fIn(inFile.c_str(), std::ios::binary);
    if (!readPPM(fIn, imgSrc)) {
      std::cerr << "Reading '" << inFile << "' failed!\n";
      return 1;
    }
  }
  // set output image size
  if (sizeOut.first * sizeOut.second == 0) {
    sizeOut = std::make_pair(imgSrc.w(), imgSrc.h());
  }
  // transform image
  Image imgDst;
  imgDst.resize(sizeOut.first, sizeOut.second, 3 * sizeOut.second);
  transform(imgSrc, mat, imgDst);
  // write image
  { std::ofstream fOut(outFile.c_str(), std::ios::binary);
    if (!writePPM(fOut, imgDst) || (fOut.close(), !fOut.good())) {
      std::cerr << "Writing '" << outFile << "' failed!\n";
      return 1;
    }
  }
  // done
  return 0;
}

注意:

命令行參數按順序處理。 從單位矩陣開始,將每個轉換命令從左向已組合的轉換矩陣相乘。 這是因為變換的串聯導致矩陣的逆序乘法。 (矩陣乘法是右關聯的。)

例如,用於轉換的相應矩陣:

x' = 翻譯x
x“ = 旋轉x'
x“' = 比例尺x”

這是

x“' = 比例尺旋轉平移x )))

M 變換 = M 比例 · M 旋轉 · M 平移

x“' = M 比例 · M 旋轉 · M 平移 · x = M 變換 · x

cygwin中編譯和測試:

$ g++ -std=c++11 -o xformRGBImg image.cc imagePPM.cc linMath.cc xformRGBImg.cc

$ ./xformRGBImg                                                               
Missing arguments!
Usage:
  xformRGBImg IN_FILE OUT_FILE [[CMD]...]

Commands:
  resize W H
  translate X Y
  rotate ANGLE
  scale SX SY

$

最后,生成一個樣本圖像cat.jpg (在GIMP中轉換為PPM ):

原始圖像目錄ppm

尺寸為300×300。

注意:

所有嵌入的圖像都從PPM轉換為JPEG(再次在GIMP中 )。 (圖片上傳不支持PPM,我也無法想象任何瀏覽器都能正確顯示它。)

首先開始:

$ ./xformRGBImg cat.ppm cat.copy.ppm

$

身份轉換圖像cat.copy.ppm

看起來很原始-身份轉換應該期望什么。

現在,旋轉30°:

$ ./xformRGBImg cat.ppm cat.rot30.ppm rotate 30

$

旋轉30°圖像時旋轉rot30.ppm

要繞某個中心旋轉,有一個響應。 之前和之后需要的翻譯:

$ ./xformRGBImg cat.ppm cat.rot30c150,150.ppm \
  translate -150 -150 rotate 30 translate 150 150

$

圍繞中心(150,150)圖像旋轉30°圖像類別:rot30c150,150.ppm

可以使用w·√2×h·√2調整輸出圖像的大小以適合任何中心旋轉。

因此,將輸出圖像的大小調整為425×425,在此位置上一次轉換分別被調整為translate 212.5 212.5

$ ./xformRGBImg cat.ppm cat.rot30c150,150.425x425.ppm \
  resize 425 425 translate -150 -150 rotate 30 translate 212.5 212.5

$

以中心(150,150)為中心旋轉30°進入調整大小的圖像目錄.rot30c150,150.425x425.ppm

縮放比例尚未檢查:

$ ./xformRGBImg cat.ppm cat.rot30c150,150s0.7,0.7.ppm \
  translate -150 -150 rotate 30 scale 0.7 0.7 translate 150 150

$

繞中心點(150,150)旋轉30°,並使用0.7圖像進行均勻縮放。類別:rot30c150,150s0.7,0.7.ppm]


最后,為了公平起見,我想提到我的小玩具工具的“老大哥”: ImageMagick

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM