题外话:一直是打算把这本书的全部课后编程题写完的,中间断了几个月,一直忙于其他事。现在开始补上。
这道题我不清楚我理解的题意是不是正确的,这道题可以练习用OpenCV实现透视变换(可以用于矫正在3维环境下的图像失真),b&c部分则是实现图像放大缩小插值,旋转图像。所有的功能都和键盘事件处理联系起来,纯OpenCV实现,和OS无关。不过,在处理SHIFT键时,我取得是其在键盘上对应字符的ASCII码,好像OpenCV对键盘事件的支持不如对鼠标事件的支持。所以SHIFT键+小键盘上的数字键是不行的。
代码:
1 #include <opencv/highgui.h> 2 #include <opencv/cv.h> 3 #include <opencv_libs.h> 4 5 /* 6 *《学习OpenCV》第四章第七题 7 * 完成时间:0:23 10/4 星期五 2013 8 */ 9 10 #define WARPSTEPSIZE 0.01 // 透视变换矩阵每次的变化率 11 12 #define RESIZESTEPSIZE 0.1 // 放大、缩小每次的变化率 13 14 #define ROTATESTEPSIZE 10 // 旋转图像时每次旋转的角度(逆时针) 15 16 // 透视变换目标变换矩阵 17 CvPoint2D32f g_dstQuad[4]; 18 19 // image width & height 20 int g_width = 0; 21 int g_height = 0; 22 23 // rotate degree 24 int g_RotateDegree = 0; 25 26 /* 27 * function: Zoom in or zoom out an image. 28 * param: inorout -- indicate in or out(0 means zoom in; 1 means zoom out) 29 * param: the destination size. 30 */ 31 CvSize getZoomDstSize(int inorout) 32 { 33 if(inorout != 0 && inorout != 1) 34 { 35 return cvSize(-1, -1); 36 } 37 if(g_height == 0 || g_width == 0) 38 { 39 return cvSize(-1, -1); 40 } 41 42 // Zoom in 43 if(inorout == 0) 44 { 45 g_width += g_width * RESIZESTEPSIZE; 46 g_height += g_height * RESIZESTEPSIZE; 47 } 48 else if(inorout == 1) 49 { 50 g_width -= g_width * RESIZESTEPSIZE; 51 g_height -= g_height * RESIZESTEPSIZE; 52 53 if(g_height < 1) 54 { 55 g_height = 1; 56 } 57 else if(g_width < 1) 58 { 59 g_width = 1; 60 } 61 } 62 63 return cvSize(g_width, g_height); 64 } 65 66 void rotateImage(IplImage* img, IplImage *img_rotate,float degree) 67 { 68 // 旋转中心 69 CvPoint2D32f center; 70 center.x=float (img->width/2.0+0.5); 71 center.y=float (img->height/2.0+0.5); 72 // 计算二维旋转的仿射变换矩阵 73 float m[6]; 74 CvMat M = cvMat( 2, 3, CV_32F, m ); 75 cv2DRotationMatrix( center, degree,1, &M); 76 // 变换图像,并用黑色填充其余值 77 cvWarpAffine(img,img_rotate, &M,CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS,cvScalarAll(0) ); 78 } 79 80 /* 81 * function: change the g_dstQuad(minus or plus one of the element) 82 * param: index -- which element(0 -- 4) 83 * param: xory -- indicate x or y to change(0 means x; 1 means y) 84 * param: operation -- indicate which operation(0 means plus; 1 means minus) 85 * return: 0 means success. -1 means failed. 86 */ 87 int changeg_dstQuad(int index, int xory, int operation) 88 { 89 // param check 90 if( index > 3 || index < 0 || 91 (xory != 0 && xory != 1) || 92 (operation != 0 && operation != 1) || 93 g_width == 0 || g_height == 0) 94 { 95 return -1; 96 } 97 98 // plus 99 if(operation == 0) 100 { 101 if(xory == 0) // x 102 { 103 g_dstQuad[index].x += WARPSTEPSIZE * g_width; 104 if(g_dstQuad[index].x > g_width - 1) 105 { 106 g_dstQuad[index].x = g_width - 1; 107 } 108 } 109 else if(xory == 1) // y 110 { 111 g_dstQuad[index].y += WARPSTEPSIZE * g_height; 112 if(g_dstQuad[index].y > g_height - 1) 113 { 114 g_dstQuad[index].y = g_height -1; 115 } 116 } 117 } 118 119 // minus 120 else if (operation == 1) 121 { 122 if(xory == 0) // x 123 { 124 g_dstQuad[index].x -= WARPSTEPSIZE * g_width; 125 if(g_dstQuad[index].x < 0) 126 { 127 g_dstQuad[index].x = 0; 128 } 129 } 130 else if(xory == 1 ) // y 131 { 132 g_dstQuad[index].y -= WARPSTEPSIZE * g_height; 133 if(g_dstQuad[index].y < 0) 134 { 135 g_dstQuad[index].y = 0; 136 } 137 } 138 } 139 140 return 0; 141 } 142 143 int main() 144 { 145 const char * FILEPATH = "lena.bmp"; 146 IplImage * src = cvLoadImage(FILEPATH, CV_LOAD_IMAGE_UNCHANGED); 147 148 if(!src) 149 { 150 printf("load image error. exit "); 151 return -1; 152 } 153 154 CvPoint2D32f srcQuad[4]; 155 CvMat* warp_matrix = cvCreateMat(3, 3, CV_32FC1); 156 IplImage *dst; 157 158 cvNamedWindow("Source_Image", 1); 159 cvNamedWindow("Perspective_Warp", 1); 160 161 dst = cvCloneImage(src); 162 dst->origin = dst->origin; 163 cvZero(dst); 164 165 // image width & height 166 g_width = src->width; 167 g_height = src->height; 168 169 srcQuad[0].x = 0; // src Top left 170 srcQuad[0].y = 0; 171 srcQuad[1].x = g_width - 1; // src Top right 172 srcQuad[1].y = 0; 173 srcQuad[2].x = 0; // src Bottom left 174 srcQuad[2].y = g_height - 1; 175 srcQuad[3].x = g_width - 1; // src Bottom right 176 srcQuad[3].y = g_height - 1; 177 178 g_dstQuad[0].x = 0; // dst Top left 179 g_dstQuad[0].y = 0; 180 g_dstQuad[1].x = g_width - 1; // dst Top right 181 g_dstQuad[1].y = 0; 182 g_dstQuad[2].x = 0; // dst Bottom left 183 g_dstQuad[2].y = g_height - 1; 184 g_dstQuad[3].x = g_width - 1; // dst Bottom right 185 g_dstQuad[3].y = g_height - 1; 186 187 while(1) 188 { 189 cvShowImage("Source_Image", src); 190 char c = cvWaitKey(10); 191 192 int ret = 0; 193 switch(c) 194 { 195 // ESC 196 case 27: 197 goto end; 198 break; 199 // 0 -- 放大图像 200 case 48: 201 { 202 cvNamedWindow("Resize", 1); 203 CvSize dstSize = getZoomDstSize(0); 204 if(dstSize.height == -1 || dstSize.width == -1) 205 { 206 goto end; 207 } 208 209 IplImage* imageresize = cvCreateImage(dstSize, src->depth, src->nChannels); 210 if(!imageresize) 211 { 212 goto end; 213 } 214 cvResize(src, imageresize, CV_INTER_LINEAR); 215 cvShowImage("Resize", imageresize); 216 cvReleaseImage(&imageresize); 217 } 218 break; 219 // SHIFT + 0 ')' -- 缩小图像 220 case 41: 221 { 222 cvNamedWindow("Resize", 1); 223 CvSize dstSize = getZoomDstSize(1); 224 if(dstSize.height == -1 || dstSize.width == -1) 225 { 226 goto end; 227 } 228 229 IplImage* imageresize = cvCreateImage(dstSize, src->depth, src->nChannels); 230 if(!imageresize) 231 { 232 goto end; 233 } 234 cvResize(src, imageresize, CV_INTER_LINEAR); 235 cvShowImage("Resize", imageresize); 236 cvReleaseImage(&imageresize); 237 } 238 break; 239 // 9 -- 旋转图像(逆时针) 240 case 57: 241 { 242 cvNamedWindow("Rotate", 1); 243 244 IplImage* imagerotate = cvCreateImage(cvGetSize(src), src->depth, src->nChannels); 245 if(!imagerotate) 246 { 247 goto end; 248 } 249 g_RotateDegree += ROTATESTEPSIZE; 250 rotateImage(src, imagerotate, g_RotateDegree); 251 cvShowImage("Rotate", imagerotate); 252 cvReleaseImage(&imagerotate); 253 } 254 break; 255 // SHIFT + 9 '(' -- 旋转图像(顺时针) 256 case 40: 257 { 258 cvNamedWindow("Rotate", 1); 259 260 IplImage* imagerotate = cvCreateImage(cvGetSize(src), src->depth, src->nChannels); 261 if(!imagerotate) 262 { 263 goto end; 264 } 265 g_RotateDegree -= ROTATESTEPSIZE; 266 rotateImage(src, imagerotate, g_RotateDegree); 267 cvShowImage("Rotate", imagerotate); 268 cvReleaseImage(&imagerotate); 269 } 270 break; 271 // 1 272 case 49: 273 ret = changeg_dstQuad(0, 0, 0); 274 break; 275 // 2 276 case 50: 277 ret = changeg_dstQuad(0, 1, 0); 278 break; 279 // 3 280 case 51: 281 ret = changeg_dstQuad(1, 0, 0); 282 break; 283 // 4 284 case 52: 285 ret = changeg_dstQuad(1, 1, 0); 286 break; 287 // 5 288 case 53: 289 ret = changeg_dstQuad(2, 0, 0); 290 break; 291 // 6 292 case 54: 293 ret = changeg_dstQuad(2, 1, 0); 294 break; 295 // 7 296 case 55: 297 ret = changeg_dstQuad(3, 0, 0); 298 break; 299 // 8 300 case 56: 301 ret = changeg_dstQuad(3, 1, 0); 302 break; 303 // SHIFT + 1 '!' 304 case 33: 305 ret = changeg_dstQuad(0, 0, 1); 306 break; 307 // SHIFT + 2 '@' 308 case 64: 309 ret = changeg_dstQuad(0, 1, 1); 310 break; 311 // SHIFT + 3 '#' 312 case 35: 313 ret = changeg_dstQuad(1, 0, 1); 314 break; 315 // SHIFT + 4 '$' 316 case 36: 317 ret = changeg_dstQuad(1, 1, 1); 318 break; 319 // SHIFT + 5 '%' 320 case 37: 321 ret = changeg_dstQuad(2, 0, 1); 322 break; 323 // SHIFT + 6 '^' 324 case 94: 325 ret = changeg_dstQuad(2, 1, 1); 326 break; 327 // SHIFT + 7 '&' 328 case 38: 329 ret = changeg_dstQuad(3, 0, 1); 330 break; 331 // SHIFT + 8 '*' 332 case 42: 333 ret = changeg_dstQuad(3, 1, 1); 334 break; 335 336 default: 337 break; 338 } 339 340 // Error 341 if(ret != 0) 342 { 343 goto end; 344 } 345 cvGetPerspectiveTransform(srcQuad, g_dstQuad, warp_matrix); 346 cvWarpPerspective( src, dst, warp_matrix, 347 CV_INTER_LINEAR + CV_WARP_FILL_OUTLIERS, cvScalarAll(0)); 348 349 cvShowImage("Source_Image", src); 350 cvShowImage("Perspective_Warp", dst); 351 } 352 353 end: 354 cvReleaseImage(&src); 355 cvReleaseImage(&dst); 356 cvReleaseMat(&warp_matrix); 357 cvDestroyAllWindows(); 358 359 return 0; 360 }
程序运行截图:
首先是透视变换:
图像放大缩小(b部分):
图像旋转(c部分):