打印
[技术讨论]

百问FB显示开发图像处理 - 图像调整

[复制链接]
169|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
神棍地海棠|  楼主 | 2024-12-2 09:38 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
## 2.4 图像调整

### 2.4.1 图像的缩放

#### 2.4.1.1 图像缩放算法浅析

图像缩放算法有很多种,这里参考网友"lantianyu520"所著的"图像缩放算法"。

原理浅析

​                要理解这个图像缩放算法的原理,最重要的是需要理解:对于图像上的每一个像素点,它缩放前后,相对于整个图像的比例应该是一样的。

比如:

​                以一个长度和宽度分别为200,100的长方形为例,将其放大两倍,那么缩放后的长度和宽度为400,200。

为方便理解,我们建立一个笛卡尔坐标系,把这个长方形左下角的顶点放到坐标(0,0)位置,四个点的坐标分别为:(0,0),(0,100),(200,0),(200,100)。

​                假设此时对长方形中的坐标点(40,50),它的x坐标相对于长的比值是40/200=0.2,y坐标相对于宽的比值是50/100=0.5,那么该点的变换后的坐标Dx,Dy则应满足:Dx/400 = 5;Dy/200 = 0.5,这样,缩放后的坐标就可以算出来了。

​                根据上面的分析,设缩放前的像素点坐标为(Sx,Sy),对应的缩放后的像素点坐标为(Dx,Dy),缩放前的图像长宽分别为Sw,Sh,缩放后的图像长宽分别为Dw,Dh,则有:

Sx/Dx = Sw/Dw,Sy/Dy = Sh/Dh

故有Sx = Dx * Sw/Dw,Sy = Dy * Sh/Dh,

#### 2.4.1.2源码编写:图像缩放算法

有了这个上面两条等式后,图像缩放算法的代码就好理解了。

下面的函数实现了基于上述原理实现的图像缩放算法:

```c
代码清单2.4
1.        /**********************************************************************
2.         * 函数名称: PicZoom
3.         * 功能描述: 近邻取样插值方法缩放图片
4.         *            注意该函数会分配内存来存放缩放后的图片,用完后要用free函数释放掉
5.         *            "近邻取样插值"的原理请参考网友"lantianyu520"所著的"图像缩放算法"
6.         * 输入参数: ptPicData - 内含缩放前后的图像数据
7.         *            fSize    - 缩放倍数
8.         * 输出参数: ptPicData->pucZoomData,内含缩放后的数据
9.         * 返 回 值: 0 - 成功, 其他值 - 失败
10.         ***********************************************************************/  
11.        int PicZoom(PT_PictureData ptPicData,float fSize)  
12.        {  
13.            ptPicData->iZoomWidth = ptPicData->iWidth * fSize;  
14.            ptPicData->iZoomHeight= ptPicData->iHeight* fSize;  
15.            unsigned long* pdwSrcXTable;  
16.            unsigned long x;  
17.            unsigned long y;  
18.            unsigned long dwSrcY;  
19.            unsigned char *pucDest;  
20.            unsigned char *pucSrc;  
21.            unsigned long dwPixelBytes = ptPicData->iBpp/8;  
22.            ptPicData->pucZoomData= malloc(sizeof(unsigned char) * ptPicData->iZoomWidth*ptPicData->iZoomHeight*ptPicData->iBpp/8);  
23.            pdwSrcXTable = malloc(sizeof(unsigned long) * ptPicData->iZoomWidth);  
24.            if (NULL == pdwSrcXTable){  
25.                printf("malloc error!\n");  
26.                return -1;  
27.            }  
28.          
29.            /* 这几个for循环的本质是Sx = Dx * Sw/Dw,Sy = Dy * Sh/Dh*/  
30.            for (x = 0; x < ptPicData->iZoomWidth; x++){//生成表 pdwSrcXTable  
31.                /* 第一个for循环对应x方向的坐标
32.             * pdwSrcXTable[x] 对应Sx,
33.             * x 对应Dx,
34.             * ptPicData->iWidth 对应Sw
35.             * ptPicData->iZoomWidth 对应 Dw*/   
36.                pdwSrcXTable[x]=(x*ptPicData->iWidth/ptPicData->iZoomWidth);  
37.            }  
38.          
39.            for (y = 0; y < ptPicData->iZoomHeight; y++){  
40.            /* 第2个循环对应y方向的坐标
41.             * dwSrcY 对应Sy,
42.             * y 对应Dy,
43.             * ptPicData->iHeight 对应Sh
44.             * ptPicData->iZoomHeight 对应 Dh*/      
45.                dwSrcY = (y * ptPicData->iHeight / ptPicData->iZoomHeight);  
46.            /* 根据这些可算得各像素点的RGB数据存放的地址 */  
47.                pucDest = ptPicData->pucZoomData + y*ptPicData->iZoomWidth*3;  
48.                pucSrc  = ptPicData->pucRgbData + dwSrcY*ptPicData->iWidth*3;  
49.          
50.            /* 最后拷贝数据 */         
51.                for (x = 0; x <ptPicData->iZoomWidth; x++){  
52.                     memcpy(pucDest+x*dwPixelBytes, pucSrc+pdwSrcXTable[x]*dwPixelBytes, dwPixelBytes);  
53.                }  
54.            }  
55.          
56.            free(pdwSrcXTable);  
57.            return 0;  
58.        }  
```

### 2.4.2 图像的旋转

#### 2.4.2.1 图像旋转算法浅析

这里的图像旋转算法原理参考网友"落叶的思维"所著的"图像旋转算法与实现"

原理浅析

这个旋转算法的原理的关键点有两个:

1. 原图像是以图像的左下角为原点建立笛卡尔坐标系的,而旋转一般是以图像的中心作为旋转点旋转的。

因此为了便于转换,我们先约定两个坐标系,一个是以图像左下角为原点建立的坐标系,称为坐标系A,这也是原图像的坐标系。一个是以图像中心为原点建立的坐标系,称为坐标系B。

由此,可以知道这个旋转算法的步骤:先将坐标系A下的坐标转换为坐标系B下的坐标,然后在坐标系B下进行图像的旋转。

在坐标系B下,我们假设点(x0,y0)距离原点的距离为r,点与原点之间的连线与x轴的夹角为b,旋转的角度为a,旋转后的点为(x1,y1), 如下图所示。

![ImageProcess_Image005](http://photos.100ask.net/NewHomeSite/ImageProcess_Image005.jpeg)

那么有以下结论:

x0=rcosb;y0=rsinb

x1 = rcos(b-a) = rcosbcosa+rsinbsina=x0cosa+y0sina;

y1=rsin(b-a)=rsinbcosa-rcosbsina=-x0sina+y0cosa;

最后,由于我们显示图像的RGB数据还是要在坐标系A下获取的,我们最后只需要将坐标系B下的x1,y1转换回坐标系A下的坐标就可以了。

旋转后的图像的长和宽会发生变化,因此要计算新图像的长和宽。

由几何关系可知,新图像的长和宽分别是旋转后,对角坐标相见后的最大值

#### 2.4.2.2 源码编写:图像旋转算法

```c
代码清单2.5
1.         #define PI 3.1415926535  
2.        //角度到弧度转化  
3.        #define RADIAN(angle) ((angle)*PI/180.0)  
4.          
5.          
6.          
7.          
8.          
9.        typedef struct ConcernCoor {  
10.            int iLTx;// left top x  
11.            int iLTy;//left top y  
12.            int iLBx;//left bottom x  
13.            int iLBy;//left bottom y  
14.            int iRTx;//right top x  
15.            int iRTy;//right top y  
16.            int iRBx;// right bottom x  
17.            int iRBy;// right bottom y  
18.        }T_ConcernCoor, *PT_ConcernCoor;  
19.          
20.          
21.        /**********************************************************************
22.         * 函数名称: max
23.         * 功能描述:比较两个参数,返回较大值
24.         * 输入参数:x,y均为int型
25.         * 输出参数: 无
26.         * 返 回 值: x,y中的较大值
27.         ***********************************************************************/  
28.        static int max(int x,int y){  
29.            return x>y?x:y;  
30.        }  
31.        /**********************************************************************
32.         * 函数名称: PicRotate
33.         * 功能描述: 旋转图片
34.         *            注意该函数会分配内存来存放缩放后的图片,用完后要用free函数释放掉
35.         *              参考网友"落叶的思维"所著的"图像旋转算法与实现"
36.         * 输入参数: ptPicData - 内含图片的象素数据
37.         *            fAngle    - 旋转角度,0<=angle<=360
38.         * 输出参数: ptPicData->pucRotateData,内含旋转后的rgb数据
39.         * 返 回 值: 0 - 成功, 其他值 - 失败
40.         ***********************************************************************/  
41.        int PicRotate(PT_PictureData ptPicData,float fAngle)  
42.        {  
43.            int i ,j;  
44.            T_ConcernCoor tConCor,tRonCor;  
45.            //原图像每一行去除偏移量的字节数  
46.            //int iSrcLineSize = bitCount * srcW / 8;  
47.            int iSrcLineSize = ptPicData->iBpp* ptPicData->iZoomWidth / 8;  
48.            int iDesLineSize;  
49.            int iX;//旋转后的x坐标  
50.            int iY; //旋转后的y坐标  
51.          
52.               /* 将坐标系A下的坐标转换为坐标系B下的坐标,
53.                * 用于计算旋转后的图像的宽和高  
54.                * tConCor用于存放坐标系B下旋转前的坐标
55.                * tRonCor用于存放坐标系B下旋转后的坐标*/  
56.               tConCor.iLTx = -ptPicData->iZoomWidth/2; tConCor.iLTy = ptPicData->iZoomHeight/2;  
57.            tConCor.iRTx = ptPicData->iZoomWidth/2; tConCor.iRTy = ptPicData->iZoomHeight/2;  
58.            tConCor.iLBx = -ptPicData->iZoomWidth/2;tConCor.iLBy = -ptPicData->iZoomHeight/2;  
59.            tConCor.iRBx = ptPicData->iZoomWidth/2;tConCor.iRBy = -ptPicData->iZoomHeight/2;  
60.          
61.          
62.            /* 计算坐标系B下旋转后的坐标 */  
63.            double sina = sin(RADIAN(fAngle));  
64.            double cosa = cos(RADIAN(fAngle));  
65.            tRonCor.iLTx =tConCor.iLTx * cosa + tConCor.iLTy * sina;  
66.            tRonCor.iLTy = -tConCor.iLTx * sina + tConCor.iLTy * cosa;  
67.            tRonCor.iRTx =tConCor.iRTx * cosa + tConCor.iRTy * sina;  
68.            tRonCor.iRTy = -tConCor.iRTx * sina + tConCor.iRTy * cosa;  
69.            tRonCor.iLBx = tConCor.iLBx * cosa + tConCor.iLBy * sina;  
70.            tRonCor.iLBy = -tConCor.iLBx * sina + tConCor.iLBy * cosa;  
71.            tRonCor.iRBx = tConCor.iRBx * cosa + tConCor.iRBy * sina;  
72.            tRonCor.iRBy = -tConCor.iRBx * sina + tConCor.iRBy * cosa;  
73.          
74.             
75.            /* 计算旋转后图像宽和高 */  
76.            ptPicData->iRotateWidth = max(abs(tRonCor.iRBx - tRonCor.iLTx),abs(tRonCor.iRTx - tRonCor.iLBx));  
77.            ptPicData->iRotateHeight = max(abs(tRonCor.iRBy - tRonCor.iLTy),abs(tRonCor.iRTy - tRonCor.iLBy));  
78.          
79.            /* 像素信息要保证3字节对齐,否则数据有可能出错*/  
80.            iDesLineSize = ((ptPicData->iRotateWidth* ptPicData->iBpp+ 23) / 24) * 3 ;  
81.            /* 分配旋转后的空间,注意这里要用旋转后的宽和高 */  
82.            ptPicData->pucRotateData = malloc(iDesLineSize * ptPicData->iRotateHeight);  
83.            if(NULL == ptPicData->pucRotateData){  
84.                printf("malloc error\n");  
85.                return -1;  
86.            }  
87.          
88.            /* 通过新图像的坐标,计算对应的原图像的坐标*
89.              * i,j坐标就是对应的坐标系B下的x1,y1*/  
90.            for (i = 0; i < ptPicData->iRotateHeight; i++){         
91.                for (j = 0; j < ptPicData->iRotateWidth; j++){  
92.                    /* 坐标系B下的x,y1坐标,经过逆运算转换得到iX,iY,这两个值对应x0,y0 */  
93.                    iX = (j - ptPicData->iRotateWidth / 2)*cos(RADIAN(360 - fAngle)) + (-i + ptPicData->iRotateHeight / 2)*sin(RADIAN(360 - fAngle));  
94.                    iY = -(j - ptPicData->iRotateWidth / 2)*sin(RADIAN(360 - fAngle)) + (-i + ptPicData->iRotateHeight / 2)*cos(RADIAN(360 - fAngle));  
95.                    /*如果这个坐标不在原图像内,则不赋值*/  
96.                    if (iX > ptPicData->iZoomWidth / 2 || iX < -ptPicData->iZoomWidth / 2 || iY > ptPicData->iZoomHeight / 2 || iY < -ptPicData->iZoomHeight / 2){  
97.                        continue;  
98.                    }  
99.                    /* 再将坐标系B下的x0,y0坐标,转换为坐标系A下的坐标 */  
100.                    int iXN = iX + ptPicData->iZoomWidth / 2;   
101.                 int iYN = abs(iY - ptPicData->iZoomHeight  / 2);  
102.                    /* 值拷贝*/  
103.                    memcpy(&ptPicData->pucRotateData[i * iDesLineSize + j * 3],&ptPicData->pucZoomData[iYN * iSrcLineSize + iXN * 3],3);   
104.                }  
105.            }  
106.          return 0;  
107.        }  
```

使用特权

评论回复

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

276

主题

284

帖子

0

粉丝