2018-10-28 23:02:40 Aidam_Bo 阅读数 655
• ###### c++入门到精通教程 c++11/14/17

本教程适合那些只有一点点c语言编程知识的新手，也适合那些c++98标准已经掌握的不错但对c++11/14/17新标准基本无所知的c++开发老手，欢迎大家尽早加入学习，请大家从授课目录，讲解课程时长、演示范例详尽程度，试听后收获四个方面评估本教程的质量。

7345 人正在学习 去看看 王健伟

``````//添加椒盐噪声
void salt(Mat& src,int number)
{
for (int i = 0; i < number; i++)
{
int r = static_cast<int>(rng.uniform(0, src.rows));
int c = static_cast<int>(rng.uniform(0, src.cols));
int k = (static_cast<int>(rng.uniform(0, 1000))&1);
if(k==1)
src.at<uchar>(r, c) = 255;
else
src.at<uchar>(r, c) = 0;
}
return;
}
``````
``````/*
* @ drt ：高斯方差
* @ Medium ：高斯均值
*/
int Get_Gauss(int Medium, int drt)
{
//产生高斯样本，以U为均值，D为均方差
double sum = 0;
for (int i = 0; i<12; i++)
sum += rand() / 32767.00;
//计算机中rand()函数为－32767～＋32767（2^15-1）
//故sum＋为0～1之间的均匀随机变量
return int(Medium + drt*(sum - 6));
//产生均值为U，标准差为D的高斯分布的样本，并返回
}
/*
* variance ：高斯噪声的方差
*/``````

``````//添加高斯噪声
void ImgAddGaussNoise1( uchar * dstImgbuff, int srcwith, int srcheigh, int chanels)
{
assert( srcwith > 0 && srcheigh > 0);
int bytecount = srcwith * srcheigh * chanels;

for (size_t i = 0; i < bytecount; i++)
{
int  iTemp = dstImgbuff[i] + Get_Gauss(0, 20);
iTemp = iTemp > 255 ? 255 : iTemp;
iTemp = iTemp < 0 ? 0 : iTemp;
dstImgbuff[i] = iTemp;
}
}``````

``````//均值求取
void Meanvalue(Mat* src, int indexrows, int indexcols, float* meanv, int ker)
{
int lo = (ker - 1) / 2;
float total = 0;
for (int i = indexrows - lo; i <= indexrows + lo; i++)
{
for (int j = indexcols - lo; j <= indexcols + lo; j++)
{
total += src->at<uchar>(i, j);
}
}
*meanv = total / (ker * ker);
return;
}
``````
``````//中值求取
void Media(Mat* src, int indexrows, int indexcols, int* meanv, int ker)
{
int lo = (ker - 1) / 2;
vector<int>moreo;
for (int i = indexrows - lo; i <= indexrows + lo; i++)
{
for (int j = indexcols - lo; j <= indexcols + lo; j++)
{
moreo.push_back(src->at<uchar>(i, j));
}
}
sort(moreo.begin(), moreo.end());
*meanv = moreo.at(ker * ker / 2);
return;
}``````

``````//局部方差求取
void Vvalue(Mat* src, int indexrows, int indexcols, int* vall, int ker, float mean)
{
int lo = (ker - 1) / 2;
float total = 0;
for (int i = indexrows - lo; i <= indexrows + lo; i++)
{
for (int j = indexcols - lo; j <= indexcols + lo; j++)
{
total += pow((src->at<uchar>(i, j) - mean), 2);
}
}
*vall = static_cast<int>(total);
return;
}``````

``````//像素方差
void Variance(Mat& src, vector<test>& hierachy, int ker)
{
int row = src.rows;
int col = src.cols;
int lo = (ker - 1) / 2;
for (int ir = lo; ir < row - lo; ir++)
{
for (int jc = lo; jc < col - lo; jc++)
{
float means;
int var;
//计算均值
Meanvalue(&src, ir, jc, &means, ker);
Vvalue(&src, ir, jc, &var, ker, means);
test temp;
temp.menval = var;
temp.x = ir;
temp.y = jc;
hierachy.push_back(temp);
}
}
return;
}``````

``````//STL排序方式
bool SortByM1(const test &v1, const test &v2)//注意：本函数的参数的类型一定要与vector中元素的类型一致
{
return v1.menval < v2.menval;//升序排列
}``````

``````//SSIM 结构相似比
Scalar getMSSIM(const Mat& i1, const Mat& i2)
{
const double C1 = 6.5025, C2 = 58.5225;
/***************************** INITS **********************************/
int d = CV_32F;

Mat I1, I2;
i1.convertTo(I1, d);           // cannot calculate on one byte large values
i2.convertTo(I2, d);

int num = I1.channels();
//cv::imshow("123", I1);
//cv::waitKey();

Mat I2_2 = I2.mul(I2);        // I2^2
Mat I1_2 = I1.mul(I1);        // I1^2
Mat I1_I2 = I1.mul(I2);        // I1 * I2

/*************************** END INITS **********************************/

Mat mu1, mu2;   // PRELIMINARY COMPUTING
GaussianBlur(I1, mu1, Size(11, 11), 1.5);
GaussianBlur(I2, mu2, Size(11, 11), 1.5);

Mat mu1_2 = mu1.mul(mu1);
Mat mu2_2 = mu2.mul(mu2);
Mat mu1_mu2 = mu1.mul(mu2);

Mat sigma1_2, sigma2_2, sigma12;

GaussianBlur(I1_2, sigma1_2, Size(11, 11), 1.5);
sigma1_2 -= mu1_2;

GaussianBlur(I2_2, sigma2_2, Size(11, 11), 1.5);
sigma2_2 -= mu2_2;

GaussianBlur(I1_I2, sigma12, Size(11, 11), 1.5);
sigma12 -= mu1_mu2;

///////////////////////////////// FORMULA ////////////////////////////////
Mat t1, t2, t3;

t1 = 2 * mu1_mu2 + C1;
t2 = 2 * sigma12 + C2;
t3 = t1.mul(t2);              // t3 = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))

t1 = mu1_2 + mu2_2 + C1;
t2 = sigma1_2 + sigma2_2 + C2;
t1 = t1.mul(t2);               // t1 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2))

Mat ssim_map;
divide(t3, t1, ssim_map);      // ssim_map =  t3./t1;

Scalar mssim = mean(ssim_map); // mssim = average of ssim map
return mssim;
}``````
``````//功能：局部均值求取 局部方差求取
/* zc 2018/07/08
parameters:
Mat*         src;         //待处理的图像
float*       meanv;       //保存局部均值
float*       dev;         //保存局部方差
int          indexrows;   //要求局部所在行
int          indexcols;   //要求局部所在列
int          ker;         //窗口大小系数
*/
void Meanvalue(Mat* src, float* meanv, float* dev, int indexrows, int indexcols,  int ker)
{
int lo = (ker - 1) / 2;
float total = 0;
float total2 = 0;
for (int i = indexrows - lo; i <= indexrows + lo; i++)
{
for (int j = indexcols - lo; j <= indexcols + lo; j++)
{
float temp = static_cast<float>(src->at<uchar>(i, j));
total += temp;
total2 += temp*temp;
}
}
int size = ker * ker;
*meanv = total / size;                                      //均值
*dev = (total2 - (total*total) / size) / size;              //方差
return;
}``````

2012-07-15 16:54:39 ljy1988123 阅读数 16244
• ###### c++入门到精通教程 c++11/14/17

本教程适合那些只有一点点c语言编程知识的新手，也适合那些c++98标准已经掌握的不错但对c++11/14/17新标准基本无所知的c++开发老手，欢迎大家尽早加入学习，请大家从授课目录，讲解课程时长、演示范例详尽程度，试听后收获四个方面评估本教程的质量。

7345 人正在学习 去看看 王健伟
C++：
Prata《C++ Primer Plus》：基础，第一本书。（之前的版本也可，不过推荐最新的）
Lippman《Inside C++ Object Model》：初级，加深语言层次上的理解。不过有点小老了。里面的cfront编译器早退出江湖好久了。思想值得学习
侯捷《STL源码剖析》：第三本书，中级，深入STL
Vandevoorde《C++ Template》：第四本，中级，范式编程
罗剑锋《Boost》两本：中级，Boost库深入，不过他写的浅了，boost方面没发现更好的书了，一般自己看代码
Mayer《Effective C++》：中级，编码原则问题。要在C++用了一段时间之后再看
Martin《敏捷软件开发》：中级，OB的原则，极限编程
Alexandrescu《Modern C++》：高级，一些泛型上的顶级技术
Moo《C++沉思录》：高级，没到一定的分数不建议看
Schmidt《C++ Network Programming》：ACE老大写的系列，网络编程方面如果决定用ACE了的话，这是必看的。
Press《Numerical Recipes in C++》：C++数值算法，高级，本科我们的教材，不过那个时候学纯粹是胡闹。这本书，太帅了。读完这个，你就抛弃matlab那么弱的工具吧，用uBLAS等强悍的科学计算库你会发现C++能做的事确实比matlab多太多了，这本书里的东西是所有科学计算库的基础。不过。。。。。还是有不过的，有的选的话，我宁肯用matlab。。。。嗨。。。。编码量少啊。。。。
还有http://www.cplusplus.com/  。这个网站要是你没上过的话，我怀疑你仲么写的程序。。。。。
当然，官方文档也应在列。不过说实话，我没看。不敢列上来害人。可读性应该不高。
还有，boost和Qt。搞熟这俩工具。大部分问题难不倒你啦。
C:
Lindon《C专家编程》：C相对C++很容易，我看了这两本书就基本木有问题了。
Koening《C traps and pit fails》：
《GoF Design patterns》
《Pattern-Oriented software architecture vol.1 2. 3. 4. .......》

张银奎《软件调试》：国内少见的大作，水平不是顶级，但在国内算顶级了
钱林松《C++反汇编与逆向分析》：很不错的逆向工程书，入门首选
俞甲子《程序员的自我修养——链接、装载与库》：这个真是C++/C程序员必看。讲dll、lib、堆、栈的原理，linux和windows对比分析
Aho《Compilers:Principles》：传说中的编译原理，软院的会学，所以瞎子都知道重要性。

Kurzweil《奇点临近》：我称之神书，是我看过的我评价最高的一本技术书。作者的技术高度已经到了世界顶级的顶级了，加速回归理论谁能掌握谁就会发现世界好容易分析。
Mitchell《Machine Learning》：机器学习入门的好书，数学不太复杂，也不厚，但一点不失水准
Kamber的《数据挖掘 概念与技术》：数据挖掘多少得懂点吧。这书写的一点也不复杂，很有条理。我就用这本书完成了一个数据挖掘的大项目。
冈萨雷斯《数字图像处理》：那个啥，别说C++用不到，程序员的语言只是工具，关键还是看你干嘛。我就不信你遇不到图像处理。。。。
Theodoridis《Pattern Recognition》：我觉得吧。快速切入人工这本书必不可少，也应该是第一本书。
史忠植《神经网络》：我觉得得记住这个人。我认可的中国最有水平的人工智能方面的大全级，国宝级专家（虽然和Kurzweil一比就不好意思了）
海金《神经网络与机器学习》：说实话，本不想把这本列上来。太难懂了。不过不能否认其重量级。建议看史忠植的。
Moesland《Visual Analysis of Humans ：Looking at People》

Leiserson《Introduction to Algorithms》：传说中的算法导论，入门必看的。
Heineman《Algorithms in a nutshell》：也是入门级，不过是以应用的视角分类算法，实际应用能力比导论好很多。
Knuth《The art of Computer Programming》：这才是殿堂级的著作，可惜我只看了第一本，共7本。不是专业搞算法的就别深入啃这7本了。。。会死人的。。。。

Andrew S. Tanenbaum 《Modern operating system》：引入了不少现代的概念和想法，建议作为操作系统入门看的第二本书
Abraham Silberschats 《Operating System Concept》：操作系统方面的恐龙书，我个人觉的这是讲理论讲的最易懂而透彻的一本书
William Stallings 《Operating Systems Internals and Design Principles》：偏硬件，涉及到算法的时候讲的非常详细。
《The Design and Implementation of the 4.4 BSD Operating System》
《The Design and Implementation of the FreeBSD Operating System》：可以作为进阶的书籍，对BSD经典的进行深入
Andrew S. Tanenbaum 《Modern operating system》：引入了不少现代的概念和想法，建议作为操作系统入门看的第二本
《Unix Network  Programming》：不多说废话了。

《人月神话》：听过的人很多，看过的人很少，想了解软件工程的多少看一下吧。
《GoF Design patterns》
《Pattern-Oriented software architecture vol.1 2. 3. 4. .......》
Brooks《The design of design》：设计原本。不建议看太早，没几年编程经验，没点深入技能，看了也白看。把软件设计通用化的神书。
Blaha《Object-Oriented Modelling and Design with UNL》：从UML视角讲解系统分析与设计。比衣杨老师的那本书好上几千个档次。。。不过衣杨老师的课很好。

《TCP/IP 详解》：不多说废话了
《Unix Network  Programming》：不多说废话了。

Korth《数据库系统概念》。我觉得，数据库这一本足够了。没意见吧？不过数据仓库的话，见数据挖掘那本。（人工智能项）

推荐隆重一个系列：GTM。我看过《Graph Theory》。。。。于是。。。。整个系列都在我的收藏夹了。。。。。
数据结构就维斯的那本吧。
其他数学对程序员来说基本都浮云。

IT侃大山：
吴军《浪潮之巅》：真心推荐。我个人是双学位，管理也懂些。所以深刻知道吴军的技术高度来的有多么不容易。一个人真的该懂点市场，活得明白。
Paul《黑客与画家》：这个有点老了。大家就不要信里面的算法了。作者对于他的反垃圾算法和lisp都太过自信了。不过他的经历真心值得学习。

2010-10-10 14:52:00 maozefa 阅读数 9269
• ###### c++入门到精通教程 c++11/14/17

本教程适合那些只有一点点c语言编程知识的新手，也适合那些c++98标准已经掌握的不错但对c++11/14/17新标准基本无所知的c++开发老手，欢迎大家尽早加入学习，请大家从授课目录，讲解课程时长、演示范例详尽程度，试听后收获四个方面评估本教程的质量。

7345 人正在学习 去看看 王健伟

《C++图像处理》系列以代码清晰，可读性为主，全部使用C++代码。

《Delphi图像处理》系列以效率为侧重点，一般代码为PASCAL，核心代码采用BASM。

尽可能保持二者内容一致，可相互对照。

本文代码必须包括《C++图像处理 -- 数据类型及公用函数文章中的BmpData.h头文件。

有关图像的平面几何变换，现有的教程、计算机图书以及网上的资料上介绍理论的偏多，即使有些编程实例，也只是介绍图像几何变换的某些特例，如旋转、缩放、平移等。GDI+倒是有个Matrix类，可完整地实现图像的几何变换，可惜没法得到源码。

本文将完整的实现一个类似GDI+ Matrix的C++几何变换类TransformMatrix，关于几何变换的理论及原理请参考有关书籍或资料。

下面是C++几何变换类TransformMatrix的代码：

```//---------------------------------------------------------------------------
#ifndef TransformMatrixH
#define TransformMatrixH
//---------------------------------------------------------------------------
#include <math.h>
//---------------------------------------------------------------------------

typedef union
{
FLOAT Elements[6];
struct
{
FLOAT m11;
FLOAT m12;
FLOAT m21;
FLOAT m22;
FLOAT dx;
FLOAT dy;
};
}MatrixElements, *PMatrixElements;
//---------------------------------------------------------------------------

class TransformMatrix
{
private:
MatrixElements elements;

VOID ElementsInit(MatrixElements &e)
{
e.m11 = e.m22 = 1.0f;
e.m12 = e.m21 = e.dx = e.dy = 0.0f;
}

VOID ElementsMultiply(MatrixElements &e)
{
FLOAT m11 = elements.m11;
FLOAT m12 = elements.m12;
elements.m11 = e.m11 * m11 + e.m12 * elements.m21;
elements.m12 = e.m11 * m12 + e.m12 * elements.m22;
elements.m21 = e.m21 * m11 + e.m22 * elements.m21;
elements.m22 = e.m21 * m12 + e.m22 * elements.m22;
}

public:
// 建立一个新实例，并初始化为单位矩阵 Elements = 1,0,0,1,0,0
TransformMatrix(VOID)
{
Reset();
}

// 建立一个新实例，并复制matrix的元素
TransformMatrix(TransformMatrix *matrix)
{
SetElements(matrix->elements);
}

TransformMatrix(TransformMatrix &matrix)
{
SetElements(matrix.elements);
}

// 建立一个按指定的元素初始化的新实例
TransformMatrix(FLOAT m11, FLOAT m12, FLOAT m21, FLOAT m22, FLOAT dx, FLOAT dy)
{
SetElements(m11, m12, m21, m22, dx, dy);
}

// 重置对象为单位矩阵
VOID Reset(VOID)
{
ElementsInit(elements);
}

// 将对象与matrix相乘
VOID Multiply(TransformMatrix *matrix)
{
elements.dx += (matrix->elements.dx * elements.m11 + matrix->elements.dy  * elements.m21);
elements.dy += (matrix->elements.dx * elements.m12 + matrix->elements.dy  * elements.m22);
ElementsMultiply(matrix->elements);
}

VOID Multiply(TransformMatrix &matrix)
{
Multiply(&matrix);
}

// 设置平移
VOID Translate(FLOAT offsetX, FLOAT offsetY)
{
elements.dx += (offsetX * elements.m11 + offsetY * elements.m21);
elements.dy += (offsetX * elements.m12 + offsetY * elements.m22);
}

// 设置缩放
VOID Scale(FLOAT scaleX, FLOAT scaleY)
{
MatrixElements e;
ElementsInit(e);
e.m11 = scaleX;
e.m22 = scaleY;
ElementsMultiply(e);
}

// 设置剪切，注意不要将shearX, shearY同时设置为1
VOID Shear(FLOAT shearX, FLOAT shearY)
{
MatrixElements e;
ElementsInit(e);
e.m21 = shearX;
e.m12 = shearY;
ElementsMultiply(e);
}

// 设置按角度angle沿原点旋转
VOID Rotate(FLOAT angle)
{
MatrixElements e;
angle = angle * M_PI / 180.0f;
e.m11 = e.m22 = cos(angle);
e.m12 = sin(angle);
e.m21 = -e.m12;
e.dx = e.dy = 0.0f;
ElementsMultiply(e);
}

// 设置按角度angle沿中心点centerX, centerY旋转
VOID RotateAt(FLOAT angle, FLOAT centerX, FLOAT centerY)
{
Translate(centerX, centerY);
Rotate(angle);
Translate(-centerX, -centerY);
}

// 如果此对象是可逆转的，则逆转该对象，返回TRUE；否则返回FALSE
BOOL Invert(VOID)
{
double tmp = elements.m11 * elements.m22 - elements.m12 * elements.m21;
if ((INT)(tmp * 1000.0f) == 0) return FALSE;
tmp = 1.0f / tmp;
FLOAT m11 = elements.m11;
FLOAT dx = -elements.dx;
elements.m11 = tmp * elements.m22;
elements.m12 = tmp * -elements.m12;
elements.m21 = tmp * -elements.m21;
elements.m22 = tmp * m11;
elements.dx = dx * elements.m11 - elements.dy * elements.m21;
elements.dy = dx * elements.m12 - elements.dy * elements.m22;
return TRUE;
}

// 按给定的大小计算并返回实施变换后的尺寸
VOID GetTransformSize(INT width, INT height, FLOAT &fx, FLOAT &fy, FLOAT &fwidth, FLOAT &fheight)
{
FLOAT fxs[3], fys[3], v;
fxs[1] = fys[0] = 0.0f;
fxs[0] = fxs[2] = width;
fys[1] = fys[2] = height;
fx = fy = fwidth = fheight = 0.0f;
for (INT i = 0; i < 3; i ++)
{
v = fxs[i] * elements.m11 + fys[i] * elements.m21;
if (v < fx) fx = v;
else if (v > fwidth) fwidth = v;
v = fxs[i] * elements.m12 + fys[i] * elements.m22;
if (v < fy) fy = v;
else if (v > fheight) fheight = v;
}
fwidth -= fx;
fheight -= fy;
fx += elements.dx;
fy += elements.dy;
}

// 按给定的大小计算并返回实施变换后整型数矩形
VOID GetTransformRect(INT width, INT height, RECT &r)
{
FLOAT fx, fy, fwidth, fheight;
GetTransformSize(width, height, fx, fy, fwidth, fheight);
r.left = (INT)fx;
r.top = (INT)fy;
r.right = (INT)(fwidth + fx + 0.999999f);
r.bottom = (INT)(fheight + fy + 0.999999f);
}

// 判断此对象是否是单位矩阵
BOOL GetIdentity(VOID)
{
return (elements.m11 == 1.0f &&
elements.m22 == 1.0f &&
elements.m12 == 0.0f &&
elements.m21 == 0.0f &&
elements.dx == 0.0f &&
elements.dy == 0.0f);
}

// 获取对象的x偏移量
FLOAT GetOffsetX(VOID)
{
return elements.dx;
}

// 获取对象的y偏移量
FLOAT GetOffsetY(VOID)
{
return elements.dy;
}

// 判断对象是否是可逆转的。
BOOL GetInvertible(VOID)
{
return (INT)(1000.0f * (elements.m11 * elements.m22 - elements.m12 * elements.m21)) != 0;
}

// 获取对象元素
MatrixElements& GetElements(VOID)
{
return elements;
}

// 设置对象元素。注：设置元素是覆盖形式的
VOID SetElements(CONST MatrixElements &value)
{
SetElements(value.m11, value.m12, value.m21, value.m22, value.dx, value.dy);
}

VOID SetElements(FLOAT m11, FLOAT m12, FLOAT m21, FLOAT m22, FLOAT dx, FLOAT dy)
{
elements.m11 = m11;
elements.m12 = m12;
elements.m21 = m21;
elements.m22 = m22;
elements.dx = dx;
elements.dy = dy;
}

};
//---------------------------------------------------------------------------
#endif
```

上面代码中定义了一个几何变换矩阵成员类型MatrixElements，便于实际编程时获取或设置几何变换矩阵成员，TransformMatrix只是简单的对其进行了封装，并通过计算实现有关的几何变换。

TransformMatrix的核心代码是Multiply函数（或ElementsMultiply函数）和Invert函数。

Multiply函数可完成各种复杂的几何变换计算，所有能够实现的具体几何变换都是可以通过其完成的（代码中的平移函数Translate也可以通过其完成的，当然多了一些不必要的计算）。虽说本文标题是《实现完整的图像平面几何变换》，但TransformMatrix中的几种基础的变换函数并不代表全部的几何变换，如对称几何变换（镜像），更不用说复杂的组合变换。这倒不是本人要做“标题党”，我所说的“实现完整的图像几何变换”，是指可以通过Multiply函数或者更直接的变换矩阵成员设置去实现“完整的”图像几何变换，除非其不能使用平面几何变换矩阵进行描述（如梯形变换我就没想到怎么实现，也许其超出了平面几何变换矩阵范畴？），或者不能进行实际的几何变换（不可逆）；“实现完整的图像几何变换”的另一层含义是下面的图像变换执行函数可实现TransformMatrix所能表示的任意图像几何变换，而不必去写一个个具体的，如缩放、旋转变换函数等。

Invert函数实现了变换矩阵的逆矩阵，通过这个几何变换逆矩阵，可以很方便地实现图形图像几何变换的实际操作。为什么要靠几何变换矩阵的逆矩阵，而不是直接依据变换矩阵来实现图形图像几何变换的实际操作呢？因为几何变换矩阵表示的意思是，把源图像的任意座标点通过几何变换后投影到目标图像。因为源图像像素通过几何变换后与目标图像上的像素点有可能不能一一对应，如图像缩放变换后，不是多个源图像像素点对应同一个目标像素点（缩小），就是源图像像素点不足以填充全部的目标像素点（放大），这就有可能造成目标图像像素点被重复绘制或者被遗漏的现象发生；而几何变换逆矩阵所表示的意思是，对于目标图像任意一个像素点，如果在几何变换前有源图像像素点与其对应，则进行复制。遍历目标图像像素点就能保证目标图像像素点既不重复、也不遗漏的被复制。

下面是一个图像几何变换函数代码：

```// 执行图像数据几何变换
VOID Transform(BitmapData *dest, INT x, INT y, CONST BitmapData *source, TransformMatrix *matrix)
{
// 复制几何变换矩阵对象
TransformMatrix m(matrix);
// 几何变换矩阵绝对增加平移量x, y
m.GetElements().dx += x;
m.GetElements().dy += y;
// 按几何变换矩阵计算并获取目标图像数据子数据
float fx, fy, fwidth, fheight;
m.GetTransformSize(source->Width, source->Height, fx, fy, fwidth, fheight);
BitmapData dst;
if (!GetBitmapData(dest, (INT)fx, (INT)fy,
(INT)(fwidth + 0.999999f), (INT)(fheight + 0.999999f), &dst))
return;
// 获取几何变换逆矩阵
if (!m.Invert()) return;
// 如果子图数据与目标图像原点不一致，几何变换矩阵相对增加平移量fx, fy
if (fx > 0.0f || fy > 0.0f)
{
if (fx < 0.0f) fx = 0.0f;
else if (fy < 0.0f) fy = 0.0f;
m.Translate(fx, fy);
}
// 设置子图扫描线指针及行偏移宽度
UINT *pix = (UINT*)dst.Scan0;
INT dstOffset = (dst.Stride >> 2) - dst.Width;
// 几何变换逆矩阵的平移量为与子图原点对应的源图起始坐标点
MatrixElements e = m.GetElements();
float xs = e.dx;
float ys = e.dy;
// 逐点计算并复制源图几何变换后的数据到目标子图
for (y = 0; y < (INT)dst.Height; y ++, pix += dstOffset, xs += e.m21, ys += e.m22)
{
float xs0 = xs;
float ys0 = ys;
for (x = 0; x < (INT)dst.Width; x ++, pix ++, xs0 += e.m11, ys0 += e.m12)
{
INT x0 = xs0 < 0.0f? (INT)(xs0 - 0.5f) : (INT)(xs0 + 0.5f);
INT y0 = ys0 < 0.0f? (INT)(ys0 - 0.5f) : (INT)(ys0 + 0.5f);
if (y0 >= 0 && y0 < (INT)source->Height && x0 >= 0 && x0 < (INT)source->Width)
*pix = *(UINT*)((CHAR*)source->Scan0 + y0 * source->Stride + (x0 << 2));
}
}
}
```

上面图像几何变换函数的几个特点：

1、可以实现任意的图像几何变换（只要TransformMatrix能正确表达的，即变换矩阵可逆）；

2、采用了GDI+ 的BitmapData结构（转换为32位ARGB像素格式），而并非任何具体的图像格式，保证了其通用性；

3、函数使用浮点数运算，但在计算像素点位置时避免了通常的浮点数乘除运算，既提高了一定的运算速度，也为以后修改为定点数运算奠定了基础；

4、函数采用临近像素插值，且没有边界像素处理代码，像素复制质量较差。

可以看出，Transform函数的着重点在于特点（1），在实际的实现代码中，可以把它作为一个框架进行扩充和修改。

下面是一个利用Transform函数对GDI+位图进行旋转变换的例子（使用BCB2007）：

```void __fastcall TForm1::Button1Click(TObject *Sender)
{
// 获取源图像扫描线数据
Gdiplus::Bitmap *bmp =  new Gdiplus::Bitmap(L"..\\..\\media\\IMG_9440_mf.jpg");
BitmapData source, dest;
LockBitmap(bmp, &source);

// 设置几何变换
TransformMatrix matrix;
matrix.Rotate(45);
// 建立目标位图并获取其扫描线数据
RECT r;
matrix.GetTransformRect(source.Width, source.Height, r);
Gdiplus::Bitmap *newBmp = new Gdiplus::Bitmap(
r.right - r.left, r.bottom - r.top, PixelFormat32bppARGB);
LockBitmap(newBmp, &dest);

// 执行图像几何变换
Transform(&dest, 0, 0, &source, &matrix);
// 释放图像扫描线数据(位图解锁)
UnlockBitmap(newBmp, &dest);
UnlockBitmap(bmp, &source);

// 画几何变换后的图像
Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);
g->DrawImage(newBmp, 0, 0);

delete g;
delete newBmp;
delete bmp;

}
//---------------------------------------------------------------------------
```

下面是图像旋转变换例子运行界面截图：

由于图像几何变换是以源图原点（0，0）为变换原点，所以界面上只能看到原点右下边的图像。还有些几何变换，如旋转90度、180度等，可能会导致几何变换后的图像完全不可见，为了直观的看到各种几何变换后的完整图像，可以修改一下例子代码，将TransformMatrix::GetTransformRect函数返回矩形的左上边部分也包括进来：

```void __fastcall TForm1::Button1Click(TObject *Sender)
{
// 获取源图像扫描线数据
Gdiplus::Bitmap *bmp =  new Gdiplus::Bitmap(L"..\\..\\media\\IMG_9440_mf.jpg");
BitmapData source, dest;
LockBitmap(bmp, &source);

// 设置几何变换
TransformMatrix matrix;
matrix.Rotate(45);
// 建立目标位图并获取其扫描线数据
RECT r;
matrix.GetTransformRect(source.Width, source.Height, r);
Gdiplus::Bitmap *newBmp = new Gdiplus::Bitmap(
r.right - r.left, r.bottom - r.top, PixelFormat32bppARGB);
LockBitmap(newBmp, &dest);

// 执行图像几何变换。
// 注意这里使用-r.left, -r.top为坐标，使得变换后的图像完全可见
Transform(&dest, -r.left, -r.top, &source, &matrix);
// 释放图像扫描线数据(位图解锁)
UnlockBitmap(newBmp, &dest);
UnlockBitmap(bmp, &source);

// 画几何变换后的图像
Gdiplus::Graphics *g = new Gdiplus::Graphics(Canvas->Handle);
g->DrawImage(newBmp, 0, 0);

delete g;
delete newBmp;
delete bmp;

}
//---------------------------------------------------------------------------
```

运行界面截图如下：

因水平有限，错误在所难免，欢迎指正和指导。邮箱地址：maozefa@hotmail.com

这里可访问《C++图像处理 -- 文章索引

2012-05-05 09:13:50 Java_beginer1 阅读数 532
• ###### c++入门到精通教程 c++11/14/17

本教程适合那些只有一点点c语言编程知识的新手，也适合那些c++98标准已经掌握的不错但对c++11/14/17新标准基本无所知的c++开发老手，欢迎大家尽早加入学习，请大家从授课目录，讲解课程时长、演示范例详尽程度，试听后收获四个方面评估本教程的质量。

7345 人正在学习 去看看 王健伟
XX老师：
您好，我是计科一班的XXX，原本能及早的完成的任务却拖延到现在，我感到深深的歉意，早在一个月前，我就开始准备课程设计，去图书馆里面借了很多的书籍，关于ＶＣ＋＋的，看了很多的书，发现几乎所有的书都是介绍ＭＦＣ的我又开始看了ＭＦＣ基础教程，看了一段时间后发现太过繁琐，之后就放弃ＭＦＣ，又继续找关于Ｃ＋＋其他的书籍，想从中获得一些知识，又在网上搜到了关于Ｑｔ的一些东西，随后看了一些后发现，这些都是公司自己开发的框架，从中我知道了标准Ｃ＋＋与ＶＣ＋＋的区别，也知道了Ｃ＋＋的在哪一方面的应用，从网上也了解到Ｃ＋＋都应用在那一块，知道了Ｃ＋＋的强大之处，无论是系统软件，还是应用软件都少不了Ｃ＋＋的身影，那嵌入式这块就更不用说了，绝对是Ｃ＋＋的天下，还有网络游戏，杀毒软件，图像处理，大型数据库等等，到处都能见到Ｃ＋＋的身影，可以说Ｃ＋＋无处不在，而我以后也会在编程语言上选择Ｃ＋＋，以后我会在Ｌｉｎｕｘ下Ｃ／Ｃ＋＋这方面发展，而以后的这些规划，都是建立在现在的基础上的，看到平时很多人整天宅在寝室打游戏，家里人那几千块给他们买的笔记本，嫣然已成为游戏机，相信他们问家里要钱买电脑时也不会是说电脑买回来时玩游戏的；看到这些现象感到非常的替他们惋惜，像大学这样能专心用来学习自己的东西的时光，相信以后不再会有，随说大学所应该学习的东西，已不再只是局限在课本上，学习已融入生活，学习无处不在，可是掌握一门能让自己在社会有立足之地的技能也是相当重要的；毕业时虽然每个人都能拿到拿毕业证，但是这个毕业证背后所包含的东西确实有天壤之别的；
老师可能也知道我们系的XXX他去了百度年薪１５Ｗ，当时百度的工程师电话面试他的时候我也在旁边听着，感觉他的实力真是太强了，对于所学过的东西，绝对是相当的熟悉；当时就感慨去百度是正常的，实力放在那里；虽然衡量一个人是不能用金钱的，但是在一个大学生毕业时人家公司就是用的钱来衡量你这个人的能力的；可能我这个认识有点偏激，后来我在九州通班听到有几个大四女生在为去一月１８００管住，还是２５００不管住的两家公司纠结时，当时我内心的想法就是同样是一起毕业的，差距竟会如此之大。
我以后想在Ｌｉｎｕｘ下的Ｃ／Ｃ＋＋的应用这块发展，对于Ｃ＋＋的要求就不用多说了，那肯定是很高的，而现在编程语言众多，对于Ｃ＋＋的程序员的要求就更高了，以后是趋于精英化，也就是说搞Ｃ＋＋的人要么找不到工作，要么就是很好的工作，而我当然是想是后者，现在看到老师布置完作业很多人不经过思考就直接上网下载一个糊弄一下，替他们惋惜，做一个课程设计这中间能学到很多的东西，他们却体会不到。
我的课程设计拖延到现在才交，我感到深深的歉意，