2019-03-26 15:04:29 weixin_40920848 阅读数 1262

以前没有关注这方面的内容,因为本身与医学也相差万里。总是不理解为什么女朋友每天实验会那么辛苦,原来会经常数数以万计的细胞。大致了解了下一般采用的是PS、Image J等手段来进行细胞计数,但也都会出现不同程度的误差。所以就想着从图像处理的角度去尝试下,网上也已经有一些相关的利用MATLAB编写的代码。

一般来说利用图像处理的手段可以分为以下几个步骤:

  1. 图像格式的转换,及RGB图像转化为GRAY图像,这步大家都了解的;
  2. 然后进行图像的分割,中间会利用一些形态学处理方式,主要思想就是先进行膨胀扩大细胞边缘,然后进行腐蚀取反以确定细胞核的位置,之后采用分水岭算法将连接的细胞分割开;
  3. 对图像进行高斯滤波使图像变得模糊,细胞核相对于细胞边缘颜色较深,突出显示细胞核位置;
  4. 对图像进行锐化获得细胞的轮廓位置信息并进行边沿去除操作;
  5. 通过中值滤波之后对检测到的细胞进行编号。

 

用上面两幅图验证了下,结果还不错,具体是什么细胞我也不是很清楚。中间会有几个参数对结果有一定的影响,图像分割时的灰度阈值由大津法产生,高斯滤波是sigma设为3,其中对最终结果影响最大的是中值滤波的模板大小,通常情况下设置为13较为合理,可以适当的根据图像中细胞数量的多少进行调节。排列较密集时可以适当降低模板大小,对于细胞中心部分和边沿部分变现差异很小且散布集中的细胞处理有一定的难度。班门弄斧了,希望大家指点吐槽,但不接受人身攻击。

clc;close all;clear all;
origin_img=imread('C:\Users\Administrator\Desktop\大黄\1.png'); 
subplot(2,3,1);imshow(origin_img);title('原始图像');
[m,n]=size(origin_img);
img=rgb2gray(origin_img);%灰度
B=origin_img(:,:,3);
B_gray=im2double(B);
level=graythresh(B_gray);
B_bw=im2bw(B_gray,level);
show_img1=B_bw;
subplot(2,3,2);imshow(show_img1);title('灰度图像');
D=-bwdist(~show_img1);
mask=imextendedmin(D,2);
D2=imimposemin(D,mask);
Ld=watershed(D2);
Water_splited=show_img1;
Water_splited(Ld==0)=0;
show_img1=Water_splited;
subplot(2,3,3);imshow(show_img1);title('分割后图像');
sigma=3;
gausFilter=fspecial('gaussian',[5,5],sigma);
Mod=imfilter(img,gausFilter,'replicate');%高斯滤波
subplot(2,3,4);imshow(Mod);title('模糊图像');
lamda=1;
img2=(img-lamda*Mod)/(1-lamda);
subplot(2,3,5);imshow(uint8(img2));title('锐化后图像');%锐化后图像
img3=imclearborder(img2,8);
subplot(2,3,6);imshow(uint8(img3));title('去除边缘图像');%锐化后图像
% % BW = imregionalmin(img3,8);%最小连通区域
% P=BW+5;
% % img4 = bwareaopen(img3,P,8);%删除小于P后的图像
% imwrite(uint8(img2),'leansharpe.jpg');
K=imnoise(img3,'salt & pepper',0.005);%加入椒盐噪声
n2=12;%中值滤波的模板的大小
I=im2bw(K);
Y3=medfilt2(K,[n2 n2]); %调用系统函数进行中值滤波,n2为模板大小 
[L,num] = bwlabel(Y3,8);
STATS1=regionprops(L,'Perimeter'); 
ahe=size(STATS1);
figure(2);imshow(origin_img);
m1=ahe(1,1);
m=zeros(2,m1); 
for i=1:m1
  % 计算目标区域中心,用于显示编号的位置
  [p,q]=find(L==i);
  temp=[p,q];   
  [x,y]=size(temp);
  m(1,i)=sum(p)/x;
  m(2,i)=sum(q)/x;
end
for i=1:m1
  figure(2);
  text(m(2,i),m(1,i),int2str(i),'FontSize',12,'color','red','LineWidth',10)
end
num

 

 

 

 

 

 

2018-02-11 21:08:32 a664607530 阅读数 900

图像的缩小从物理意义上来说,就是将图像的每个像素的大小缩小相应的倍数。从数字图像处理的角度来看,图像的缩小实际就是通过减少像素个数来实现的。显而易见的,减少图像的像素会造成图像信息丢失。为了在缩小图像的同时,保持原图的概貌特征不丢失,从原图中选择的像素方法是非常重要的。本文主要介绍基于等间隔提取图像缩小和基于区域子块提取图像缩放以及其在OpenCV的实现。

基于等间隔提取图像缩小

这种图像缩小算法,通过对原图像像素进行均匀采样来保持所选择到的像素仍旧可以反映原图像的概貌特征。

算法描述

设原图的大小为W*H,宽度和高度的缩小因子分别为看k1和k2,那么采样间隔为:W/k1,H/k2。也就是说在原图的水平方向每隔W/k1,在垂直方向每隔H/k2取一个像素。长和宽的缩小因子k1和k2相等时,图像时等比例缩小,不等时是不等比例缩小,缩小图像的长和宽的比例会发生变化。

基于OpenCV的算法实现

//基于等间隔提取像素缩放
cv::Mat imageReduction1(cv::Mat &image, double kx, double ky)
{
	int row = cvRound(image.rows * kx), col = cvRound(image.cols * ky);//获取输出图像分辨率,cvRound是对double类型进行四舍五入
	cv::Mat result(row, col, image.type());
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			int x = static_cast<int>((i + 1) / kx + 0.5) - 1;
			int y = static_cast<int>((j + 1) / ky + 0.5) - 1;
			result.at<cv::Vec3b>(i, j) = image.at<cv::Vec3b>(x, y);
		}
	}
	return result;
}

运行结果


基于区域子块提取图像缩小

算法描述

等间隔提取图像缩小方法实现简单,但是原图像中未被选中的像素信息会在缩小后的图像中丢失。基于区域子块提取图像缩小方法对其进行了改进。在求缩小图像的像素时,不仅仅单纯的取在原图像中的采样点像素,将原图像分成一个个的子块。缩小图像的像素取相应子块像素的均值。

基于OpenCV的算法实现

//获取区域子块的均值
cv::Vec3b getave(const cv::Mat &image, cv::Point_<int>leftPoint, cv::Point_<int>rightPoint)
{
	int tmp1 = 0, tmp2 = 0, tmp3 = 0;
	int sum = (rightPoint.x - leftPoint.x + 1)*(rightPoint.y - leftPoint.y + 1);
	for (int i = leftPoint.x; i <= rightPoint.x; i++)
	{
		for (int j = leftPoint.y; j <= rightPoint.y; j++)
		{
			tmp1 += image.at<cv::Vec3b>(i, j)[0];
			tmp2 += image.at<cv::Vec3b>(i, j)[1];
			tmp3 += image.at<cv::Vec3b>(i, j)[2];
		}
	}
	cv::Vec3b ans;
	ans[0] = tmp1 / sum;
	ans[1] = tmp2 / sum;
	ans[2] = tmp3 / sum;
	return ans;
}

//基于区域子块提取图像缩放
cv::Mat imageReduction2(const cv::Mat &image, double kx, double ky)
{
	int row = cvRound(image.rows * kx), col = cvRound(image.cols * ky);
	cv::Mat result(row, col, image.type());
	int leftrow = 0, leftcol = 0;
	for (int i = 0; i < row; i++)
	{
		int x = static_cast<int>((i + 1) / kx + 0.5) - 1;
		for (int j = 0; j < col; j++)
		{
			int y = static_cast<int>((j + 1) / ky + 0.5) - 1;
			result.at<cv::Vec3b>(i, j) = getave(image, cv::Point_<int>(leftrow, leftcol), cv::Point_<int>(x, y));
			leftcol = y + 1;
		}
		leftrow = x + 1;
		leftcol = 0;
	}
	return result;
}

运行结果


OpenCV图像缩放使用的函数是:resize
void resize(InputArray src, OutputArray dst, Size dsize, double fx = 0, double fy = 0, int interpolation = INTER_LINEAR)
参数含义:
InputArray src - 原图像
OutputArray dst - 输出图像
Size dsize - 目标图像的大小
double fx = 0 - 在x轴上的缩放比例
double fy = 0 - 在y轴上的缩放比例
int interpolation - 插值方式,有以下四种方式:

INTER_NN - 最近邻插值
INTER_LINEAR - 双线性插值(缺省使用)
INTER_AREA - 使用象素关系重采样,当图像缩小时候,该方法可以避免波纹出现。当图像放大时,类似于 INTER_NN 方法。
INTER_CUBIC - 立方插值。

说明:dsize与fx和fy必须不能同时为零

2019-12-07 22:31:37 Aoman_Hao 阅读数 24

模糊特征隶属度函数

若以像素的相对灰度等级作为感兴趣的模糊特征,模糊隶属度函数的定义方式多,在实际问题中,最常用的隶属度函数形式是标准的SS型函数和paipai型函数。其中,SS型函数是一种从0到1的单调增长喊数;paipai型函数是指“中间高两边低”的函数。从图像处理的角度看,转化为模糊域的灰度值是从低到高的连续过程,SS型函数符合边缘的过渡变化过程,所以以SS型函数作为模糊函数的基本变换形式比较合理。

SS型函数公式如下:
f(x,a,x)=11+ea(xc)f(x,a,x) = \frac{1}{1+e^{-a(x-c)}}

根据参数a为正,SS型隶属度函数的开口朝左

图像模糊增强边缘提取

根据灰度级阈值参数XTX_T,定义新的隶属函数,式如:

XijX_{ij}减小,μij\mu_{ij}减小。

参数FdF_d为倒数型模糊因子,FeF_e为指数型模糊因子,均为正数,其值影响模糊性,影响曲线形状。

图像增强

基于模糊集的图像增强

基于模糊集的图像增强算法框架如下:

模糊域内图像增强实在图像的模糊特征平面上对μij\mu_{ij}进行非线性变换,当μij>0.5\mu_{ij}>0.5时,增大μij\mu_{ij}数值,当μij<0.5\mu_{ij}<0.5时,增大μij\mu_{ij}数值

如下增强因子

TrT_rT1T_1的多次递归调用,有限次递归调用可以增强图像,地带次数够多,会产生二值图像。

TT变换图像如下:

空域图像增强

模糊域增强图像进行G1G^{-1}逆变换,得到空域增强图像,逆变换公式如下:

模糊边缘提取效果

调节模糊参数减少噪声对图像边缘提取的影响,具有一定的抗噪声干扰能力

我的个人博客主页,欢迎访问

我的CSDN主页,欢迎访问

我的GitHub主页,欢迎访问

2011-03-13 00:39:00 foxwit 阅读数 713

最近纠缠于文本图像处理,总算告一段落。

对一幅文本图像,首先进行去噪。通过高斯滤波方法滤去噪声。然后检测图像的倾斜角度。参考了一篇介绍用线性回归方法的论文后,并不断尝试,终于获得理想效果,准确提取了图像的倾斜角度。但是对不同大小的字体是否准确,需要进一步测试。

2007-04-27 09:50:00 housisong 阅读数 23355

          图形图像处理-之-任意角度的高质量的快速的图像旋转 上篇 纯软件的任意角度的快速旋转
                               
HouSisong@GMail.com   2007.04.26

 

(2009.03.09  可以到这里下载旋转算法的完整的可以编译的项目源代码:  http://blog.csdn.net/housisong/archive/2009/03/09/3970925.aspx  )

(2007.06.22 优化PicRotary3加快13.6%,优化PicRotarySSE加快6.1%,
            尝试了一下使用SSE2的写缓冲优化MOVNTI)
(2007.04.29 修正一个TRotaryClipData.find_begin的bug)
(2007.05.16 更换测试用电脑和编译器,为了保持测试数据一致和可对比性,更新了测试数据)


 

tag:图像旋转,任意角度,图像缩放,速度优化,定点数优化,近邻取样插值,二次线性插值,
   三次卷积插值,MipMap链,三次线性插值,MMX/SSE优化,CPU缓存优化,AlphaBlend,颜色混合,并行

摘要:首先给出一个基本的图像旋转算法,然后一步一步的优化其速度和旋转质量,打破不能软件旋转的神话!

任意角度的高质量的快速的图像旋转 全文 分为:
     上篇 纯软件的任意角度的快速旋转
     中篇 高质量的旋转
     下篇 补充话题(完整AlphaBlend旋转、旋转函数并行化、针对大图片的预读缓冲区优化)

正文:
  为了便于讨论,这里只处理32bit的ARGB颜色;
  代码使用C++;涉及到汇编优化的时候假定为x86平台;使用的编译器为vc2005;
  为了代码的可读性,没有加入异常处理代码;
   测试使用的CPU为赛扬2G(新的测试平台的CPU为AMD64x2 4200+(2.37G),测试时使用的单线程执行);
  (一些基础代码和插值原理的详细说明参见作者的《图形图像处理-之-高质量的快速的图像缩放》系列文章)


速度测试说明:
  只测试内存数据到内存数据的缩放
  测试图片都是800*600旋转到1004*1004,测试成绩取各个旋转角度的平均速度值; fps表示每秒钟的帧数,值越大表示函数越快


A:旋转原理和旋转公式:
  推导旋转公式:
             
                       旋转示意图
   有:  tg(b)=y/x                             ----(1)
         tg(a+b)=y'/x'                         ----(2)
         x*x + y*y = x'*x' + y'*y'             ----(3)
   有公式:tg(a+b) = ( tg(a)+tg(b) ) / ( 1-tg(a)*tg(b) )  ----(4)
     把(1)代入(4)从而消除参数b;
     tg(a)+y/x = y'/x'*( 1-tg(a)*y/x )                ----(5)
     由(5)可以得x'=y'*(x-y*tg(a))/( x*tg(a)+y )       ----(6)
   把(6)代入(3)从而消除参数x',化简后求得:
     y'=x*sin(a)+y*cos(a);                     ----(7)
   把(7)代入(6),有:
     x'=x*cos(a)-y*sin(a);                     ----(8)

  OK,旋转公式有了,那么来看看在图片旋转中的应用;
  假设对图片上任意点(x,y),绕一个坐标点(rx0,ry0)逆时针旋转RotaryAngle角度后的新的坐标设为(x', y'),有公式:
  (x平移rx0,y平移ry0,角度a对应-RotaryAngle , 带入方程(7)、(8)后有: ) 
  x'= (x - rx0)*cos(RotaryAngle) + (y - ry0)*sin(RotaryAngle) + rx0 ;
  y'=-(x - rx0)*sin(RotaryAngle) + (y - ry0)*cos(RotaryAngle) + ry0 ;

那么,根据新的坐标点求源坐标点的公式为:
  x=(x'- rx0)*cos(RotaryAngle) - (y'- ry0)*sin(RotaryAngle) + rx0 ;
  y=(x'- rx0)*sin(RotaryAngle) + (y'- ry0)*cos(RotaryAngle) + ry0 ;

旋转的时候还可以顺便加入x轴和y轴的缩放和平移,而不影响速度,那么完整的公式为:           
  x=(x'- move_x-rx0)/ZoomX*cos(RotaryAngle) - (y'- move_y-ry0)/ZoomY*sin(RotaryAngle) + rx0 ;
  y=(x'- move_x-rx0)/ZoomX*sin(RotaryAngle) + (y'- move_y-ry0)/ZoomY*cos(RotaryAngle) + ry0 ;
  其中: RotaryAngle为逆时针旋转的角度;
         ZoomX,ZoomY为x轴y轴的缩放系数(支持负的系数,相当于图像翻转);
         move_x,move_y为x轴y轴的平移量;

 一些颜色和图片的数据定义:

#define asm __asm

typedef unsigned 
char TUInt8; // [0..255]
struct TARGB32      //32 bit color
{
    TUInt8  b,g,r,a;          
//a is alpha

};

struct TPicRegion  //一块颜色数据区的描述,便于参数传递

{
    TARGB32
*    pdata;         //颜色数据首地址

    long        byte_width;    //一行数据的物理宽度(字节宽度);
                
//abs(byte_width)有可能大于等于width*sizeof(TARGB32);

    long        width;         //像素宽度
    long        height;        //像素高度
};

//那么访问一个点的函数可以写为:

inline TARGB32& Pixels(const TPicRegion& pic,const long x,const long y)
{
    
return ( (TARGB32*)((TUInt8*)pic.pdata+pic.byte_width*
y) )[x];
}
//判断一个点是否在图片中

inline bool PixelsIsInPic(const TPicRegion& pic,const long x,const long y)
{
    
return ( (x>=0)&&(x<pic.width) && (y>=0)&&(y<
pic.height) );
}

 

 B:一个简单的浮点实现版本

//////////////////////////////////////////////////////////////////////////////////////////////////////
//函数假设以原图片的中心点坐标为旋转和缩放的中心

void PicRotary0(const TPicRegion& Dst,const TPicRegion& Src,double RotaryAngle,double ZoomX,double ZoomY,double move_x,double move_y)
{
    
if ( (fabs(ZoomX*Src.width)<1.0e-4|| (fabs(ZoomY*Src.height)<1.0e-4) ) return//太小的缩放比例认为已经不可见

    double rx0=Src.width*0.5;  //(rx0,ry0)为旋转中心 
    double ry0=Src.height*0.5
    
for (long y=0;y<Dst.height;++
y)
    {
        
for (long x=0;x<Dst.width;++
x)
        {
            
long srcx=(long)((x- move_x-rx0)/ZoomX*cos(RotaryAngle) - (y- move_y-ry0)/ZoomY*sin(RotaryAngle) +
 rx0) ;
            
long srcy=(long)((x- move_x-rx0)/ZoomX*sin(RotaryAngle) + (y- move_y-ry0)/ZoomY*cos(RotaryAngle) +
 ry0) ;
            
if
 (PixelsIsInPic(Src,srcx,srcy))
                Pixels(Dst,x,y)
=
Pixels(Src,srcx,srcy);
        }
    }
}

(调用方法比如:
   PicRotary0(ppicDst,ppicSrc,PI/6,0.9,0.9,(dst_wh-ppicSrc.width)*0.5,(dst_wh-ppicSrc.height)*0.5);
   //作用:将图片ppicSrc按0.9的缩放比例旋转PI/6幅度后绘制到图片ppicDst的中心
)

//注:测试图片都是800*600的图片旋转到1004*1004的图片中心 测试成绩取各个旋转角度的平均速度值
////////////////////////////////////////////////////////////////////////////////
//速度测试:                 
//==============================================================================
// PicRotary0              34.9 fps
//////////////////////////////////////////////////////////////////////////////// 

旋转结果图示(小图):

   
                 30度                         60度                      90度

   
                 120度                       150度                      180度

  
                 210度                            240度                          270度

   
                 300度                        330度                       360度


C:优化循环内部,化简系数
  1.sin和cos函数是很慢的计算函数,可以在循环前预先计算好sin(RotaryAngle)和cos(RotaryAngle)的值:
    double sinA=sin(RotaryAngle);
    double cosA=cos(RotaryAngle);
  2.可以将除以ZoomX、ZoomY改成乘法,预先计算出倒数:
    double rZoomX=1.0/ZoomX;
    double rZoomY=1.0/ZoomY;
  3.优化内部的旋转公式,将能够预先计算的部分提到循环外(即:拆解公式):
     原:  long srcx=(long)((x- move_x-rx0)/ZoomX*cos(RotaryAngle) - (y- move_y-ry0)/ZoomY*sin(RotaryAngle) + rx0) ;
           long srcy=(long)((x- move_x-rx0)/ZoomX*sin(RotaryAngle) + (y- move_y-ry0)/ZoomY*cos(RotaryAngle) + ry0) ;
     变形为:
           long srcx=(long)( Ax*x + Bx*y +Cx ) ;
           long srcy=(long)( Ay*x + By*y +Cy ) ;
      其中: Ax=(rZoomX*cosA); Bx=(-rZoomY*sinA); Cx=(-(rx0+move_x)*rZoomX*cosA+(ry0+move_y)*rZoomY*sinA+rx0);
            Ay=(rZoomX*sinA); By=(rZoomY*cosA);  Cy=(-(rx0+move_x)*rZoomX*sinA-(ry0+move_y)*rZoomY*cosA+ry0);
      (提示: Ax,Bx,Cx,Ay,By,Cy都可以在旋转之前预先计算出来)
  改进后的函数为:

void PicRotary1(const TPicRegion& Dst,const TPicRegion& Src,double RotaryAngle,double ZoomX,double ZoomY,double move_x,double move_y)
{
    
if ( (fabs(ZoomX*Src.width)<1.0e-4|| (fabs(ZoomY*Src.height)<1.0e-4) ) return//太小的缩放比例认为已经不可见

    double rZoomX=1.0/ZoomX;
    
double rZoomY=1.0/
ZoomY;
    
double sinA=
sin(RotaryAngle);
    
double cosA=
cos(RotaryAngle);
    
double Ax=(rZoomX*
cosA); 
    
double Ay=(rZoomX*
sinA); 
    
double Bx=(-rZoomY*
sinA); 
    
double By=(rZoomY*
cosA); 
    
double rx0=Src.width*0.5;  //(rx0,ry0)为旋转中心 

    double ry0=Src.height*0.5
    
double Cx=(-(rx0+move_x)*rZoomX*cosA+(ry0+move_y)*rZoomY*sinA+
rx0);
    
double Cy=(-(rx0+move_x)*rZoomX*sinA-(ry0+move_y)*rZoomY*cosA+
ry0); 

    TARGB32
* pDstLine=
Dst.pdata;
    
double srcx0_f=
(Cx);
    
double srcy0_f=
(Cy);
    
for (long y=0;y<Dst.height;++
y)
    {
        
double srcx_f=
srcx0_f;
        
double srcy_f=
srcy0_f;
        
for (long x=0;x<Dst.width;++
x)
        {
            
long srcx=(long
)(srcx_f);
            
long srcy=(long
)(srcy_f);
            
if
 (PixelsIsInPic(Src,srcx,srcy))
                pDstLine[x]
=
Pixels(Src,srcx,srcy);
            srcx_f
+=
Ax;
            srcy_f
+=
Ay;
        }
        srcx0_f
+=
Bx;
        srcy0_f
+=
By;
        ((TUInt8
*&)pDstLine)+=
Dst.byte_width;
    }
}

////////////////////////////////////////////////////////////////////////////////
//速度测试:                 
//==============================================================================
// PicRotary1               62.0 fps
//////////////////////////////////////////////////////////////////////////////// 

( 在AMD64x2 4200+和VC2005编译下PicRotary1(51.8fps)比PicRotary0(27.1fps)快90%;
  在AMD64x2 4200+和VC6编译下PicRotary1(20.3fps)比PicRotary0(16.1fps)快26%;
  以前在赛扬2G和VC6编译下PicRotary1(8.4fps)反而比PicRotary0(12.7fps)慢50%! :( ) 

D:更深入的优化、定点数优化
  (浮点数到整数的转化也是应该优化的一个地方,这里不再处理,可以参见
    <图形图像处理-之-高质量的快速的图像缩放 上篇 近邻取样插值和其速度优化>中的PicZoom3_float函数)
  1.优化除法:
    原: double rZoomX=1.0/ZoomX;
         double rZoomY=1.0/ZoomY;
    改写为(优化掉了一次除法):
         double tmprZoomXY=1.0/(ZoomX*ZoomY); 
         double rZoomX=tmprZoomXY*ZoomY;
         double rZoomY=tmprZoomXY*ZoomX;
  2.x86的浮点计算单元(FPU)有一条指令"fsincos"可以同时计算出sin和cos值
    //定义SinCos函数: 同时计算sin(Angle)和cos(Angle)的内嵌x86汇编函数
    void __declspec(naked) __stdcall SinCos(const double Angle,double& sina,double& cosa)
    {
        asm
        {
            fld  qword ptr [esp+4]//Angle  
            mov  eax,[esp+12]//&sina
            mov  edx,[esp+16]//&cosa
            fsincos  
            fstp qword ptr [edx]  
            fstp qword ptr [eax] 
            ret 16
        }
    }
  3.用定点数代替浮点计算

void PicRotary2(const TPicRegion& Dst,const TPicRegion& Src,double RotaryAngle,double ZoomX,double ZoomY,double move_x,double move_y)
{
    
if ( (fabs(ZoomX*Src.width)<1.0e-4|| (fabs(ZoomY*Src.height)<1.0e-4) ) return//太小的缩放比例认为已经不可见

    double tmprZoomXY=1.0/(ZoomX*ZoomY);  
    
double rZoomX=tmprZoomXY*
ZoomY;
    
double rZoomY=tmprZoomXY*
ZoomX;
    
double
 sinA,cosA;
    SinCos(RotaryAngle,sinA,cosA);
    
long Ax_16=(long)(rZoomX*cosA*(1<<16
)); 
    
long Ay_16=(long)(rZoomX*sinA*(1<<16
)); 
    
long Bx_16=(long)(-rZoomY*sinA*(1<<16
)); 
    
long By_16=(long)(rZoomY*cosA*(1<<16
)); 
    
double rx0=Src.width*0.5;  //(rx0,ry0)为旋转中心 

    double ry0=Src.height*0.5
    
long Cx_16=(long)((-(rx0+move_x)*rZoomX*cosA+(ry0+move_y)*rZoomY*sinA+rx0)*(1<<16
));
    
long Cy_16=(long)((-(rx0+move_x)*rZoomX*sinA-(ry0+move_y)*rZoomY*cosA+ry0)*(1<<16
)); 

    TARGB32
* pDstLine=
Dst.pdata;
    
long srcx0_16=
(Cx_16);
    
long srcy0_16=
(Cy_16);
    
for (long y=0;y<Dst.height;++
y)
    {
        
long srcx_16=
srcx0_16;
        
long srcy_16=
srcy0_16;
        
for (long x=0;x<Dst.width;++
x)
        {
            
long srcx=(srcx_16>>16
);
            
long srcy=(srcy_16>>16
);
            
if
 (PixelsIsInPic(Src,srcx,srcy))
                pDstLine[x]
=
Pixels(Src,srcx,srcy);
            srcx_16
+=
Ax_16;
            srcy_16
+=
Ay_16;
        }
        srcx0_16
+=
Bx_16;
        srcy0_16
+=
By_16;
        ((TUInt8
*&)pDstLine)+=
Dst.byte_width;
    }
}

////////////////////////////////////////////////////////////////////////////////
//速度测试:
//==============================================================================
// PicRotary2             134.2 fps
//////////////////////////////////////////////////////////////////////////////// 


E:优化内部循环的判断函数PixelsIsInPic,将判断展开到内部循环之外,跳过不需要处理的目标像素;
   TRotaryClipData类用于寻找旋转需要处理的边界范围;算法思路是首先寻找原图片中心点对应的;
那条扫描线,然后依次向上和向下寻找边界;  如果想要更快速的边界寻找算法,可以考虑利用像素的直线
  绘制原理来自动生成边界(有机会的时候再来实现它);

                  

                       边界寻找算法图示

struct TRotaryClipData{
public
:
    
long
 src_width;
    
long
 src_height;
    
long
 dst_width;
    
long
 dst_height;
    
long
 Ax_16; 
    
long
 Ay_16; 
    
long
 Bx_16; 
    
long
 By_16; 
    
long
 Cx_16;
    
long
 Cy_16; 
public
:
    
long
 out_dst_up_y;
    
long
 out_dst_down_y;

    
long
 out_src_x0_16;
    
long
 out_src_y0_16;
private
:
    
long
 out_dst_up_x0;
    
long
 out_dst_up_x1;
    
long
 out_dst_down_x0;
    
long
 out_dst_down_x1;
public
:
    inline 
long get_up_x0(){ if (out_dst_up_x0<0return 0;  else return
 out_dst_up_x0; }
    inline 
long get_up_x1(){ if (out_dst_up_x1>=dst_width) return dst_width;  else return
 out_dst_up_x1; }
    inline 
long get_down_x0(){ if (out_dst_down_x0<0return 0;  else return
 out_dst_down_x0; }
    inline 
long get_down_x1(){ if (out_dst_down_x1>=dst_width) return dst_width;  else return
 out_dst_down_x1; }

    inline 
bool is_in_src(long src_x_16,long
 src_y_16)
    {
         
return ( ( (src_x_16>=0)&&((src_x_16>>16)<
src_width) )
               
&& ( (src_y_16>=0)&&((src_y_16>>16)<
src_height) ) );
    }
    
void find_begin_in(long dst_y,long& out_dst_x,long& src_x_16,long&
 src_y_16)
    {
        src_x_16
-=
Ax_16;
        src_y_16
-=
Ay_16;
        
while
 (is_in_src(src_x_16,src_y_16))
        {
            
--
out_dst_x;
            src_x_16
-=
Ax_16;
            src_y_16
-=
Ay_16;
        }
        src_x_16
+=
Ax_16;
        src_y_16
+=
Ay_16;
    }
    
bool find_begin(long dst_y,long& out_dst_x0,long
 dst_x1)
    {
        
long test_dst_x0=out_dst_x0-1
;
        
long src_x_16=Ax_16*test_dst_x0 + Bx_16*dst_y +
 Cx_16;
        
long src_y_16=Ay_16*test_dst_x0 + By_16*dst_y +
 Cy_16;
        
for (long i=test_dst_x0;i<=dst_x1;++
i)
        {
            
if
 (is_in_src(src_x_16,src_y_16))
            {
                out_dst_x0
=
i;
                
if (i==
test_dst_x0)
                    find_begin_in(dst_y,out_dst_x0,src_x_16,src_y_16);
                
if (out_dst_x0<0
)
                {
                    src_x_16
-=(Ax_16*
out_dst_x0);
                    src_y_16
-=(Ay_16*
out_dst_x0);
                }
                out_src_x0_16
=
src_x_16;
                out_src_y0_16
=
src_y_16;
                
return true
;
            }
            
else

            {
                src_x_16
+=Ax_16;
                src_y_16
+=
Ay_16;
            }
        }
        
return false
;
    }
    
void find_end(long dst_y,long dst_x0,long&
 out_dst_x1)
    {
        
long test_dst_x1=
out_dst_x1;
        
if (test_dst_x1<dst_x0) test_dst_x1=
dst_x0;

        
long src_x_16=Ax_16*test_dst_x1 + Bx_16*dst_y +
 Cx_16;
        
long src_y_16=Ay_16*test_dst_x1 + By_16*dst_y +
 Cy_16;
        
if
 (is_in_src(src_x_16,src_y_16))
        {
            
++
test_dst_x1;
            src_x_16
+=
Ax_16;
            src_y_16
+=
Ay_16;
            
while
 (is_in_src(src_x_16,src_y_16))
            {
                
++
test_dst_x1;
                src_x_16
+=
Ax_16;
                src_y_16
+=
Ay_16;
            }
            out_dst_x1
=
test_dst_x1;
        }
        
else

        {
            src_x_16
-=Ax_16;
            src_y_16
-=
Ay_16;
            
while (!
is_in_src(src_x_16,src_y_16))
            {
                
--
test_dst_x1;
                src_x_16
-=
Ax_16;
                src_y_16
-=
Ay_16;
            }
            out_dst_x1
=
test_dst_x1;
        }
    }

    
bool inti_clip(double move_x,double
 move_y)
    {
        
//计算src中心点映射到dst后的坐标

        out_dst_down_y=(long)(src_height*0.5+move_y);
        out_dst_down_x0
=(long)(src_width*0.5+
move_x);
        out_dst_down_x1
=
out_dst_down_x0;
        
//得到初始out_???

        if (find_begin(out_dst_down_y,out_dst_down_x0,out_dst_down_x1))
            find_end(out_dst_down_y,out_dst_down_x0,out_dst_down_x1);
        out_dst_up_y
=
out_dst_down_y;
        out_dst_up_x0
=
out_dst_down_x0;
        out_dst_up_x1
=
out_dst_down_x1;
        
return (out_dst_down_x0<
out_dst_down_x1);
    }
    
bool
 next_clip_line_down()
    {
        
++
out_dst_down_y;
        
if (!find_begin(out_dst_down_y,out_dst_down_x0,out_dst_down_x1)) return false
;
        find_end(out_dst_down_y,out_dst_down_x0,out_dst_down_x1);
        
return (out_dst_down_x0<
out_dst_down_x1);
    }
    
bool
 next_clip_line_up()
    {
        
--
out_dst_up_y;
        
if (!find_begin(out_dst_up_y,out_dst_up_x0,out_dst_up_x1)) return false
;
        find_end(out_dst_up_y,out_dst_up_x0,out_dst_up_x1);
        
return (out_dst_up_x0<
out_dst_up_x1);
    }
};

 

void PicRotary3_CopyLine(TARGB32* pDstLine,long dstCount,long Ax_16,long Ay_16,
                        
long srcx0_16,long srcy0_16,TARGB32* pSrcLine,long
 src_byte_width)
{
    
for (long x=0;x<dstCount;++
x)
    {
        pDstLine[x]
=Pixels(pSrcLine,src_byte_width,srcx0_16>>16,srcy0_16>>16
);
        srcx0_16
+=
Ax_16;
        srcy0_16
+=
Ay_16;
    }
}

void PicRotary3(const TPicRegion& Dst,const TPicRegion& Src,double RotaryAngle,double ZoomX,double ZoomY,double move_x,double
 move_y)
{
    
if ( (fabs(ZoomX*Src.width)<1.0e-4|| (fabs(ZoomY*Src.height)<1.0e-4) ) return//太小的缩放比例认为已经不可见

    double tmprZoomXY=1.0/(ZoomX*ZoomY);  
    
double rZoomX=tmprZoomXY*
ZoomY;
    
double rZoomY=tmprZoomXY*
ZoomX;
    
double
 sinA,cosA;
    SinCos(RotaryAngle,sinA,cosA);
    
long Ax_16=(long)(rZoomX*cosA*(1<<16
)); 
    
long Ay_16=(long)(rZoomX*sinA*(1<<16
)); 
    
long Bx_16=(long)(-rZoomY*sinA*(1<<16
)); 
    
long By_16=(long)(rZoomY*cosA*(1<<16
)); 
    
double rx0=Src.width*0.5;  //(rx0,ry0)为旋转中心 

    double ry0=Src.height*0.5
    
long Cx_16=(long)((-(rx0+move_x)*rZoomX*cosA+(ry0+move_y)*rZoomY*sinA+rx0)*(1<<16
));
    
long Cy_16=(long)((-(rx0+move_x)*rZoomX*sinA-(ry0+move_y)*rZoomY*cosA+ry0)*(1<<16
)); 

    TRotaryClipData rcData;
    rcData.Ax_16
=
Ax_16;
    rcData.Bx_16
=
Bx_16;
    rcData.Cx_16
=
Cx_16;
    rcData.Ay_16
=
Ay_16;
    rcData.By_16
=
By_16;
    rcData.Cy_16
=
Cy_16;
    rcData.dst_width
=
Dst.width;
    rcData.dst_height
=
Dst.height;
    rcData.src_width
=
Src.width;
    rcData.src_height
=
Src.height;
    
if (!rcData.inti_clip(move_x,move_y)) return
;

    TARGB32
* pDstLine=
Dst.pdata;
    ((TUInt8
*&)pDstLine)+=(Dst.byte_width*
rcData.out_dst_down_y);
    
while (true//to down

    {
        
long y=
rcData.out_dst_down_y;
        
if (y>=Dst.height) break
;
        
if (y>=0
)
        {
            
long x0=
rcData.get_down_x0();
            PicRotary3_CopyLine(
&pDstLine[x0],rcData.get_down_x1()-
x0,Ax_16,Ay_16,
                rcData.out_src_x0_16,rcData.out_src_y0_16,Src.pdata,Src.byte_width);
        }
        
if (!rcData.next_clip_line_down()) break
;
        ((TUInt8
*&)pDstLine)+=
Dst.byte_width;
    }
   
    pDstLine
=
Dst.pdata;
    ((TUInt8
*&)pDstLine)+=(Dst.byte_width*
rcData.out_dst_up_y);
    
while (rcData.next_clip_line_up()) //to up

    {
        
long y=
rcData.out_dst_up_y;
        
if (y<0break
;
        ((TUInt8
*&)pDstLine)-=
Dst.byte_width;
        
if (y<
Dst.height)
        {
            
long x0=
rcData.get_up_x0();
            PicRotary3_CopyLine(
&pDstLine[x0],rcData.get_up_x1()-
x0,Ax_16,Ay_16,
                rcData.out_src_x0_16,rcData.out_src_y0_16,Src.pdata,Src.byte_width);
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
//速度测试:
//==============================================================================
// PicRotary3             280.9 fps
//////////////////////////////////////////////////////////////////////////////// 


F:使用SSE的MOVNTQ指令优化CPU写缓冲

 

 (仅改写了PicRotary3_CopyLine的实现)


void __declspec(naked) __stdcall PicRotarySSE_CopyLine(TARGB32* pDstLine,long dstCount,long Ax_16,long Ay_16,
                        
//                                      [esp+ 4]      [esp+ 8]      [esp+12]   [esp+16]

                        long srcx0_16,long srcy0_16,TARGB32* pSrcLine,long src_byte_width)
                        
//   [esp+20]      [esp+24]          [esp+28]      [esp+32]

{
    
//利用SSE的MOVNTQ指令优化写缓冲的汇编实现

    asm
    {
        push        ebx  
        push        esi  
        push        edi   
        push        ebp  
        
//esp offset 16

        mov         ebx,dword ptr [esp+ 8+16
        mov         esi,dword ptr [esp
+32+16

        mov         edi,dword ptr [esp
+28+16

        mov         eax,dword ptr [esp
+24+16

        mov         ecx,dword ptr [esp
+20+16

        dec         ebx 
        xor         edx,edx 
        test        ebx,ebx 
        mov         dword ptr [esp
+ 8+16
],ebx 
        jle         loop_bound 

        
//
jmp   loop_begin
        
//align 16

    loop_begin:
            mov         ebx,eax 
            add         eax,dword ptr [esp
+16+16

            sar         ebx,
16
 
            imul        ebx,esi 
            add         ebx,edi 
            mov         ebp,ecx 
            add         ecx,dword ptr [esp
+12+16

            sar         ebp,
16
 
            MOVD        MM0,dword ptr [ebx
+ebp*4

            mov         ebx,eax 
            add         eax,dword ptr [esp
+16+16

            sar         ebx,
16
 
            imul        ebx,esi 
            mov         ebp,ecx 
            add         ecx,dword ptr [esp
+12+16

            sar         ebp,
16
 
            add         ebx,edi 
            MOVD        MM1,dword ptr [ebx
+ebp*4

            mov         ebp,dword ptr [esp
+ 4+16

            PUNPCKlDQ   MM0,MM1
            mov         ebx,dword ptr [esp
+ 8+16

            MOVNTQ      qword ptr [ebp
+edx*4
],MM0 
            add         edx,
2
 
            cmp         edx,ebx  
            jl          loop_begin 

            EMMS

    loop_bound:
        cmp         edx,ebx 
        jne         loop_bound_end 
        sar         eax,
16
 
        imul        eax,esi 
        sar         ecx,
16
 
        add         eax,edi 
        mov         eax,dword ptr [eax
+ecx*4

        mov         ecx,dword ptr [esp
+ 4+16

        mov         dword ptr [ecx
+edx*4
],eax 
    loop_bound_end: 
        pop         ebp  
        pop         edi  
        pop         esi  
        pop         ebx  
        ret         
32
  
    }
}

void PicRotarySSE(const TPicRegion& Dst,const TPicRegion& Src,double RotaryAngle,double ZoomX,double ZoomY,double move_x,double
 move_y)
{
    
if ( (fabs(ZoomX*Src.width)<1.0e-4|| (fabs(ZoomY*Src.height)<1.0e-4) ) return//太小的缩放比例认为已经不可见

    double tmprZoomXY=1.0/(ZoomX*ZoomY);  
    
double rZoomX=tmprZoomXY*
ZoomY;
    
double rZoomY=tmprZoomXY*
ZoomX;
    
double
 sinA,cosA;
    SinCos(RotaryAngle,sinA,cosA);
    
long Ax_16=(long)(rZoomX*cosA*(1<<16
)); 
    
long Ay_16=(long)(rZoomX*sinA*(1<<16
)); 
    
long Bx_16=(long)(-rZoomY*sinA*(1<<16
)); 
    
long By_16=(long)(rZoomY*cosA*(1<<16
)); 
    
double rx0=Src.width*0.5;  //(rx0,ry0)为旋转中心 

    double ry0=Src.height*0.5
    
long Cx_16=(long)((-(rx0+move_x)*rZoomX*cosA+(ry0+move_y)*rZoomY*sinA+rx0)*(1<<16
));
    
long Cy_16=(long)((-(rx0+move_x)*rZoomX*sinA-(ry0+move_y)*rZoomY*cosA+ry0)*(1<<16
)); 

    TRotaryClipData rcData;
    rcData.Ax_16
=
Ax_16;
    rcData.Bx_16
=
Bx_16;
    rcData.Cx_16
=
Cx_16;
    rcData.Ay_16
=
Ay_16;
    rcData.By_16
=
By_16;
    rcData.Cy_16
=
Cy_16;
    rcData.dst_width
=
Dst.width;
    rcData.dst_height
=
Dst.height;
    rcData.src_width
=
Src.width;
    rcData.src_height
=
Src.height;
    
if (!rcData.inti_clip(move_x,move_y)) return
;

    TARGB32
* pDstLine=
Dst.pdata;
    ((TUInt8
*&)pDstLine)+=(Dst.byte_width*
rcData.out_dst_down_y);
    
while (true//to down

    {
        
long y=
rcData.out_dst_down_y;
        
if (y>=Dst.height) break
;
        
if (y>=0
)
        {
            
long x0=
rcData.get_down_x0();
            PicRotarySSE_CopyLine(
&pDstLine[x0],rcData.get_down_x1()-
x0,Ax_16,Ay_16,
                rcData.out_src_x0_16,rcData.out_src_y0_16,Src.pdata,Src.byte_width);
        }
        
if (!rcData.next_clip_line_down()) break
;
        ((TUInt8
*&)pDstLine)+=
Dst.byte_width;
    }
   
    pDstLine
=
Dst.pdata;
    ((TUInt8
*&)pDstLine)+=(Dst.byte_width*
rcData.out_dst_up_y);
    
while (rcData.next_clip_line_up()) //to up 

    {
        
long y=
rcData.out_dst_up_y;
        
if (y<0break
;
        ((TUInt8
*&)pDstLine)-=
Dst.byte_width;
        
if (y<
Dst.height)
        {
            
long x0=
rcData.get_up_x0();
            PicRotarySSE_CopyLine(
&pDstLine[x0],rcData.get_up_x1()-
x0,Ax_16,Ay_16,
                rcData.out_src_x0_16,rcData.out_src_y0_16,Src.pdata,Src.byte_width);
        }
    }

    asm  sfence 
//刷新写入

}

////////////////////////////////////////////////////////////////////////////////
//速度测试:
//==============================================================================
// PicRotarySEE           306.3 fps
////////////////////////////////////////////////////////////////////////////////

F':尝试利用SSE2新增的MOVNTI指令优化CPU写缓冲


void __declspec(naked) __stdcall PicRotarySSE2_CopyLine(TARGB32* pDstLine,long dstCount,long Ax_16,long Ay_16,
                        
//                                       [esp+ 4]      [esp+ 8]      [esp+12]   [esp+16]

                        long srcx0_16,long srcy0_16,TARGB32* pSrcLine,long src_byte_width)
                        
//   [esp+20]      [esp+24]          [esp+28]      [esp+32]

{
    
//利用SSE2的MOVNTI指令优化写缓冲的汇编实现

    asm
    {
        push        ebx  
        push        esi  
        push        edi   
        push        ebp  
        
//esp offset 16

        mov         ebx,dword ptr [esp+ 8+16
        mov         esi,dword ptr [esp
+32+16

        mov         edi,dword ptr [esp
+28+16

        mov         eax,dword ptr [esp
+24+16

        mov         ecx,dword ptr [esp
+20+16

        dec         ebx 
        xor         edx,edx 
        test        ebx,ebx 
        mov         dword ptr [esp
+ 8+16
],ebx 
        jle         loop_bound 

        jmp   loop_begin
        align 
16

    loop_begin:
        mov         ebx,eax 
        add         eax,dword ptr [esp
+16+16
        sar         ebx,
16
 
        imul        ebx,esi 
        add         ebx,edi 
        mov         ebp,ecx 
        add         ecx,dword ptr [esp
+12+16

        sar         ebp,
16
 
        mov         ebx,dword ptr [ebx
+ebp*4

        mov         ebp,dword ptr [esp
+ 4+16

        MOVNTI         dword ptr [ebp
+edx*4
],ebx 
        mov         ebx,eax 
        add         eax,dword ptr [esp
+16+16

        sar         ebx,
16
 
        imul        ebx,esi 
        mov         ebp,ecx 
        add         ecx,dword ptr [esp
+12+16

        sar         ebp,
16
 
        add         ebx,edi 
        mov         ebx,dword ptr [ebx
+ebp*4

        mov         ebp,dword ptr [esp
+4+16

        MOVNTI         dword ptr [ebp
+edx*4+4
],ebx 
        mov         ebx,dword ptr [esp
+ 8+16

        add         edx,
2
 
        cmp         edx,ebx 
        jl          loop_begin 
    loop_bound:
        cmp         edx,ebx 
        jne         loop_bound_end 
        sar         eax,
16
 
        imul        eax,esi 
        sar         ecx,
16
 
        add         eax,edi 
        mov         eax,dword ptr [eax
+ecx*4

        mov         ecx,dword ptr [esp
+ 4+16

        mov         dword ptr [ecx
+edx*4
],eax 
    loop_bound_end: 
        pop         ebp  
        pop         edi  
        pop         esi  
        pop         ebx  
        ret         
32
  
    }
}

void PicRotarySSE2(const TPicRegion& Dst,const TPicRegion& Src,double RotaryAngle,double ZoomX,double ZoomY,double move_x,double
 move_y)
{
    
if ( (fabs(ZoomX*Src.width)<1.0e-4|| (fabs(ZoomY*Src.height)<1.0e-4) ) return//太小的缩放比例认为已经不可见

    double tmprZoomXY=1.0/(ZoomX*ZoomY);  
    
double rZoomX=tmprZoomXY*
ZoomY;
    
double rZoomY=tmprZoomXY*
ZoomX;
    
double
 sinA,cosA;
    SinCos(RotaryAngle,sinA,cosA);
    
long Ax_16=(long)(rZoomX*cosA*(1<<16
)); 
    
long Ay_16=(long)(rZoomX*sinA*(1<<16
)); 
    
long Bx_16=(long)(-rZoomY*sinA*(1<<16
)); 
    
long By_16=(long)(rZoomY*cosA*(1<<16
)); 
    
double rx0=Src.width*0.5;  //(rx0,ry0)为旋转中心 

    double ry0=Src.height*0.5
    
long Cx_16=(long)((-(rx0+move_x)*rZoomX*cosA+(ry0+move_y)*rZoomY*sinA+rx0)*(1<<16
));
    
long Cy_16=(long)((-(rx0+move_x)*rZoomX*sinA-(ry0+move_y)*rZoomY*cosA+ry0)*(1<<16
)); 

    TRotaryClipData rcData;
    rcData.Ax_16
=
Ax_16;
    rcData.Bx_16
=
Bx_16;
    rcData.Cx_16
=
Cx_16;
    rcData.Ay_16
=
Ay_16;
    rcData.By_16
=
By_16;
    rcData.Cy_16
=
Cy_16;
    rcData.dst_width
=
Dst.width;
    rcData.dst_height
=
Dst.height;
    rcData.src_width
=
Src.width;
    rcData.src_height
=
Src.height;
    
if (!rcData.inti_clip(move_x,move_y)) return
;

    TARGB32
* pDstLine=
Dst.pdata;
    ((TUInt8
*&)pDstLine)+=(Dst.byte_width*
rcData.out_dst_down_y);
    
while (true//to down

    {
        
long y=
rcData.out_dst_down_y;
        
if (y>=Dst.height) break
;
        
if (y>=0
)
        {
            
long x0=
rcData.get_down_x0();
            PicRotarySSE2_CopyLine(
&pDstLine[x0],rcData.get_down_x1()-
x0,Ax_16,Ay_16,
                rcData.out_src_x0_16,rcData.out_src_y0_16,Src.pdata,Src.byte_width);
        }
        
if (!rcData.next_clip_line_down()) break
;
        ((TUInt8
*&)pDstLine)+=
Dst.byte_width;
    }
   
    pDstLine
=
Dst.pdata;
    ((TUInt8
*&)pDstLine)+=(Dst.byte_width*
rcData.out_dst_up_y);
    
while (rcData.next_clip_line_up()) //to up 

    {
        
long y=
rcData.out_dst_up_y;
        
if (y<0break
;
        ((TUInt8
*&)pDstLine)-=
Dst.byte_width;
        
if (y<
Dst.height)
        {
            
long x0=
rcData.get_up_x0();
            PicRotarySSE2_CopyLine(
&pDstLine[x0],rcData.get_up_x1()-
x0,Ax_16,Ay_16,
                rcData.out_src_x0_16,rcData.out_src_y0_16,Src.pdata,Src.byte_width);
        }
    }

    asm  sfence 
//刷新写入

}

////////////////////////////////////////////////////////////////////////////////
//速度测试:
//==============================================================================
// PicRotarySEE2          304.2 fps
////////////////////////////////////////////////////////////////////////////////

一张效果图:
 //程序使用的调用参数:
    const long testcount=2000;
    long dst_wh=1004;
    for (int i=0;i<testcount;++i)
    {
        double zoom=rand()*(1.0/RAND_MAX)+0.5;
        PicRotarySSE(ppicDst,ppicSrc,rand()*(PI*2/RAND_MAX),zoom,zoom,((dst_wh+ppicSrc.width)*rand()*(1.0/RAND_MAX)-ppicSrc.width),(dst_wh+ppicSrc.height)*rand()*(1.0/RAND_MAX)-ppicSrc.height);
    }

 //ps:如果很多时候源图片绘制时可能落在目标区域的外面,那么需要写一个剪切算法快速排除不必要的绘制

 

一张测试函数速度的时候生成的图像:


G:旋转测试的结果放到一起:

//注:测试图片都是800*600的图片旋转到1004*1004的图片中心,测试成绩取各个旋转角度的平均速度值 
////////////////////////////////////////////////////////////////////////////////
//速度测试:  (测试CPU为AMD64x2 4200+(2.37G),单线程)
//==============================================================================
// PicRotary0              34.9 fps
// PicRotary1              62.0 fps
// PicRotary2             134.2 fps
// PicRotary3             280.9 fps
// PicRotarySEE           306.3 fps
// PicRotarySEE2          304.2 fps
//(PicRotarySSE2_Block    316.6 fps (参见《下篇 补充话题》))
////////////////////////////////////////////////////////////////////////////////

补充Intel Core2 4400上的测试成绩:

////////////////////////////////////////////////////////////////////////////////
//速度测试:  (测试CPU为Intel Core2 4400(2.00G)单线程)
//==============================================================================
// PicRotary0              58.6 fps
// PicRotary1              82.1 fps
// PicRotary2             167.9 fps
// PicRotary3             334.9 fps
// PicRotarySEE           463.1 fps
// PicRotarySEE2          449.3 fps
//(PicRotarySSE2_Block    351.3 fps (参见《下篇 补充话题》))
////////////////////////////////////////////////////////////////////////////////

//ps:文章的下篇将进一步优化图片旋转的质量(使用二次线性插值、三次卷积插值和MipMap链),并完美的处理边缘的锯齿,并考虑介绍颜色的Alpha Blend混合


(希望读者能在这一系列的文章中不仅能学到旋转和缩放,还能够学到一些优化的基本技巧和思路;也欢迎指出文章中的错误、我没有做到的优化、改进意见 等)

没有更多推荐了,返回首页