CPP 替代 PIL 图片处理(缩略图生成)
python中使用PIL(Pyhton Image Library)进行图片处理,好处就是编写简单方便,但是不能很好利用机器多核的特点,于是在项目中决定使用cpp来实现图片处理。
项目中的图片处理主要是生成缩略图。网上收集了一些cpp图片处理库,并进行了对比:
在项目中需要对jpg、png、gif格式的图片进行处理,可行的cpp库有Img、FreeImage、GD,而安装使用后进行效率对比,决定使用FreeImage。
折腾了一个星期,把可用程序完成,并和PIL进行对比(这里是使用python commands库运行上传图片demo,上传时使用threading多进行并发):
注意:
1、测试时上传多张大图片时内存很快用完,所以要测试要排除内存用完的影响,内存耗尽会影响测试。(上传数量控制一下)
2、linux机器上传时,记得ulimit -n 调大文件句柄打开数,以免影响测试。
测试结果还是挺理想,准备正式推广。
FreeImage cpp 缩略图生成代码:
1 #include "stdio.h" 2 #include "stdlib.h" 3 #include "unistd.h" 4 #include <sys/time.h> 5 #include <sys/stat.h> 6 #include "FreeImage.h" 7 8 #ifdef _DEBUG 9 #pragma comment(lib, "FreeImaged.lib") 10 #else 11 #pragma comment(lib, "FreeImage.lib") 12 #endif 13 14 #define THUM_TYPE_SMALL 1 15 #define THUM_TYPE_MID 2 16 17 const float IMAGE_NEED_CROP_RATIO = 3.0; 18 const float IMAGE_CROP_RATIO = 1.76; 19 const int MID_IMAGE_LIMIT_SIZE = 3*1024*1024; 20 21 struct ImageSize{ 22 int width; 23 int height; 24 }; 25 26 inline void print_size(ImageSize img_size, const char* str) 27 { 28 printf("%s : %d %d\n", str, img_size.width, img_size.height); 29 } 30 31 inline bool is_long_image(ImageSize img_size) 32 { 33 34 if(float(img_size.height) / img_size.width > IMAGE_NEED_CROP_RATIO) 35 return true; 36 else 37 return false; 38 } 39 40 inline bool is_panorama_image(ImageSize img_size) 41 { 42 43 if(float(img_size.width) / img_size.height > IMAGE_NEED_CROP_RATIO) 44 return true; 45 else 46 return false; 47 } 48 49 inline bool is_need_crop(ImageSize img_size) 50 { 51 if(is_long_image(img_size)) 52 return true; 53 54 if(is_panorama_image(img_size)) 55 return true; 56 57 return false; 58 } 59 60 inline bool is_need_resize(ImageSize img_size, ImageSize des_img_size) 61 { 62 int width = img_size.width; 63 int height = img_size.height; 64 int des_width = des_img_size.width; 65 int des_height = des_img_size.height; 66 if((width < des_width) && (height < des_height)) 67 return false; 68 else 69 return true; 70 } 71 72 inline ImageSize get_crop_size(ImageSize img_size) 73 { 74 if(is_long_image(img_size)) 75 img_size.height = img_size.width * IMAGE_CROP_RATIO; 76 else if(is_panorama_image(img_size)) 77 img_size.width = img_size.height * IMAGE_CROP_RATIO; 78 79 return img_size; 80 } 81 82 inline ImageSize get_resize_size(ImageSize img_size, ImageSize des_img_size) 83 { 84 int width = img_size.width; 85 int height = img_size.height; 86 int des_width = des_img_size.width; 87 int des_height = des_img_size.height; 88 89 float ratio = 1.0; 90 if(height > width) 91 ratio = float(des_height) / height; 92 else 93 ratio = float(des_width) / width; 94 95 img_size.width = width * ratio; 96 img_size.height = height * ratio; 97 98 return img_size; 99 } 100 101 102 inline FIBITMAP * get_crop_picture(FIBITMAP *sourcePic) 103 { 104 105 int src_width, src_height; 106 107 //获取图片大小 108 src_width = FreeImage_GetWidth(sourcePic); 109 src_height = FreeImage_GetHeight(sourcePic); 110 111 struct ImageSize src_size = {src_width, src_height}; 112 113 if(!is_need_crop(src_size)) 114 return sourcePic; 115 116 struct ImageSize crop_size = get_crop_size(src_size); 117 //print_size(crop_size, "crop_size"); 118 119 FIBITMAP * cropPic = FreeImage_Copy(sourcePic, 0, 0, crop_size.width, crop_size.height); 120 121 FreeImage_Unload(sourcePic); 122 sourcePic = NULL; 123 124 return cropPic; 125 126 } 127 128 inline FIBITMAP * get_resize_picture(FIBITMAP *sourcePic, ImageSize des_size) 129 { 130 131 int src_width, src_height; 132 133 //获取图片大小 134 src_width = FreeImage_GetWidth(sourcePic); 135 src_height = FreeImage_GetHeight(sourcePic); 136 137 struct ImageSize src_size = {src_width, src_height}; 138 139 if(!is_need_resize(src_size, des_size)) 140 return sourcePic; 141 142 struct ImageSize resize_size = get_resize_size(src_size, des_size); 143 //print_size(resize_size, "resize_size"); 144 145 FIBITMAP * resizePic = FreeImage_Rescale(sourcePic, resize_size.width, resize_size.height, FILTER_BOX); 146 147 FreeImage_Unload(sourcePic); 148 sourcePic = NULL; 149 150 return resizePic; 151 152 } 153 154 int get_file_size1(const char * src_pic_path) 155 { 156 struct stat stat_buf; 157 stat(src_pic_path, &stat_buf); 158 int file_size = stat_buf.st_size; 159 160 return file_size; 161 } 162 163 int get_file_size2(const char * src_pic_path) 164 { 165 FILE * fp = fopen(src_pic_path, "rb"); 166 if(fp == NULL) 167 return 0; 168 169 fseek (fp, 0, SEEK_END); 170 int src_file_size = ftell(fp); 171 fclose(fp); 172 173 return src_file_size; 174 } 175 176 int gen_symbolic_link(const char *src_pic_path, const char *des_pic_path) 177 { 178 char cmd[200]; 179 sprintf(cmd, "ln -s %s %s", src_pic_path, des_pic_path); 180 int sh_ret = system(cmd); 181 182 return sh_ret; 183 } 184 185 int gen_small_thumbnail(const char *src_pic_path, const char *des_pic_path, int des_width, int des_height) 186 { 187 //printf("gen_small_thumbnail:%s --> %s w:%d, h:%d\n", src_pic_path, des_pic_path, des_width, des_height); 188 189 struct ImageSize des_size = {des_width, des_height}; 190 191 FIBITMAP *sourcePic = NULL, *finalPic = NULL; 192 FreeImage_Initialise(); 193 FREE_IMAGE_FORMAT pic_type = FIF_UNKNOWN; 194 195 //获取图片格式 196 pic_type = FreeImage_GetFileType (src_pic_path, 0); 197 //printf("xxxxxxx:%d %d \n", pic_type, FIT_BITMAP); 198 //是否支持该格式类型 199 if(!FreeImage_FIFSupportsReading(pic_type)) 200 return 480; 201 202 //载入图片 203 sourcePic = FreeImage_Load(pic_type, src_pic_path, 0); 204 205 //剪切图片 206 sourcePic = get_crop_picture(sourcePic); 207 if(!sourcePic) 208 return 481; 209 210 //缩略图片 211 sourcePic = get_resize_picture(sourcePic, des_size); 212 if(!sourcePic) 213 return 482; 214 215 int returnValue = 0; 216 217 //位图转换 218 finalPic = FreeImage_ConvertTo24Bits(sourcePic); 219 220 //保存图片 221 if(!FreeImage_Save(FIF_JPEG, finalPic, des_pic_path, JPEG_DEFAULT)) 222 returnValue = 499; 223 224 //释放资源 225 FreeImage_Unload(sourcePic); 226 FreeImage_Unload(finalPic); 227 sourcePic = NULL; 228 finalPic = NULL; 229 FreeImage_DeInitialise(); 230 231 return returnValue; 232 } 233 234 int gen_mid_thumbnail(const char *src_pic_path, const char *des_pic_path, int des_width, int des_height) 235 { 236 //printf("gen_mid_thumbnail:%s --> %s w:%d, h:%d\n", src_pic_path, des_pic_path, des_width, des_height); 237 struct ImageSize des_size = {des_width, des_height}; 238 239 FIBITMAP *sourcePic = NULL, *finalPic = NULL; 240 FreeImage_Initialise(); 241 FREE_IMAGE_FORMAT pic_type = FIF_UNKNOWN; 242 243 //获取图片格式 244 pic_type = FreeImage_GetFileType (src_pic_path, 0); 245 //printf("xxxxxxx:%d %d \n", pic_type, FIT_BITMAP); 246 //是否支持该格式类型 247 if(!FreeImage_FIFSupportsReading(pic_type)) 248 return 480; 249 250 //载入图片 251 sourcePic = FreeImage_Load(pic_type, src_pic_path, 0); 252 253 struct ImageSize src_size = {FreeImage_GetWidth(sourcePic), FreeImage_GetHeight(sourcePic)}; 254 255 //缩略图片 256 int returnValue = 0; 257 258 sourcePic = get_resize_picture(sourcePic, des_size); 259 if(!sourcePic) 260 return 482; 261 262 //int src_file_size = get_file_size2(src_pic_path); 263 int file_size = get_file_size1(src_pic_path); 264 265 if(is_need_resize(src_size, des_size) || file_size > MID_IMAGE_LIMIT_SIZE){ 266 //位图转换 267 finalPic = FreeImage_ConvertTo24Bits(sourcePic); 268 269 //保存图片 270 if(!FreeImage_Save(FIF_JPEG, finalPic, des_pic_path, JPEG_DEFAULT)) 271 returnValue = 499; 272 }else{ 273 int sh_ret = gen_symbolic_link(src_pic_path, des_pic_path); 274 //if(src_file_size > MID_IMAGE_LIMIT_SIZE) 275 //printf("src_file_size:%d file_size:%d sh:%d\n", src_file_size, file_size, sh_ret); 276 } 277 278 //释放资源 279 FreeImage_Unload(sourcePic); 280 FreeImage_Unload(finalPic); 281 sourcePic = NULL; 282 finalPic = NULL; 283 FreeImage_DeInitialise(); 284 285 return returnValue; 286 } 287 288 /* 289 290 usage: 291 292 ./cpp_gen_thum src_pic_path des_pic_path height width type 293 294 源文件地址 生成文件地址 目标高 目标宽 类型 295 296 */ 297 298 int main(int argc, char* argv[]) 299 { 300 301 struct timeval t1,t2; 302 double timeuse; 303 gettimeofday(&t1,NULL); 304 305 int height = atoi(argv[3]); 306 int width = atoi(argv[4]); 307 int thum_type = atoi(argv[5]); 308 int result = 1; 309 310 if(thum_type == THUM_TYPE_SMALL){ 311 result = gen_small_thumbnail(argv[1], argv[2], height, width); 312 }else if(thum_type == THUM_TYPE_MID){ 313 result = gen_mid_thumbnail(argv[1], argv[2], height, width); 314 } 315 316 printf("%d", result); 317 318 // int small_res = gen_small_thumbnail(argv[1], argv[2], 120, 120); 319 // int mid_res = gen_mid_thumbnail(argv[1], argv[3], 640, 640); 320 // 321 // gettimeofday(&t2,NULL); 322 // timeuse = t2.tv_sec - t1.tv_sec + (t2.tv_usec - t1.tv_usec)/1000000.0; 323 // printf("Use Time:%f small_res:%d filename:%s\n\t\t mid_res:%d filename:%s\n", 324 // timeuse, small_res, argv[2], mid_res, argv[3]); 325 return 0; 326 }