Index: test-suite/trunk/MicroBenchmarks/ImageProcessing/CMakeLists.txt =================================================================== --- test-suite/trunk/MicroBenchmarks/ImageProcessing/CMakeLists.txt +++ test-suite/trunk/MicroBenchmarks/ImageProcessing/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(Dither) Index: test-suite/trunk/MicroBenchmarks/ImageProcessing/Dither/CMakeLists.txt =================================================================== --- test-suite/trunk/MicroBenchmarks/ImageProcessing/Dither/CMakeLists.txt +++ test-suite/trunk/MicroBenchmarks/ImageProcessing/Dither/CMakeLists.txt @@ -0,0 +1,13 @@ +set(IMAGEPROC_UTILS MicroBenchmarks/ImageProcessing/utils) +list(APPEND CPPFLAGS -I ${CMAKE_SOURCE_DIR}/${IMAGEPROC_UTILS} -std=c++11) + +llvm_test_verify("${CMAKE_SOURCE_DIR}/HashProgramOutput.sh ${CMAKE_CURRENT_BINARY_DIR}/orderedOutput.txt") +llvm_test_verify("${FPCMP} ${CMAKE_CURRENT_BINARY_DIR}/orderedOutput.txt ${CMAKE_CURRENT_SOURCE_DIR}/orderedDither.reference_output") + +llvm_test_verify("${CMAKE_SOURCE_DIR}/HashProgramOutput.sh ${CMAKE_CURRENT_BINARY_DIR}/floydOutput.txt") +llvm_test_verify("${FPCMP} ${CMAKE_CURRENT_BINARY_DIR}/floydOutput.txt ${CMAKE_CURRENT_SOURCE_DIR}/floydDither.reference_output") + +llvm_test_run(WORKDIR ${CMAKE_CURRENT_BINARY_DIR}) +llvm_test_executable(Dither main.cpp orderedDitherKernel.c floydDitherKernel.c ../utils/ImageHelper.cpp ../utils/glibc_compat_rand.c) + +target_link_libraries(Dither benchmark) Index: test-suite/trunk/MicroBenchmarks/ImageProcessing/Dither/dither.h =================================================================== --- test-suite/trunk/MicroBenchmarks/ImageProcessing/Dither/dither.h +++ test-suite/trunk/MicroBenchmarks/ImageProcessing/Dither/dither.h @@ -0,0 +1,15 @@ +/** + Pankaj Kukreja + github.com/proton0001 + Indian Institute of Technology Hyderabad +*/ +#ifndef _DITHER_H_ +#define _DITHER_H_ + +#define MaxGray 255 +#define MXGRAY 256 + +#define HEIGHT 512 +#define WIDTH 512 + +#endif /* _DITHER_H_ */ Index: test-suite/trunk/MicroBenchmarks/ImageProcessing/Dither/floydDither.reference_output =================================================================== --- test-suite/trunk/MicroBenchmarks/ImageProcessing/Dither/floydDither.reference_output +++ test-suite/trunk/MicroBenchmarks/ImageProcessing/Dither/floydDither.reference_output @@ -0,0 +1 @@ +23473c2d34c91e33eaa3d5008ce3640e Index: test-suite/trunk/MicroBenchmarks/ImageProcessing/Dither/floydDitherKernel.c =================================================================== --- test-suite/trunk/MicroBenchmarks/ImageProcessing/Dither/floydDitherKernel.c +++ test-suite/trunk/MicroBenchmarks/ImageProcessing/Dither/floydDitherKernel.c @@ -0,0 +1,69 @@ +/** + Source: https://imagej.net/Dithering + Modified by Pankaj Kukreja (github.com/proton0001) + Indian Institute of Technology Hyderabad +*/ +#include "dither.h" +void floydDitherKernel(int height, int width, int inputImage[HEIGHT][WIDTH], + int outputImage[height][width]) { + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + outputImage[i][j] = inputImage[i][j]; + } + } + + int err; + int a, b, c, d; + + for (int i = 1; i < height - 1; i++) { + for (int j = 1; j < width - 1; j++) { + if (outputImage[i][j] > 127) { + err = outputImage[i][j] - 255; + outputImage[i][j] = 255; + } else { + err = outputImage[i][j] - 0; + outputImage[i][j] = 0; + } + a = (err * 7) / 16; + b = (err * 1) / 16; + c = (err * 5) / 16; + d = (err * 3) / 16; + + int temp1 = (outputImage[i][j + 1] + a); + if (temp1 > 255) { + outputImage[i][j + 1] = 255; + } else if (temp1 < 0) { + outputImage[i][j + 1] = 0; + } else { + outputImage[i][j + 1] = temp1; + } + + int temp2 = (outputImage[i + 1][j + 1] + b); + if (temp2 > 255) { + outputImage[i + 1][j + 1] = 255; + } else if (temp2 < 0) { + outputImage[i + 1][j + 1] = 0; + } else { + outputImage[i + 1][j + 1] = temp2; + } + + int temp3 = outputImage[i + 1][j + 0] + c; + if (temp3 > 255) { + outputImage[i + 1][j + 0] = 255; + } else if (temp3 < 0) { + outputImage[i + 1][j + 0] = 0; + } else { + outputImage[i + 1][j + 0] = temp3; + } + + int temp4 = outputImage[i + 1][j - 1] + d; + if (temp4 > 255) { + outputImage[i + 1][j - 1] = 255; + } else if (temp4 < 0) { + outputImage[i + 1][j - 1] = 0; + } else { + outputImage[i + 1][j - 1] = temp4; + } + } + } +} Index: test-suite/trunk/MicroBenchmarks/ImageProcessing/Dither/main.cpp =================================================================== --- test-suite/trunk/MicroBenchmarks/ImageProcessing/Dither/main.cpp +++ test-suite/trunk/MicroBenchmarks/ImageProcessing/Dither/main.cpp @@ -0,0 +1,156 @@ +/** + Pankaj Kukreja + github.com/proton0001 + Indian Institute of Technology Hyderabad +*/ +#include "ImageHelper.h" +#include "dither.h" +#include +#include // std::cerr + +#define BENCHMARK_LIB +#ifdef BENCHMARK_LIB +#include "benchmark/benchmark.h" +#endif + +int *inputImage; +extern "C" { +void orderedDitherKernel(int height, int width, int *inpImage, int *outImage, + int *temp, int n, int m); +void floydDitherKernel(int height, int width, int *inpImage, int *outImage); +} +int main(int argc, char **argv) { +#ifdef BENCHMARK_LIB + ::benchmark::Initialize(&argc, argv); +#endif + + const char *orderedOutputFilename = (char *)"./orderedOutput.txt"; + const char *floydOutputFilename = (char *)"./floydOutput.txt"; + inputImage = (int *)malloc(sizeof(int) * HEIGHT * WIDTH); + if (inputImage == NULL) { + std::cerr << "Insufficient memory\n"; + exit(1); + } + initializeRandomImage(inputImage, HEIGHT, WIDTH); + +#ifdef BENCHMARK_LIB + ::benchmark::RunSpecifiedBenchmarks(); +#endif + int *outputImage = (int *)malloc(sizeof(int) * HEIGHT * WIDTH); + int *temp = (int *)malloc(sizeof(int) * HEIGHT * WIDTH); + if (outputImage == NULL || temp == NULL) { + std::cerr << "Insufficient memory\n"; + exit(1); + } + orderedDitherKernel(HEIGHT, WIDTH, inputImage, outputImage, temp, 16, 4); + saveImage(outputImage, orderedOutputFilename, HEIGHT, WIDTH); + floydDitherKernel(HEIGHT, WIDTH, inputImage, outputImage); + + for (int i = 0; i < HEIGHT; i++) { + outputImage[(i)*WIDTH + 0] = 0; + outputImage[(i)*WIDTH + WIDTH - 1] = 0; + } + + for (int j = 0; j < WIDTH; j++) { + outputImage[(0) * WIDTH + j] = 0; + outputImage[(HEIGHT - 1) * WIDTH + j] = 0; + } + + saveImage(outputImage, floydOutputFilename, HEIGHT, WIDTH); + free(temp); + free(outputImage); + free(inputImage); + return EXIT_SUCCESS; +} + +#ifdef BENCHMARK_LIB +void BENCHMARK_ORDERED_DITHER(benchmark::State &state) { + int height = state.range(0); + int width = state.range(0); + int m = state.range(1); + int n = pow(m, 2); + int *outputImage = (int *)malloc(sizeof(int) * height * width); + int *temp = (int *)malloc(sizeof(int) * height * width); + + if (outputImage == NULL) { + std::cerr << "Insufficient memory\n"; + exit(1); + } + /* This call is to warm up the cache */ + orderedDitherKernel(height, width, inputImage, outputImage, temp, n, m); + + for (auto _ : state) { + orderedDitherKernel(height, width, inputImage, outputImage, temp, n, m); + } + /* Since we are not passing state.range as 20 this if case will always be + * false. This call is to make compiler think that outputImage may be used + * later so that above kernel calls will not optimize out */ + if (state.range(0) == 20) { + saveImage(outputImage, (const char *)"failedCase.txt", height, width); + } + free(temp); + free(outputImage); +} + +#if (HEIGHT < WIDTH) +#define MINIMUM_DIM HEIGHT +#else +#define MINIMUM_DIM WIDTH +#endif + +static void CustomArguments(benchmark::internal::Benchmark *b) { + int limit = MINIMUM_DIM; + int start = 1; + if (limit > 128) { + start = 128; + } + for (int i = start; i <= limit; i <<= 1) { + b->Args({i, 2}); + b->Args({i, 3}); + b->Args({i, 4}); + b->Args({i, 8}); + } +} +BENCHMARK(BENCHMARK_ORDERED_DITHER) + ->Apply(CustomArguments) + ->Unit(benchmark::kMicrosecond); + +void BENCHMARK_FLOYD_DITHER(benchmark::State &state) { + + int height = state.range(0); + int width = state.range(0); + + int *outputImage = (int *)malloc(sizeof(int) * height * width); + + if (outputImage == NULL) { + std::cerr << "Insufficient memory\n"; + exit(1); + } + /* This call is to warm up the cache */ + floydDitherKernel(height, width, inputImage, outputImage); + for (auto _ : state) { + floydDitherKernel(height, width, inputImage, outputImage); + } + /* Since we are not passing state.range as 20 this if case will always be + * false. This call is to make compiler think that outputImage may be used + * later so that above kernel calls will not optimize out */ + if (state.range(0) == 20) { + saveImage(outputImage, (const char *)"failedCase.txt", height, width); + } + + free(outputImage); +} + +#if MINIMUM_DIM > 128 +BENCHMARK(BENCHMARK_FLOYD_DITHER) + ->RangeMultiplier(2) + ->Range(128, MINIMUM_DIM) + ->Unit(benchmark::kMicrosecond); +#else +BENCHMARK(BENCHMARK_FLOYD_DITHER) + ->RangeMultiplier(2) + ->Range(1, MINIMUM_DIM) + ->Unit(benchmark::kMicrosecond); +#endif + +#endif Index: test-suite/trunk/MicroBenchmarks/ImageProcessing/Dither/orderedDither.reference_output =================================================================== --- test-suite/trunk/MicroBenchmarks/ImageProcessing/Dither/orderedDither.reference_output +++ test-suite/trunk/MicroBenchmarks/ImageProcessing/Dither/orderedDither.reference_output @@ -0,0 +1 @@ +7b339ccc04bbaebf30f44f4f3129756f Index: test-suite/trunk/MicroBenchmarks/ImageProcessing/Dither/orderedDitherKernel.c =================================================================== --- test-suite/trunk/MicroBenchmarks/ImageProcessing/Dither/orderedDitherKernel.c +++ test-suite/trunk/MicroBenchmarks/ImageProcessing/Dither/orderedDitherKernel.c @@ -0,0 +1,72 @@ +/** + Source: github -> https://github.com/brianwu02/ImageProcessing.git + Modified by Pankaj Kukreja (github.com/proton0001) + Indian Institute of Technology Hyderabad +*/ +#include "dither.h" +#include // pow + +#define GAMMA 0.5 + +void orderedDitherKernel(int height, int width, int inputImage[HEIGHT][WIDTH], + int outputImage[height][width], + int temp[height][width], int n, int m) { + int scale; + + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + temp[i][j] = + (int)(pow((double)inputImage[i][j] / 255.0, (1.0 / GAMMA)) * 255.0); + } + } + + scale = 256 / n; + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + outputImage[i][j] = (int)(scale * (temp[i][j] / scale)) / scale; + } + } + + if (m == 2) { + int dither[2][2] = {{0, 2}, {3, 1}}; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int i = x % m; + int j = y % m; + outputImage[y][x] = ((outputImage[y][x] > dither[i][j]) ? 255 : 0); + } + } + } else if (m == 3) { + int dither[3][3] = {{6, 8, 4}, {1, 0, 3}, {5, 2, 7}}; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int i = x % m; + int j = y % m; + outputImage[y][x] = ((outputImage[y][x] > dither[i][j]) ? 255 : 0); + } + } + } else if (m == 4) { + int dither[4][4] = { + {0, 8, 2, 10}, {12, 4, 14, 6}, {3, 11, 1, 9}, {15, 7, 13, 5}}; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int i = x % m; + int j = y % m; + outputImage[y][x] = ((outputImage[y][x] > dither[i][j]) ? 255 : 0); + } + } + } else if (m == 8) { + int dither[8][8] = { + {0, 48, 12, 60, 3, 51, 15, 63}, {32, 16, 44, 28, 35, 19, 47, 31}, + {8, 56, 4, 52, 11, 59, 7, 55}, {40, 24, 36, 20, 43, 27, 39, 23}, + {2, 50, 14, 62, 1, 49, 13, 61}, {34, 18, 46, 30, 33, 17, 45, 29}, + {10, 58, 6, 54, 9, 57, 5, 53}, {42, 26, 38, 22, 41, 25, 37, 21}}; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int i = x % m; + int j = y % m; + outputImage[y][x] = ((outputImage[y][x] > dither[i][j]) ? 255 : 0); + } + } + } +}