diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..dd1d60c Binary files /dev/null and b/.DS_Store differ diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..e4522c2 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,20 @@ +{ + "configurations": [ + { + "name": "Mac", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [], + "macFrameworkPath": [ + "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks" + ], + "compilerPath": "/usr/bin/clang", + "cStandard": "c17", + "cppStandard": "c++17", + "intelliSenseMode": "macos-clang-arm64", + "configurationProvider": "ms-vscode.makefile-tools" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/README.md b/README.md index ecff122..14ca8a9 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,4 @@ # CISC372_hw4 -Do not commit to this repository. Please fork a copy into your own repository, then work from your personal forked copy. \ No newline at end of file + USE "make image" to compile the original program + USE "make image_pt" to compile the pthread version + USE "make image_omp" to compile the OpenMP version \ No newline at end of file diff --git a/image b/image new file mode 100755 index 0000000..2f3329a Binary files /dev/null and b/image differ diff --git a/image.c b/image.c index 2573a5b..945c77c 100644 --- a/image.c +++ b/image.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -119,4 +118,4 @@ int main(int argc,char** argv){ t2=time(NULL); printf("Took %ld seconds\n",t2-t1); return 0; -} \ No newline at end of file +} diff --git a/image.dSYM/Contents/Info.plist b/image.dSYM/Contents/Info.plist new file mode 100644 index 0000000..869ab66 --- /dev/null +++ b/image.dSYM/Contents/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.xcode.dsym.image + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + dSYM + CFBundleSignature + ???? + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/image.dSYM/Contents/Resources/DWARF/image b/image.dSYM/Contents/Resources/DWARF/image new file mode 100644 index 0000000..0d3aa59 Binary files /dev/null and b/image.dSYM/Contents/Resources/DWARF/image differ diff --git a/image.h b/image.h index 83b4b81..5f46c71 100644 --- a/image.h +++ b/image.h @@ -17,6 +17,7 @@ typedef double Matrix[3][3]; uint8_t getPixelValue(Image* srcImage,int x,int y,int bit,Matrix algorithm); void convolute(Image* srcImage,Image* destImage,Matrix algorithm); +void* p_convolute(void *args); int Usage(); enum KernelTypes GetKernelType(char* type); diff --git a/image_omp.c b/image_omp.c new file mode 100644 index 0000000..18adf4c --- /dev/null +++ b/image_omp.c @@ -0,0 +1,128 @@ +#include +#include +#include +#include "image.h" +#include + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb_image_write.h" + +//An array of kernel matrices to be used for image convolution. +//The indexes of these match the enumeration from the header file. ie. algorithms[BLUR] returns the kernel corresponding to a box blur. +Matrix algorithms[]={ + {{0,-1,0},{-1,4,-1},{0,-1,0}}, + {{0,-1,0},{-1,5,-1},{0,-1,0}}, + {{1/9.0,1/9.0,1/9.0},{1/9.0,1/9.0,1/9.0},{1/9.0,1/9.0,1/9.0}}, + {{1.0/16,1.0/8,1.0/16},{1.0/8,1.0/4,1.0/8},{1.0/16,1.0/8,1.0/16}}, + {{-2,-1,0},{-1,1,1},{0,1,2}}, + {{0,0,0},{0,1,0},{0,0,0}} +}; + + +//getPixelValue - Computes the value of a specific pixel on a specific channel using the selected convolution kernel +//Paramters: srcImage: An Image struct populated with the image being convoluted +// x: The x coordinate of the pixel +// y: The y coordinate of the pixel +// bit: The color channel being manipulated +// algorithm: The 3x3 kernel matrix to use for the convolution +//Returns: The new value for this x,y pixel and bit channel +uint8_t getPixelValue(Image* srcImage,int x,int y,int bit,Matrix algorithm){ + int px,mx,py,my,i,span; + span=srcImage->width*srcImage->bpp; + // for the edge pixes, just reuse the edge pixel + px=x+1; py=y+1; mx=x-1; my=y-1; + if (mx<0) mx=0; + if (my<0) my=0; + if (px>=srcImage->width) px=srcImage->width-1; + if (py>=srcImage->height) py=srcImage->height-1; + uint8_t result= + algorithm[0][0]*srcImage->data[Index(mx,my,srcImage->width,bit,srcImage->bpp)]+ + algorithm[0][1]*srcImage->data[Index(x,my,srcImage->width,bit,srcImage->bpp)]+ + algorithm[0][2]*srcImage->data[Index(px,my,srcImage->width,bit,srcImage->bpp)]+ + algorithm[1][0]*srcImage->data[Index(mx,y,srcImage->width,bit,srcImage->bpp)]+ + algorithm[1][1]*srcImage->data[Index(x,y,srcImage->width,bit,srcImage->bpp)]+ + algorithm[1][2]*srcImage->data[Index(px,y,srcImage->width,bit,srcImage->bpp)]+ + algorithm[2][0]*srcImage->data[Index(mx,py,srcImage->width,bit,srcImage->bpp)]+ + algorithm[2][1]*srcImage->data[Index(x,py,srcImage->width,bit,srcImage->bpp)]+ + algorithm[2][2]*srcImage->data[Index(px,py,srcImage->width,bit,srcImage->bpp)]; + return result; +} + +//convolute: Applies a kernel matrix to an image +//Parameters: srcImage: The image being convoluted +// destImage: A pointer to a pre-allocated (including space for the pixel array) structure to receive the convoluted image. It should be the same size as srcImage +// algorithm: The kernel matrix to use for the convolution +//Returns: Nothing +void convolute(Image* srcImage,Image* destImage,Matrix algorithm){ + int row,pix,bit,span; + span=srcImage->bpp*srcImage->bpp; +# pragma omp for + for (row=0;rowheight;row++){ + for (pix=0;pixwidth;pix++){ + for (bit=0;bitbpp;bit++){ + destImage->data[Index(pix,row,srcImage->width,bit,srcImage->bpp)]=getPixelValue(srcImage,pix,row,bit,algorithm); + } + } + } +} + +//Usage: Prints usage information for the program +//Returns: -1 +int Usage(){ + printf("Usage: image \n\twhere type is one of (edge,sharpen,blur,gauss,emboss,identity)\n"); + return -1; +} + +//GetKernelType: Converts the string name of a convolution into a value from the KernelTypes enumeration +//Parameters: type: A string representation of the type +//Returns: an appropriate entry from the KernelTypes enumeration, defaults to IDENTITY, which does nothing but copy the image. +enum KernelTypes GetKernelType(char* type){ + if (!strcmp(type,"edge")) return EDGE; + else if (!strcmp(type,"sharpen")) return SHARPEN; + else if (!strcmp(type,"blur")) return BLUR; + else if (!strcmp(type,"gauss")) return GAUSE_BLUR; + else if (!strcmp(type,"emboss")) return EMBOSS; + else return IDENTITY; +} + +//main: +//argv is expected to take 2 arguments. First is the source file name (can be jpg, png, bmp, tga). Second is the lower case name of the algorithm. +int main(int argc,char** argv){ + long t1,t2; + t1=time(NULL); + + stbi_set_flip_vertically_on_load(0); + if (argc!=3) return Usage(); + char* fileName=argv[1]; + if (!strcmp(argv[1],"pic4.jpg")&&!strcmp(argv[2],"gauss")){ + printf("You have applied a gaussian filter to Gauss which has caused a tear in the time-space continum.\n"); + } + enum KernelTypes type=GetKernelType(argv[2]); + + Image srcImage,destImage,bwImage; + srcImage.data=stbi_load(fileName,&srcImage.width,&srcImage.height,&srcImage.bpp,0); + if (!srcImage.data){ + printf("Error loading file %s.\n",fileName); + return -1; + } + + destImage.bpp=srcImage.bpp; + destImage.height=srcImage.height; + destImage.width=srcImage.width; + destImage.data=malloc(sizeof(uint8_t)*destImage.width*destImage.bpp*destImage.height); + +# pragma omp parallel + convolute(&srcImage,&destImage,algorithms[type]); + t2 = time(NULL); + printf("Processing done, saving image\n"); + + stbi_write_png("output.png",destImage.width,destImage.height,destImage.bpp,destImage.data,destImage.bpp*destImage.width); + stbi_image_free(srcImage.data); + + free(destImage.data); + printf("Took %ld seconds\n",t2-t1); + return 0; +} diff --git a/image_pt.c b/image_pt.c new file mode 100644 index 0000000..390848e --- /dev/null +++ b/image_pt.c @@ -0,0 +1,184 @@ +#include +#include +#include +#include +#include "image.h" +#include + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb_image_write.h" + +enum KernelTypes type; + +int thread_count; + +//Struct to store all args to pass to p_convolute +typedef struct { + Image* srcImage; + Image* destImage; +} convArgs; + +convArgs c_args; +//An array of kernel matrices to be used for image convolution. +//The indexes of these match the enumeration from the header file. ie. algorithms[BLUR] returns the kernel corresponding to a box blur. +Matrix algorithms[]={ + {{0,-1,0},{-1,4,-1},{0,-1,0}}, + {{0,-1,0},{-1,5,-1},{0,-1,0}}, + {{1/9.0,1/9.0,1/9.0},{1/9.0,1/9.0,1/9.0},{1/9.0,1/9.0,1/9.0}}, + {{1.0/16,1.0/8,1.0/16},{1.0/8,1.0/4,1.0/8},{1.0/16,1.0/8,1.0/16}}, + {{-2,-1,0},{-1,1,1},{0,1,2}}, + {{0,0,0},{0,1,0},{0,0,0}} +}; + + +//getPixelValue - Computes the value of a specific pixel on a specific channel using the selected convolution kernel +//Paramters: srcImage: An Image struct populated with the image being convoluted +// x: The x coordinate of the pixel +// y: The y coordinate of the pixel +// bit: The color channel being manipulated +// algorithm: The 3x3 kernel matrix to use for the convolution +//Returns: The new value for this x,y pixel and bit channel +uint8_t getPixelValue(Image* srcImage,int x,int y,int bit,Matrix algorithm){ + int px,mx,py,my,i,span; + span=srcImage->width*srcImage->bpp; + // for the edge pixes, just reuse the edge pixel + px=x+1; py=y+1; mx=x-1; my=y-1; + if (mx<0) mx=0; + if (my<0) my=0; + if (px>=srcImage->width) px=srcImage->width-1; + if (py>=srcImage->height) py=srcImage->height-1; + uint8_t result= + algorithm[0][0]*srcImage->data[Index(mx,my,srcImage->width,bit,srcImage->bpp)]+ + algorithm[0][1]*srcImage->data[Index(x,my,srcImage->width,bit,srcImage->bpp)]+ + algorithm[0][2]*srcImage->data[Index(px,my,srcImage->width,bit,srcImage->bpp)]+ + algorithm[1][0]*srcImage->data[Index(mx,y,srcImage->width,bit,srcImage->bpp)]+ + algorithm[1][1]*srcImage->data[Index(x,y,srcImage->width,bit,srcImage->bpp)]+ + algorithm[1][2]*srcImage->data[Index(px,y,srcImage->width,bit,srcImage->bpp)]+ + algorithm[2][0]*srcImage->data[Index(mx,py,srcImage->width,bit,srcImage->bpp)]+ + algorithm[2][1]*srcImage->data[Index(x,py,srcImage->width,bit,srcImage->bpp)]+ + algorithm[2][2]*srcImage->data[Index(px,py,srcImage->width,bit,srcImage->bpp)]; + return result; +} + +//convolute: Applies a kernel matrix to an image +//Parameters: srcImage: The image being convoluted +// destImage: A pointer to a pre-allocated (including space for the pixel array) structure to receive the convoluted image. It should be the same size as srcImage +// algorithm: The kernel matrix to use for the convolution +//Returns: Nothing +void convolute(Image* srcImage,Image* destImage,Matrix algorithm){ + int row,pix,bit,span; + span=srcImage->bpp*srcImage->bpp; + for (row=0;rowheight;row++){ + for (pix=0;pixwidth;pix++){ + for (bit=0;bitbpp;bit++){ + destImage->data[Index(pix,row,srcImage->width,bit,srcImage->bpp)]=getPixelValue(srcImage,pix,row,bit,algorithm); + } + } + } +} + + +//p_convolute: Applies a kernel matrix to an image with p_threads +//Parameters: srcImage: The image being convoluted +// destImage: A pointer to a pre-allocated (including space for the pixel array) structure to receive the convoluted image. It should be the same size as srcImage +// algorithm: The kernel matrix to use for the convolution +//Returns: Nothing +void* p_convolute(void * rank){ + int my_rows, my_start, bit, span, pix; + long my_rank = (long) rank; + int total_rows = c_args.destImage->height; + span=c_args.srcImage->bpp * c_args.srcImage->bpp; + + //calculate how many rows each thread will process + my_rows = (int) total_rows / thread_count; + my_start = (int) my_rank * my_rows; + if((int)my_rank==thread_count-1){ + my_rows+=total_rows % thread_count; + } + + //process the given rows for the thread + for(int row = my_start; rowwidth; pix++){ + for(bit=0; bitbpp; bit++){ + c_args.destImage->data[Index(pix,row,c_args.srcImage->width,bit,c_args.srcImage->bpp)]=getPixelValue(c_args.srcImage,pix,row,bit,algorithms[type]); + } + } + } + + return NULL; +} + +//Usage: Prints usage information for the program +//Returns: -1 +int Usage(){ + printf("Usage: image \n\twhere type is one of (edge,sharpen,blur,gauss,emboss,identity)\n"); + return -1; +} + +//GetKernelType: Converts the string name of a convolution into a value from the KernelTypes enumeration +//Parameters: type: A string representation of the type +//Returns: an appropriate entry from the KernelTypes enumeration, defaults to IDENTITY, which does nothing but copy the image. +enum KernelTypes GetKernelType(char* type){ + if (!strcmp(type,"edge")) return EDGE; + else if (!strcmp(type,"sharpen")) return SHARPEN; + else if (!strcmp(type,"blur")) return BLUR; + else if (!strcmp(type,"gauss")) return GAUSE_BLUR; + else if (!strcmp(type,"emboss")) return EMBOSS; + else return IDENTITY; +} + +//main: +//argv is expected to take 2 arguments. First is the source file name (can be jpg, png, bmp, tga). Second is the lower case name of the algorithm. +int main(int argc,char** argv){ + long t1,t2; + t1=time(NULL); + + stbi_set_flip_vertically_on_load(0); + if (argc!=4) return Usage(); + char* fileName=argv[1]; + if (!strcmp(argv[1],"pic4.jpg")&&!strcmp(argv[2],"gauss")){ + printf("You have applied a gaussian filter to Gauss which has caused a tear in the time-space continum.\n"); + } + enum KernelTypes type=GetKernelType(argv[2]); + + Image srcImage,destImage,bwImage; + srcImage.data=stbi_load(fileName,&srcImage.width,&srcImage.height,&srcImage.bpp,0); + if (!srcImage.data){ + printf("Error loading file %s.\n",fileName); + return -1; + } + destImage.bpp=srcImage.bpp; + destImage.height=srcImage.height; + destImage.width=srcImage.width; + destImage.data=malloc(sizeof(uint8_t)*destImage.width*destImage.bpp*destImage.height); + + //create threads and call function HERE + long thread; + pthread_t* thread_handles; + thread_count = strtol(argv[3], NULL, 10); + thread_handles = (pthread_t*)malloc(thread_count * sizeof(pthread_t)); + + //loop to create p_threads + for(thread=0; thread