GetPixel优化,速度有质提升300倍.

来源:互联网 发布:怎样查看网卡mac地址 编辑:程序博客网 时间:2024/05/02 02:22
DC.GetPixel(j, i)低效率的优化

当我们要扫描一幅图350*350=122,500个像素.其实很小的一幅图.原代码需要3.284秒 优化后只需0.13秒,最后再优化只需0.081秒.
当我们在用CORE i5 i7的飞机时代,同志们有没有想象因为要在ARM(拖拉机时代)的800MHz内存32M的感觉呢?


说回正题:
在用DC.GetPixel(j, i)对这个图逐点扫描,发觉时间竟要3.284秒.实在难以接受.
那么是否有好的办法加快呢?
答案是肯定的.内存直接操作.
首先需要将DC转为内存块
好在有这么一个函数可用CreateDIBSection

HBITMAP m_hDIBitmap1 = CreateDIBSection(m_hDIBDC1, (BITMAPINFO *)&m_hdr, DIB_RGB_COLORS,(void **)&m_Bitmap1, NULL, 0);
具体用法查看MSDN

BitBlt(m_hDIBDC1,0,0,m_nWidth,m_nHeight,m_memDC,0,0,SRCCOPY);  //从m_memDC图中能转到我们的内存m_Bitmap1中了,这是关键.
//当然这两三步因为是大遍的内存拷贝,耗时可以不计. 当然用DWORD dwS=GetTickCount();来查看也看出转为内存的时间也只需几毫秒.


到我们的具体代码段

 for (i=0;i<m_nHeight;i++)
 {
  j=0;
  do
  {   
   while (j <= m_nWidth-1  && m_memDC.GetPixel(j, i) == colorTransparent)
   {
                j++;
   }
   iLeftX= j;
   while (j <= m_nWidth-1  && m_memDC.GetPixel(j, i) != colorTransparent)
   {
                ++j;
   }

   
   pRect->left = m_iLeftX+iLeftX;
   pRect->right = m_iLeftX+j;
   pRect->top = m_iTopY+i;
   pRect->bottom = m_iTopY+i+1;
   pRgnData->rdh.nCount++;
   
   pRect++;
   //这里要判断是否多于申请的内存
   if (pRgnData->rdh.nCount>=MAXNUM)
   {
   
   }  
  }while(j <m_nWidth-1 );
 }
//这段代码用时达到惊人的3秒多.

为了代码的简结,将判断是否相等的代码写成函数
bool GetColorYes(BYTE* byte,int iWidth,int iHeight ,int x,int y, BYTE r,BYTE g,BYTE b)
{ //计算内存的其中一个点是否相同颜色
 int nPixelSize=4;
 BYTE btB2,btG2,btR2;
 
 btB2 = byte[(iHeight-y-1) * iWidth * nPixelSize   + x * nPixelSize ] ;
 btG2 = byte[(iHeight-y-1) * iWidth * nPixelSize   + x * nPixelSize + 1 ] ;
 btR2 = byte[(iHeight-y-1) * iWidth * nPixelSize   + x * nPixelSize + 2 ] ;
 
 if ( btB2==b && btG2==g && btR2==r )
  return true;
 else
  return false;
}

//对应的汇编
汇编指令
.text:00013668 CDZQSPLASH_DLG__GetColorYes             ; CODE XREF: CDZQSPLASH_DLG__SetRgnSplash+368p
.text:00013668                                         ; CDZQSPLASH_DLG__SetRgnSplash+3CCp
.text:00013668                                         ; DATA XREF: ...
.text:00013668
.text:00013668 arg_0           =  0 //参数1
.text:00013668 arg_4           =  4 //参数2
.text:00013668 arg_8           =  8 //参数3
.text:00013668 arg_C           =  0xC //参数4
.text:00013668 arg_10          =  0x10 //参数5
.text:00013668
01.text:00013668                 STMFD   SP!, {R4,LR}  //将寄存器列表中的R4-LR存入堆栈
02.text:0001366C                 LDR     R0, [SP,#8+arg_4] //读入参数2
03.text:00013670                 LDR     LR, [SP,#8+arg_0] //读入参数1
04.text:00013674                 LDRB    R4, [SP,#8+arg_10] //读入参数5
05.text:00013678                 SUB     R3, R3, R0  //减法
06.text:0001367C                 SUB     R3, R3, #1  //再减 1
07.text:00013680                 MLA     R2, R3, R2, LR
08.text:00013684                 LDRB    R3, [R1,R2,LSL#2]! 
09.text:00013688                 CMP     R3, R4
10.text:0001368C                 BNE     loc_136B0
11.text:00013690                 LDRB    R2, [R1,#1]
12.text:00013694                 LDRB    R3, [SP,#8+arg_C]
13.text:00013698                 CMP     R2, R3
14.text:0001369C                 LDREQB  R2, [R1,#2]
15.text:000136A0                 LDREQB  R3, [SP,#8+arg_8]
16.text:000136A4                 CMPEQ   R2, R3
17.text:000136A8                 MOVEQ   R0, #1
18.text:000136AC                 LDMEQFD SP!, {R4,PC}
19.text:000136B0
20.text:000136B0 loc_136B0                               ; CODE XREF: CDZQSPLASH_DLG__GetColorYes+24j
21.text:000136B0                 MOV     R0, #0
22.text:000136B4                 LDMFD   SP!, {R4,PC}
.text:000136B4 ; End of function CDZQSPLASH_DLG__GetColorYes

//可以看到比较CMP执行为3次,指令流水线还是很长,效果比直接用GetPixel已好了很多很多,快了接近300倍。别小看这300倍,当那大图像处理只需要运行一天的程序,没优化的代码则需要用300天?有点吓人。。。

 for (i=0;i<m_nHeight;i++)
 {
  j=0;
  do
  {
   while (j <= m_nWidth-1  && GetColorYes(m_Bitmap1,m_nWidth,m_nHeight,j,i,btR1,btG1,btB1))
   {
                j++;
   }
   iLeftX= j;
   while (j <= m_nWidth-1  && !GetColorYes(m_Bitmap1,m_nWidth,m_nHeight,j,i,btR1,btG1,btB1))
   {
                ++j;
   }

   
   pRect->left = m_iLeftX+iLeftX;
   pRect->right = m_iLeftX+j;
   pRect->top = m_iTopY+i;
   pRect->bottom = m_iTopY+i+1;
   pRgnData->rdh.nCount++;
   
   pRect++;
   //这里要判断是否多于申请的内存
   if (pRgnData->rdh.nCount>=MAXNUM)
   {
   
   }  
  }while(j <m_nWidth-1 );
 }
//这段代码耗时0.13秒.

是否还有优化的可能呢?回过头来看,因为图像的掩色刚好是B 还算好,只需一次CMP就跳了,但如果是R呢?那真是可悲的一件事。。。
刚好内存中的数据为BGRP.32位与一个int的值对应。再作优化存在可能了。
那就是构造一个int类型.

 unsigned int *iRGB;// 无符号int
 pRGB[0]=btB1;
 pRGB[1]=btG1;
 pRGB[2]=btR1;
 pRGB[3]=0;
 iRGB=(unsigned int *)pRGB; //强制转换

bool GetColorYesInt(BYTE* byte,int iWidth,int iHeight ,int x,int y,unsigned int *iRGB )
{ //计算内存的其中一个点是否相同颜色
 int nPixelSize=4;
 unsigned int *iBtRGB;// 无符号int

 iBtRGB= (unsigned int *)(byte+((iHeight-y-1) * iWidth * nPixelSize   + x * nPixelSize ));
 if (iBtRGB[0]==iRGB[0] )
  return true;
 else
  return false;
}
对应汇编指令
汇编指令
.text:00013668 CDZQSPLASH_DLG__GetColorYesInt          ; DATA XREF: .pdata:00039338o
.text:00013668
.text:00013668 arg_0           =  0
.text:00013668 arg_4           =  4
.text:00013668 arg_8           =  8
.text:00013668
01.text:00013668                 STMFD   SP!, {R4,LR}
02.text:0001366C                 LDR     R0, [SP,#8+arg_4]
03.text:00013670                 LDR     LR, [SP,#8+arg_0]
04.text:00013674                 LDR     R4, [SP,#8+arg_8]
05.text:00013678                 SUB     R3, R3, R0
06.text:0001367C                 SUB     R3, R3, #1
07.text:00013680                 MLA     R2, R3, R2, LR
08.text:00013684                 LDR     R0, [R4]
09.text:00013688                 LDR     R3, [R1,R2,LSL#2]
10.text:0001368C                 CMP     R3, R0
11.text:00013690                 MOVEQ   R0, #1
12.text:00013694                 MOVNE   R0, #0
13.text:00013698                 LDMFD   SP!, {R4,PC}
.text:00013698 ; End of function CDZQSPLASH_DLG__GetColorYesInt

从上面的汇编可以看到流水线减至13行指令,并且,只进行一次比较。
 unsigned int *iRGB;// 无符号int
 pRGB[0]=btB1;
 pRGB[1]=btG1;
 pRGB[2]=btR1;
 pRGB[3]=0;
 iRGB=(unsigned int *)pRGB; //强制转换

 for (i=0;i<m_nHeight;i++)
 {
  j=0;
  do
  {
   while (j <= m_nWidth-1  && GetColorYesInt(m_Bitmap1,m_nWidth,m_nHeight,j,i,iRGB))
   {
                j++;
   }
   iLeftX= j;
   while (j <= m_nWidth-1  && !GetColorYesInt(m_Bitmap1,m_nWidth,m_nHeight,j,i,iRGB))
   {
                ++j;
   }

   
   pRect->left = m_iLeftX+iLeftX;
   pRect->right = m_iLeftX+j;
   pRect->top = m_iTopY+i;
   pRect->bottom = m_iTopY+i+1;
   pRgnData->rdh.nCount++;
   
   pRect++;
   //这里要判断是否多于申请的内存
   if (pRgnData->rdh.nCount>=MAXNUM)
   {
   
   }  
  }while(j <m_nWidth-1 );
 }

最后的疗效,实在不错..时间只是用了 0.08秒.

学会看汇编是优代码的一个好方法..

小试挖空一个图片看看看...

 

原图

最后效果图

 

 

另:CSDN的这个编辑器好弱.代码段没显示出来的东东好垃圾,只好粘算了...搞得都没兴趣写什么文了...