并行(多线程)操作图片
代码
ImageStuff.h
struct ImgProp
{
int Hpixels;
int Vpixels;
unsigned char HeaderInfo[54];
unsigned long int Hbytes;
};
struct Pixel
{
unsigned char R;
unsigned char G;
unsigned char B;
};
unsigned char** ReadBMP(char* );
void WriteBMP(unsigned char** , char*);
extern struct ImgProp ip;
ImageStuff.c
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include "ImageStuff.h"
unsigned char** ReadBMP(char* filename)
{
int i;
FILE* f = fopen(filename, "rb");
if(f == NULL)
{
printf("\n\n%s NOT FOUND\n\n",filename);
exit(1);
}
unsigned char HeaderInfo[54];
fread(HeaderInfo, sizeof(unsigned char), 54, f); // read the 54-byte header
// extract image height and width from header
int width = *(int*)&HeaderInfo[18];
int height = *(int*)&HeaderInfo[22];
//copy header for re-use
for(i=0; i<54; i++) {
ip.HeaderInfo[i] = HeaderInfo[i];
}
ip.Vpixels = height;
ip.Hpixels = width;
int RowBytes = (width*3 + 3) & (~3);
ip.Hbytes = RowBytes;
printf("\n Input BMP File name: %20s (%u x %u)",filename,ip.Hpixels,ip.Vpixels);
unsigned char tmp;
unsigned char **TheImage = (unsigned char **)malloc(height * sizeof(unsigned char*));
for(i=0; i<height; i++) {
TheImage[i] = (unsigned char *)malloc(RowBytes * sizeof(unsigned char));
}
for(i = 0; i < height; i++) {
fread(TheImage[i], sizeof(unsigned char), RowBytes, f);
}
fclose(f);
return TheImage; // remember to free() it in caller!
}
void WriteBMP(unsigned char** img, char* filename)
{
FILE* f = fopen(filename, "wb");
if(f == NULL)
{
printf("\n\nFILE CREATION ERROR: %s\n\n",filename);
exit(1);
}
unsigned long int x,y;
char temp;
//write header
for(x=0; x<54; x++) {
fputc(ip.HeaderInfo[x],f);
}
//write data
for(x=0; x<ip.Vpixels; x++) {
for(y=0; y<ip.Hbytes; y++)
{
temp=img[x][y];
fputc(temp,f);
}
}
printf("\n Output BMP File name: %20s (%u x %u)",filename,ip.Hpixels,ip.Vpixels);
fclose(f);
}
imflipP.c
#include <pthread.h>
#include <stdint.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include "ImageStuff.h"
#define REPS 129
#define MAXTHREADS 128
long NumThreads; // Total number of threads working in parallel
int ThParam[MAXTHREADS]; // Thread parameters ...
pthread_t ThHandle[MAXTHREADS]; // Thread handles
pthread_attr_t ThAttr; // Pthread attrributes
void (*FlipFunc)(unsigned char** img); // Function pointer to flip the image
void* (*MTFlipFunc)(void *arg); // Function pointer to flip the image, multi-threaded version
unsigned char** TheImage; // This is the main image
struct ImgProp ip;
void FlipImageV(unsigned char** img)
{
struct Pixel pix; //temp swap pixel
int row, col;
//vertical flip
for(col=0; col<ip.Hbytes; col+=3)
{
row = 0;
while(row<ip.Vpixels/2)
{
pix.B = img[row][col];
pix.G = img[row][col+1];
pix.R = img[row][col+2];
img[row][col] = img[ip.Vpixels-(row+1)][col];
img[row][col+1] = img[ip.Vpixels-(row+1)][col+1];
img[row][col+2] = img[ip.Vpixels-(row+1)][col+2];
img[ip.Vpixels-(row+1)][col] = pix.B;
img[ip.Vpixels-(row+1)][col+1] = pix.G;
img[ip.Vpixels-(row+1)][col+2] = pix.R;
row++;
}
}
}
void FlipImageH(unsigned char** img)
{
struct Pixel pix; //temp swap pixel
int row, col;
//horizontal flip
for(row=0; row<ip.Vpixels; row++)
{
col = 0;
while(col<(ip.Hpixels*3)/2)
{
pix.B = img[row][col];
pix.G = img[row][col+1];
pix.R = img[row][col+2];
img[row][col] = img[row][ip.Hpixels*3-(col+3)];
img[row][col+1] = img[row][ip.Hpixels*3-(col+2)];
img[row][col+2] = img[row][ip.Hpixels*3-(col+1)];
img[row][ip.Hpixels*3-(col+3)] = pix.B;
img[row][ip.Hpixels*3-(col+2)] = pix.G;
img[row][ip.Hpixels*3-(col+1)] = pix.R;
col+=3;
}
}
}
void *MTFlipV(void* tid)
{
struct Pixel pix; //temp swap pixel
int row, col;
long ts = *((int *) tid); // My thread ID is stored here
ts *= ip.Hbytes/NumThreads; // start index
long te = ts+ip.Hbytes/NumThreads-1; // end index
for(col=ts; col<=te; col+=3)
{
row=0;
while(row<ip.Vpixels/2)
{
pix.B = TheImage[row][col];
pix.G = TheImage[row][col+1];
pix.R = TheImage[row][col+2];
TheImage[row][col] = TheImage[ip.Vpixels-(row+1)][col];
TheImage[row][col+1] = TheImage[ip.Vpixels-(row+1)][col+1];
TheImage[row][col+2] = TheImage[ip.Vpixels-(row+1)][col+2];
TheImage[ip.Vpixels-(row+1)][col] = pix.B;
TheImage[ip.Vpixels-(row+1)][col+1] = pix.G;
TheImage[ip.Vpixels-(row+1)][col+2] = pix.R;
row++;
}
}
pthread_exit(0);
}
void *MTFlipH(void* tid)
{
struct Pixel pix; //temp swap pixel
int row, col;
long ts = *((int *) tid); // My thread ID is stored here
ts *= ip.Vpixels/NumThreads; // start index
long te = ts+ip.Vpixels/NumThreads-1; // end index
for(row=ts; row<=te; row++)
{
col=0;
while(col<ip.Hpixels*3/2)
{
pix.B = TheImage[row][col];
pix.G = TheImage[row][col+1];
pix.R = TheImage[row][col+2];
TheImage[row][col] = TheImage[row][ip.Hpixels*3-(col+3)];
TheImage[row][col+1] = TheImage[row][ip.Hpixels*3-(col+2)];
TheImage[row][col+2] = TheImage[row][ip.Hpixels*3-(col+1)];
TheImage[row][ip.Hpixels*3-(col+3)] = pix.B;
TheImage[row][ip.Hpixels*3-(col+2)] = pix.G;
TheImage[row][ip.Hpixels*3-(col+1)] = pix.R;
col+=3;
}
}
pthread_exit(NULL);
}
int main(int argc, char** argv)
{
char Flip;
int a,i,ThErr;
struct timeval t;
double StartTime, EndTime;
double TimeElapsed;
switch (argc){
case 3 : NumThreads=1; Flip = 'V'; break;
case 4 : NumThreads=1; Flip = toupper(argv[3][0]); break;
case 5 : NumThreads=atoi(argv[4]); Flip = toupper(argv[3][0]); break;
default: printf("\n\nUsage: imflipP input output [v/h] [thread count]");
printf("\n\nExample: imflipP infilename.bmp outname.bmp h 8\n\n");
return 0;
}
if((Flip != 'V') && (Flip != 'H')) {
printf("Flip option '%c' is invalid. Can only be 'V' or 'H' ... Exiting abruptly ...\n",Flip);
exit(EXIT_FAILURE);
}
if((NumThreads<1) || (NumThreads>MAXTHREADS)){
printf("\nNumber of threads must be between 1 and %u... Exiting abruptly\n",MAXTHREADS);
exit(EXIT_FAILURE);
}
else{
if(NumThreads != 1){
printf("\nExecuting the multi-threaded version with %li threads ...\n",NumThreads);
MTFlipFunc = (Flip=='V') ? MTFlipV:MTFlipH;
}
else{
printf("\nExecuting the serial version ...\n");
FlipFunc = (Flip=='V') ? FlipImageV:FlipImageH;
}
}
TheImage = ReadBMP(argv[1]);
gettimeofday(&t, NULL); // 要包含<sys/time.h>头文件
// 将只提供从操作系统获得的最佳结果(操作系统从硬件的时钟单元获得该值)
// 给一个结构的两个成员变量提供时间:一个是给.tv_sec成员变量设置以秒为单位的时间
// 另一个是给.tv_usec成员变量设置以微秒为单位的事件
StartTime = (double)t.tv_sec*1000000.0 + ((double)t.tv_usec);
if(NumThreads >1){
pthread_attr_init(&ThAttr);
pthread_attr_setdetachstate(&ThAttr, PTHREAD_CREATE_JOINABLE); //告诉操作系统准备启动一系列线程,并且稍后将合并它们...
for(a=0; a<REPS; a++){
for(i=0; i<NumThreads; i++){
ThParam[i] = i;
ThErr = pthread_create(&ThHandle[i], &ThAttr, MTFlipFunc, (void *)&ThParam[i]); // 线程一旦创建就开始执行
// 第一个参数 是每个线程的句柄,它对操作系统非常重要,使操作系统能够跟踪线程
// 第二参数 &TheAttr 对于所有线程都相同,内容是线程属性
// 第三个参数 告诉线程要执行的任务
// 第四个参数 线程参数,定位每个线程
if(ThErr != 0){
printf("\nThread Creation Error %d. Exiting abruptly... \n",ThErr);
exit(EXIT_FAILURE);
}
}
pthread_attr_destroy(&ThAttr);
for(i=0; i<NumThreads; i++){
pthread_join(ThHandle[i], NULL); // 线程终止(合并)
}
}
}else{
for(a=0; a<REPS; a++){
(*FlipFunc)(TheImage);
}
}
gettimeofday(&t, NULL);
EndTime = (double)t.tv_sec*1000000.0 + ((double)t.tv_usec);
TimeElapsed=(EndTime-StartTime)/1000.00;
TimeElapsed/=(double)REPS;
//merge with header and write to file
WriteBMP(TheImage, argv[2]);
// free() the allocated memory for the image
for(i = 0; i < ip.Vpixels; i++) { free(TheImage[i]); }
free(TheImage);
printf("\n\nTotal execution time: %9.4f ms (%s flip)",TimeElapsed, Flip=='V'?"Vertical":"Horizontal");
printf(" (%6.3f ns/pixel)\n", 1000000*TimeElapsed/(double)(ip.Hpixels*ip.Vpixels));
return (EXIT_SUCCESS);
}
编译
gcc imflipP.c ImageStuff.c -o imflipP -lpthread
运行效果
垂直翻转
./imflipP nature_monte.bmp nature_monte_V2.bmp V 2 ## 以2个线程运行
原图
垂直翻转后
水平翻转
./imflipP nature_monte.bmp nature_monte_H2.bmp H 2 ## 以2个线程运行
原图
水平翻转后