image::Bl dither::blue_noise(int width, int height, int threads,
bool use_opencl) {
- bool using_opencl = false;
+ bool using_opencl = false;
#if DITHERING_OPENCL_ENABLED == 1
- if (use_opencl) {
- // try to use OpenCL
- do {
- cl_device_id device;
- cl_context context;
- cl_program program;
- cl_int err;
-
- cl_platform_id platform;
-
- int filter_size = (width + height) / 2;
-
- err = clGetPlatformIDs(1, &platform, nullptr);
- if (err != CL_SUCCESS) {
- std::cerr << "OpenCL: Failed to identify a platform\n";
- break;
- }
-
- err = clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 1, &device,
- nullptr);
- if (err != CL_SUCCESS) {
- std::cerr << "OpenCL: Failed to get a device\n";
- break;
- }
-
- context = clCreateContext(nullptr, 1, &device, nullptr, nullptr,
- &err);
-
- {
- char buf[1024];
- std::ifstream program_file("src/blue_noise.cl");
- if (!program_file.good()) {
- std::cerr << "ERROR: Failed to read \"src/blue_noise.cl\" "
- "(not found?)\n";
- break;
- }
- std::string program_string;
- while (program_file.good()) {
- program_file.read(buf, 1024);
- if (int read_count = program_file.gcount();
- read_count > 0) {
- program_string.append(buf, read_count);
- }
- }
-
- const char *string_ptr = program_string.c_str();
- std::size_t program_size = program_string.size();
- program = clCreateProgramWithSource(context, 1,
- (const char **)&string_ptr,
- &program_size, &err);
- if (err != CL_SUCCESS) {
- std::cerr << "OpenCL: Failed to create the program\n";
- clReleaseContext(context);
- break;
- }
-
- err = clBuildProgram(program, 1, &device, nullptr, nullptr,
- nullptr);
- if (err != CL_SUCCESS) {
- std::cerr << "OpenCL: Failed to build the program\n";
-
- std::size_t log_size;
- clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG,
- 0, nullptr, &log_size);
- std::unique_ptr<char[]> log =
- std::make_unique<char[]>(log_size + 1);
- log[log_size] = 0;
- clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG,
- log_size, log.get(), nullptr);
- std::cerr << log.get() << std::endl;
-
- clReleaseProgram(program);
- clReleaseContext(context);
- break;
- }
- }
-
- std::cout << "OpenCL: Initialized, trying cl_impl..." << std::endl;
- std::vector<unsigned int> result = internal::blue_noise_cl_impl(
- width, height, filter_size, context, device, program);
-
- clReleaseProgram(program);
- clReleaseContext(context);
-
- if (!result.empty()) {
- return internal::rangeToBl(result, width);
- }
- std::cout << "ERROR: Empty result\n";
- } while (false);
- }
+ if (use_opencl) {
+ // try to use OpenCL
+ do {
+ cl_device_id device;
+ cl_context context;
+ cl_program program;
+ cl_int err;
+
+ cl_platform_id platform;
+
+ int filter_size = (width + height) / 2;
+
+ err = clGetPlatformIDs(1, &platform, nullptr);
+ if (err != CL_SUCCESS) {
+ std::cerr << "OpenCL: Failed to identify a platform\n";
+ break;
+ }
+
+ err = clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 1, &device, nullptr);
+ if (err != CL_SUCCESS) {
+ std::cerr << "OpenCL: Failed to get a device\n";
+ break;
+ }
+
+ context = clCreateContext(nullptr, 1, &device, nullptr, nullptr, &err);
+
+ {
+ char buf[1024];
+ std::ifstream program_file("src/blue_noise.cl");
+ if (!program_file.good()) {
+ std::cerr << "ERROR: Failed to read \"src/blue_noise.cl\" "
+ "(not found?)\n";
+ break;
+ }
+ std::string program_string;
+ while (program_file.good()) {
+ program_file.read(buf, 1024);
+ if (int read_count = program_file.gcount(); read_count > 0) {
+ program_string.append(buf, read_count);
+ }
+ }
+
+ const char *string_ptr = program_string.c_str();
+ std::size_t program_size = program_string.size();
+ program = clCreateProgramWithSource(
+ context, 1, (const char **)&string_ptr, &program_size, &err);
+ if (err != CL_SUCCESS) {
+ std::cerr << "OpenCL: Failed to create the program\n";
+ clReleaseContext(context);
+ break;
+ }
+
+ err = clBuildProgram(program, 1, &device, nullptr, nullptr, nullptr);
+ if (err != CL_SUCCESS) {
+ std::cerr << "OpenCL: Failed to build the program\n";
+
+ std::size_t log_size;
+ clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, 0,
+ nullptr, &log_size);
+ std::unique_ptr<char[]> log = std::make_unique<char[]>(log_size + 1);
+ log[log_size] = 0;
+ clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, log_size,
+ log.get(), nullptr);
+ std::cerr << log.get() << std::endl;
+
+ clReleaseProgram(program);
+ clReleaseContext(context);
+ break;
+ }
+ }
+
+ std::cout << "OpenCL: Initialized, trying cl_impl..." << std::endl;
+ std::vector<unsigned int> result = internal::blue_noise_cl_impl(
+ width, height, filter_size, context, device, program);
+
+ clReleaseProgram(program);
+ clReleaseContext(context);
+
+ if (!result.empty()) {
+ return internal::rangeToBl(result, width);
+ }
+ std::cout << "ERROR: Empty result\n";
+ } while (false);
+ }
#else
- std::clog << "WARNING: Not compiled with OpenCL support!\n";
+ std::clog << "WARNING: Not compiled with OpenCL support!\n";
#endif
- if (!using_opencl) {
- std::cout << "OpenCL: Failed to setup/use or is not enabled, using "
- "regular impl..."
- << std::endl;
- return internal::rangeToBl(
- internal::blue_noise_impl(width, height, threads), width);
- }
+ if (!using_opencl) {
+ std::cout << "OpenCL: Failed to setup/use or is not enabled, using "
+ "regular impl..."
+ << std::endl;
+ return internal::rangeToBl(
+ internal::blue_noise_impl(width, height, threads), width);
+ }
- std::cout << "ERROR: Invalid state (end of blue_noise fn)\n";
- return {};
+ std::cout << "ERROR: Invalid state (end of blue_noise fn)\n";
+ return {};
}
std::vector<unsigned int> dither::internal::blue_noise_impl(int width,
int height,
int threads) {
- int count = width * height;
- std::vector<float> filter_out;
- filter_out.resize(count);
+ int count = width * height;
+ std::vector<float> filter_out;
+ filter_out.resize(count);
- int pixel_count = count * 4 / 10;
- std::vector<bool> pbp = random_noise(count, count * 4 / 10);
- pbp.resize(count);
+ int pixel_count = count * 4 / 10;
+ std::vector<bool> pbp = random_noise(count, count * 4 / 10);
+ pbp.resize(count);
#ifndef NDEBUG
- printf("Inserting %d pixels into image of max count %d\n", pixel_count,
- count);
- // generate image from randomized pbp
- FILE *random_noise_image = fopen("random_noise.pbm", "w");
- fprintf(random_noise_image, "P1\n%d %d\n", width, height);
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- fprintf(random_noise_image, "%d ",
- pbp[utility::twoToOne(x, y, width, height)] ? 1 : 0);
- }
- fputc('\n', random_noise_image);
+ printf("Inserting %d pixels into image of max count %d\n", pixel_count,
+ count);
+ // generate image from randomized pbp
+ FILE *random_noise_image = fopen("random_noise.pbm", "w");
+ fprintf(random_noise_image, "P1\n%d %d\n", width, height);
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ fprintf(random_noise_image, "%d ",
+ pbp[utility::twoToOne(x, y, width, height)] ? 1 : 0);
}
- fclose(random_noise_image);
+ fputc('\n', random_noise_image);
+ }
+ fclose(random_noise_image);
#endif
- // #ifndef NDEBUG
- int iterations = 0;
- // #endif
+ // #ifndef NDEBUG
+ int iterations = 0;
+ // #endif
- int filter_size = (width + height) / 2;
+ int filter_size = (width + height) / 2;
- std::unique_ptr<std::vector<float>> precomputed =
- std::make_unique<std::vector<float>>(
- internal::precompute_gaussian(filter_size));
+ std::unique_ptr<std::vector<float>> precomputed =
+ std::make_unique<std::vector<float>>(
+ internal::precompute_gaussian(filter_size));
- internal::compute_filter(pbp, width, height, count, filter_size, filter_out,
- precomputed.get(), threads);
+ internal::compute_filter(pbp, width, height, count, filter_size, filter_out,
+ precomputed.get(), threads);
#ifndef NDEBUG
- internal::write_filter(filter_out, width, "filter_out_start.pgm");
+ internal::write_filter(filter_out, width, "filter_out_start.pgm");
#endif
- std::cout << "Begin BinaryArray generation loop\n";
- while (true) {
+ std::cout << "Begin BinaryArray generation loop\n";
+ while (true) {
#ifndef NDEBUG
- // if(++iterations % 10 == 0) {
- printf("Iteration %d\n", ++iterations);
+ // if(++iterations % 10 == 0) {
+ printf("Iteration %d\n", ++iterations);
// }
#endif
- // get filter values
- internal::compute_filter(pbp, width, height, count, filter_size,
- filter_out, precomputed.get(), threads);
-
- // #ifndef NDEBUG
- // for(int i = 0; i < count; ++i) {
- // int x, y;
- // std::tie(x, y) = internal::oneToTwo(i, width);
- // printf("%d (%d, %d): %f\n", i, x, y, filter_out[i]);
- // }
- // #endif
-
- int min, max;
- std::tie(min, max) = internal::filter_minmax(filter_out, pbp);
-
- // remove 1
- pbp[max] = false;
-
- // get filter values again
- internal::compute_filter(pbp, width, height, count, filter_size,
- filter_out, precomputed.get(), threads);
-
- // get second buffer's min
- int second_min;
- std::tie(second_min, std::ignore) =
- internal::filter_minmax(filter_out, pbp);
-
- if (second_min == max) {
- pbp[max] = true;
- break;
- } else {
- pbp[second_min] = true;
- }
+ // get filter values
+ internal::compute_filter(pbp, width, height, count, filter_size, filter_out,
+ precomputed.get(), threads);
- if (iterations % 100 == 0) {
- // generate blue_noise image from pbp
-#ifndef NDEBUG
- FILE *blue_noise_image = fopen("blue_noise.pbm", "w");
- fprintf(blue_noise_image, "P1\n%d %d\n", width, height);
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- fprintf(blue_noise_image, "%d ",
- pbp[utility::twoToOne(x, y, width, height)] ? 1
- : 0);
- }
- fputc('\n', blue_noise_image);
- }
- fclose(blue_noise_image);
-#endif
- }
- }
+ // #ifndef NDEBUG
+ // for(int i = 0; i < count; ++i) {
+ // int x, y;
+ // std::tie(x, y) = internal::oneToTwo(i, width);
+ // printf("%d (%d, %d): %f\n", i, x, y, filter_out[i]);
+ // }
+ // #endif
+
+ int min, max;
+ std::tie(min, max) = internal::filter_minmax(filter_out, pbp);
+
+ // remove 1
+ pbp[max] = false;
+
+ // get filter values again
internal::compute_filter(pbp, width, height, count, filter_size, filter_out,
precomputed.get(), threads);
-#ifndef NDEBUG
- internal::write_filter(filter_out, width, "filter_out_final.pgm");
-#endif
+ // get second buffer's min
+ int second_min;
+ std::tie(second_min, std::ignore) =
+ internal::filter_minmax(filter_out, pbp);
+
+ if (second_min == max) {
+ pbp[max] = true;
+ break;
+ } else {
+ pbp[second_min] = true;
+ }
+
+ if (iterations % 100 == 0) {
+ // generate blue_noise image from pbp
#ifndef NDEBUG
- // generate blue_noise image from pbp
- FILE *blue_noise_image = fopen("blue_noise.pbm", "w");
- fprintf(blue_noise_image, "P1\n%d %d\n", width, height);
- for (int y = 0; y < height; ++y) {
+ FILE *blue_noise_image = fopen("blue_noise.pbm", "w");
+ fprintf(blue_noise_image, "P1\n%d %d\n", width, height);
+ for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
- fprintf(blue_noise_image, "%d ",
- pbp[utility::twoToOne(x, y, width, height)] ? 1 : 0);
+ fprintf(blue_noise_image, "%d ",
+ pbp[utility::twoToOne(x, y, width, height)] ? 1 : 0);
}
fputc('\n', blue_noise_image);
+ }
+ fclose(blue_noise_image);
+#endif
}
- fclose(blue_noise_image);
+ }
+ internal::compute_filter(pbp, width, height, count, filter_size, filter_out,
+ precomputed.get(), threads);
+#ifndef NDEBUG
+ internal::write_filter(filter_out, width, "filter_out_final.pgm");
#endif
- std::cout << "Generating dither_array...\n";
- std::vector<unsigned int> dither_array(count);
- int min, max;
- {
- std::vector<bool> pbp_copy(pbp);
- std::cout << "Ranking minority pixels...\n";
- for (unsigned int i = pixel_count; i-- > 0;) {
#ifndef NDEBUG
- std::cout << i << ' ';
-#endif
- internal::compute_filter(pbp, width, height, count, filter_size,
- filter_out, precomputed.get(), threads);
- std::tie(std::ignore, max) =
- internal::filter_minmax(filter_out, pbp);
- pbp[max] = false;
- dither_array[max] = i;
- }
- pbp = pbp_copy;
+ // generate blue_noise image from pbp
+ FILE *blue_noise_image = fopen("blue_noise.pbm", "w");
+ fprintf(blue_noise_image, "P1\n%d %d\n", width, height);
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ fprintf(blue_noise_image, "%d ",
+ pbp[utility::twoToOne(x, y, width, height)] ? 1 : 0);
}
- std::cout << "\nRanking remainder of first half of pixels...\n";
- for (unsigned int i = pixel_count; i < (unsigned int)((count + 1) / 2);
- ++i) {
+ fputc('\n', blue_noise_image);
+ }
+ fclose(blue_noise_image);
+#endif
+
+ std::cout << "Generating dither_array...\n";
+ std::vector<unsigned int> dither_array(count);
+ int min, max;
+ {
+ std::vector<bool> pbp_copy(pbp);
+ std::cout << "Ranking minority pixels...\n";
+ for (unsigned int i = pixel_count; i-- > 0;) {
#ifndef NDEBUG
- std::cout << i << ' ';
+ std::cout << i << ' ';
#endif
- internal::compute_filter(pbp, width, height, count, filter_size,
- filter_out, precomputed.get(), threads);
- std::tie(min, std::ignore) = internal::filter_minmax(filter_out, pbp);
- pbp[min] = true;
- dither_array[min] = i;
+ internal::compute_filter(pbp, width, height, count, filter_size,
+ filter_out, precomputed.get(), threads);
+ std::tie(std::ignore, max) = internal::filter_minmax(filter_out, pbp);
+ pbp[max] = false;
+ dither_array[max] = i;
}
- std::cout << "\nRanking last half of pixels...\n";
- std::vector<bool> reversed_pbp(pbp);
- for (unsigned int i = (count + 1) / 2; i < (unsigned int)count; ++i) {
+ pbp = pbp_copy;
+ }
+ std::cout << "\nRanking remainder of first half of pixels...\n";
+ for (unsigned int i = pixel_count; i < (unsigned int)((count + 1) / 2); ++i) {
#ifndef NDEBUG
- std::cout << i << ' ';
+ std::cout << i << ' ';
#endif
- for (unsigned int i = 0; i < pbp.size(); ++i) {
- reversed_pbp[i] = !pbp[i];
- }
- internal::compute_filter(reversed_pbp, width, height, count,
- filter_size, filter_out, precomputed.get(),
- threads);
- std::tie(std::ignore, max) = internal::filter_minmax(filter_out, pbp);
- pbp[max] = true;
- dither_array[max] = i;
+ internal::compute_filter(pbp, width, height, count, filter_size, filter_out,
+ precomputed.get(), threads);
+ std::tie(min, std::ignore) = internal::filter_minmax(filter_out, pbp);
+ pbp[min] = true;
+ dither_array[min] = i;
+ }
+ std::cout << "\nRanking last half of pixels...\n";
+ std::vector<bool> reversed_pbp(pbp);
+ for (unsigned int i = (count + 1) / 2; i < (unsigned int)count; ++i) {
+#ifndef NDEBUG
+ std::cout << i << ' ';
+#endif
+ for (unsigned int i = 0; i < pbp.size(); ++i) {
+ reversed_pbp[i] = !pbp[i];
}
-
- return dither_array;
+ internal::compute_filter(reversed_pbp, width, height, count, filter_size,
+ filter_out, precomputed.get(), threads);
+ std::tie(std::ignore, max) = internal::filter_minmax(filter_out, pbp);
+ pbp[max] = true;
+ dither_array[max] = i;
+ }
+
+ return dither_array;
}
#if DITHERING_OPENCL_ENABLED == 1
std::vector<unsigned int> dither::internal::blue_noise_cl_impl(
- const int width, const int height, const int filter_size,
- cl_context context, cl_device_id device, cl_program program) {
- cl_int err;
- cl_kernel kernel;
- cl_command_queue queue;
- cl_mem d_filter_out, d_precomputed, d_pbp;
- std::size_t global_size, local_size;
-
- std::vector<float> precomputed = precompute_gaussian(filter_size);
-
- int count = width * height;
- int pixel_count = count * 4 / 10;
- std::vector<bool> pbp = random_noise(count, pixel_count);
- std::vector<int> pbp_i(pbp.size());
-
- queue = clCreateCommandQueueWithProperties(context, device, nullptr, &err);
-
- d_filter_out = clCreateBuffer(context, CL_MEM_WRITE_ONLY,
- count * sizeof(float), nullptr, nullptr);
- d_precomputed = clCreateBuffer(context, CL_MEM_READ_ONLY,
- precomputed.size() * sizeof(float), nullptr,
- nullptr);
- d_pbp = clCreateBuffer(context, CL_MEM_READ_ONLY, count * sizeof(int),
- nullptr, nullptr);
-
- err = clEnqueueWriteBuffer(queue, d_precomputed, CL_TRUE, 0,
- precomputed.size() * sizeof(float),
- &precomputed[0], 0, nullptr, nullptr);
- if (err != CL_SUCCESS) {
- std::cerr << "OpenCL: Failed to write to d_precomputed buffer\n";
- clReleaseMemObject(d_pbp);
- clReleaseMemObject(d_precomputed);
- clReleaseMemObject(d_filter_out);
- clReleaseCommandQueue(queue);
- return {};
- }
-
- kernel = clCreateKernel(program, "do_filter", &err);
- if (err != CL_SUCCESS) {
- std::cerr << "OpenCL: Failed to create kernel: ";
- switch (err) {
- case CL_INVALID_PROGRAM:
- std::cerr << "invalid program\n";
- break;
- case CL_INVALID_PROGRAM_EXECUTABLE:
- std::cerr << "invalid program executable\n";
- break;
- case CL_INVALID_KERNEL_NAME:
- std::cerr << "invalid kernel name\n";
- break;
- case CL_INVALID_KERNEL_DEFINITION:
- std::cerr << "invalid kernel definition\n";
- break;
- case CL_INVALID_VALUE:
- std::cerr << "invalid value\n";
- break;
- case CL_OUT_OF_RESOURCES:
- std::cerr << "out of resources\n";
- break;
- case CL_OUT_OF_HOST_MEMORY:
- std::cerr << "out of host memory\n";
- break;
- default:
- std::cerr << "unknown error\n";
- break;
- }
- clReleaseMemObject(d_pbp);
- clReleaseMemObject(d_precomputed);
- clReleaseMemObject(d_filter_out);
- clReleaseCommandQueue(queue);
- return {};
+ const int width, const int height, const int filter_size,
+ cl_context context, cl_device_id device, cl_program program) {
+ cl_int err;
+ cl_kernel kernel;
+ cl_command_queue queue;
+ cl_mem d_filter_out, d_precomputed, d_pbp;
+ std::size_t global_size, local_size;
+
+ std::vector<float> precomputed = precompute_gaussian(filter_size);
+
+ int count = width * height;
+ int pixel_count = count * 4 / 10;
+ std::vector<bool> pbp = random_noise(count, pixel_count);
+ std::vector<int> pbp_i(pbp.size());
+
+ queue = clCreateCommandQueueWithProperties(context, device, nullptr, &err);
+
+ d_filter_out = clCreateBuffer(context, CL_MEM_WRITE_ONLY,
+ count * sizeof(float), nullptr, nullptr);
+ d_precomputed =
+ clCreateBuffer(context, CL_MEM_READ_ONLY,
+ precomputed.size() * sizeof(float), nullptr, nullptr);
+ d_pbp = clCreateBuffer(context, CL_MEM_READ_ONLY, count * sizeof(int),
+ nullptr, nullptr);
+
+ err = clEnqueueWriteBuffer(queue, d_precomputed, CL_TRUE, 0,
+ precomputed.size() * sizeof(float),
+ &precomputed[0], 0, nullptr, nullptr);
+ if (err != CL_SUCCESS) {
+ std::cerr << "OpenCL: Failed to write to d_precomputed buffer\n";
+ clReleaseMemObject(d_pbp);
+ clReleaseMemObject(d_precomputed);
+ clReleaseMemObject(d_filter_out);
+ clReleaseCommandQueue(queue);
+ return {};
+ }
+
+ kernel = clCreateKernel(program, "do_filter", &err);
+ if (err != CL_SUCCESS) {
+ std::cerr << "OpenCL: Failed to create kernel: ";
+ switch (err) {
+ case CL_INVALID_PROGRAM:
+ std::cerr << "invalid program\n";
+ break;
+ case CL_INVALID_PROGRAM_EXECUTABLE:
+ std::cerr << "invalid program executable\n";
+ break;
+ case CL_INVALID_KERNEL_NAME:
+ std::cerr << "invalid kernel name\n";
+ break;
+ case CL_INVALID_KERNEL_DEFINITION:
+ std::cerr << "invalid kernel definition\n";
+ break;
+ case CL_INVALID_VALUE:
+ std::cerr << "invalid value\n";
+ break;
+ case CL_OUT_OF_RESOURCES:
+ std::cerr << "out of resources\n";
+ break;
+ case CL_OUT_OF_HOST_MEMORY:
+ std::cerr << "out of host memory\n";
+ break;
+ default:
+ std::cerr << "unknown error\n";
+ break;
}
+ clReleaseMemObject(d_pbp);
+ clReleaseMemObject(d_precomputed);
+ clReleaseMemObject(d_filter_out);
+ clReleaseCommandQueue(queue);
+ return {};
+ }
- if (clSetKernelArg(kernel, 0, sizeof(cl_mem), &d_filter_out) !=
- CL_SUCCESS) {
- std::cerr << "OpenCL: Failed to set kernel arg 0\n";
- clReleaseKernel(kernel);
- clReleaseMemObject(d_pbp);
- clReleaseMemObject(d_precomputed);
- clReleaseMemObject(d_filter_out);
- clReleaseCommandQueue(queue);
- return {};
- }
- if (clSetKernelArg(kernel, 1, sizeof(cl_mem), &d_precomputed) !=
+ if (clSetKernelArg(kernel, 0, sizeof(cl_mem), &d_filter_out) != CL_SUCCESS) {
+ std::cerr << "OpenCL: Failed to set kernel arg 0\n";
+ clReleaseKernel(kernel);
+ clReleaseMemObject(d_pbp);
+ clReleaseMemObject(d_precomputed);
+ clReleaseMemObject(d_filter_out);
+ clReleaseCommandQueue(queue);
+ return {};
+ }
+ if (clSetKernelArg(kernel, 1, sizeof(cl_mem), &d_precomputed) != CL_SUCCESS) {
+ std::cerr << "OpenCL: Failed to set kernel arg 1\n";
+ clReleaseKernel(kernel);
+ clReleaseMemObject(d_pbp);
+ clReleaseMemObject(d_precomputed);
+ clReleaseMemObject(d_filter_out);
+ clReleaseCommandQueue(queue);
+ return {};
+ }
+ if (clSetKernelArg(kernel, 2, sizeof(cl_mem), &d_pbp) != CL_SUCCESS) {
+ std::cerr << "OpenCL: Failed to set kernel arg 2\n";
+ clReleaseKernel(kernel);
+ clReleaseMemObject(d_pbp);
+ clReleaseMemObject(d_precomputed);
+ clReleaseMemObject(d_filter_out);
+ clReleaseCommandQueue(queue);
+ return {};
+ }
+ if (clSetKernelArg(kernel, 3, sizeof(int), &width) != CL_SUCCESS) {
+ std::cerr << "OpenCL: Failed to set kernel arg 3\n";
+ clReleaseKernel(kernel);
+ clReleaseMemObject(d_pbp);
+ clReleaseMemObject(d_precomputed);
+ clReleaseMemObject(d_filter_out);
+ clReleaseCommandQueue(queue);
+ return {};
+ }
+ if (clSetKernelArg(kernel, 4, sizeof(int), &height) != CL_SUCCESS) {
+ std::cerr << "OpenCL: Failed to set kernel arg 4\n";
+ clReleaseKernel(kernel);
+ clReleaseMemObject(d_pbp);
+ clReleaseMemObject(d_precomputed);
+ clReleaseMemObject(d_filter_out);
+ clReleaseCommandQueue(queue);
+ return {};
+ }
+ if (filter_size % 2 == 0) {
+ int filter_size_odd = filter_size + 1;
+ if (clSetKernelArg(kernel, 5, sizeof(int), &filter_size_odd) !=
CL_SUCCESS) {
- std::cerr << "OpenCL: Failed to set kernel arg 1\n";
- clReleaseKernel(kernel);
- clReleaseMemObject(d_pbp);
- clReleaseMemObject(d_precomputed);
- clReleaseMemObject(d_filter_out);
- clReleaseCommandQueue(queue);
- return {};
+ std::cerr << "OpenCL: Failed to set kernel arg 4\n";
+ clReleaseKernel(kernel);
+ clReleaseMemObject(d_pbp);
+ clReleaseMemObject(d_precomputed);
+ clReleaseMemObject(d_filter_out);
+ clReleaseCommandQueue(queue);
+ return {};
}
- if (clSetKernelArg(kernel, 2, sizeof(cl_mem), &d_pbp) != CL_SUCCESS) {
- std::cerr << "OpenCL: Failed to set kernel arg 2\n";
- clReleaseKernel(kernel);
- clReleaseMemObject(d_pbp);
- clReleaseMemObject(d_precomputed);
- clReleaseMemObject(d_filter_out);
- clReleaseCommandQueue(queue);
- return {};
- }
- if (clSetKernelArg(kernel, 3, sizeof(int), &width) != CL_SUCCESS) {
- std::cerr << "OpenCL: Failed to set kernel arg 3\n";
- clReleaseKernel(kernel);
- clReleaseMemObject(d_pbp);
- clReleaseMemObject(d_precomputed);
- clReleaseMemObject(d_filter_out);
- clReleaseCommandQueue(queue);
- return {};
- }
- if (clSetKernelArg(kernel, 4, sizeof(int), &height) != CL_SUCCESS) {
- std::cerr << "OpenCL: Failed to set kernel arg 4\n";
- clReleaseKernel(kernel);
- clReleaseMemObject(d_pbp);
- clReleaseMemObject(d_precomputed);
- clReleaseMemObject(d_filter_out);
- clReleaseCommandQueue(queue);
- return {};
- }
- if (filter_size % 2 == 0) {
- int filter_size_odd = filter_size + 1;
- if (clSetKernelArg(kernel, 5, sizeof(int), &filter_size_odd) !=
- CL_SUCCESS) {
- std::cerr << "OpenCL: Failed to set kernel arg 4\n";
- clReleaseKernel(kernel);
- clReleaseMemObject(d_pbp);
- clReleaseMemObject(d_precomputed);
- clReleaseMemObject(d_filter_out);
- clReleaseCommandQueue(queue);
- return {};
- }
- } else {
- if (clSetKernelArg(kernel, 5, sizeof(int), &filter_size) !=
- CL_SUCCESS) {
- std::cerr << "OpenCL: Failed to set kernel arg 4\n";
- clReleaseKernel(kernel);
- clReleaseMemObject(d_pbp);
- clReleaseMemObject(d_precomputed);
- clReleaseMemObject(d_filter_out);
- clReleaseCommandQueue(queue);
- return {};
- }
+ } else {
+ if (clSetKernelArg(kernel, 5, sizeof(int), &filter_size) != CL_SUCCESS) {
+ std::cerr << "OpenCL: Failed to set kernel arg 4\n";
+ clReleaseKernel(kernel);
+ clReleaseMemObject(d_pbp);
+ clReleaseMemObject(d_precomputed);
+ clReleaseMemObject(d_filter_out);
+ clReleaseCommandQueue(queue);
+ return {};
}
+ }
- if (clGetKernelWorkGroupInfo(kernel, device, CL_KERNEL_WORK_GROUP_SIZE,
- sizeof(std::size_t), &local_size,
- nullptr) != CL_SUCCESS) {
- std::cerr << "OpenCL: Failed to get work group size\n";
- clReleaseKernel(kernel);
- clReleaseMemObject(d_pbp);
- clReleaseMemObject(d_precomputed);
- clReleaseMemObject(d_filter_out);
- clReleaseCommandQueue(queue);
- return {};
- }
- global_size =
- (std::size_t)std::ceil(count / (float)local_size) * local_size;
+ if (clGetKernelWorkGroupInfo(kernel, device, CL_KERNEL_WORK_GROUP_SIZE,
+ sizeof(std::size_t), &local_size,
+ nullptr) != CL_SUCCESS) {
+ std::cerr << "OpenCL: Failed to get work group size\n";
+ clReleaseKernel(kernel);
+ clReleaseMemObject(d_pbp);
+ clReleaseMemObject(d_precomputed);
+ clReleaseMemObject(d_filter_out);
+ clReleaseCommandQueue(queue);
+ return {};
+ }
+ global_size = (std::size_t)std::ceil(count / (float)local_size) * local_size;
- std::cout << "OpenCL: global = " << global_size
- << ", local = " << local_size << std::endl;
+ std::cout << "OpenCL: global = " << global_size << ", local = " << local_size
+ << std::endl;
- std::vector<float> filter(count);
+ std::vector<float> filter(count);
- bool reversed_pbp = false;
+ bool reversed_pbp = false;
- const auto get_filter = [&queue, &kernel, &global_size, &local_size,
- &d_filter_out, &d_pbp, &pbp, &pbp_i, &count,
- &filter, &err, &reversed_pbp]() -> bool {
- for (unsigned int i = 0; i < pbp.size(); ++i) {
- if (reversed_pbp) {
- pbp_i[i] = pbp[i] ? 0 : 1;
- } else {
- pbp_i[i] = pbp[i] ? 1 : 0;
- }
- }
- if (clEnqueueWriteBuffer(queue, d_pbp, CL_TRUE, 0, count * sizeof(int),
- &pbp_i[0], 0, nullptr,
- nullptr) != CL_SUCCESS) {
- std::cerr << "OpenCL: Failed to write to d_pbp buffer\n";
- return false;
- }
+ const auto get_filter = [&queue, &kernel, &global_size, &local_size,
+ &d_filter_out, &d_pbp, &pbp, &pbp_i, &count, &filter,
+ &err, &reversed_pbp]() -> bool {
+ for (unsigned int i = 0; i < pbp.size(); ++i) {
+ if (reversed_pbp) {
+ pbp_i[i] = pbp[i] ? 0 : 1;
+ } else {
+ pbp_i[i] = pbp[i] ? 1 : 0;
+ }
+ }
+ if (clEnqueueWriteBuffer(queue, d_pbp, CL_TRUE, 0, count * sizeof(int),
+ &pbp_i[0], 0, nullptr, nullptr) != CL_SUCCESS) {
+ std::cerr << "OpenCL: Failed to write to d_pbp buffer\n";
+ return false;
+ }
- if (err = clEnqueueNDRangeKernel(queue, kernel, 1, nullptr,
- &global_size, &local_size, 0, nullptr,
- nullptr);
- err != CL_SUCCESS) {
- std::cerr << "OpenCL: Failed to enqueue task: ";
- switch (err) {
- case CL_INVALID_PROGRAM_EXECUTABLE:
- std::cerr << "invalid program executable\n";
- break;
- case CL_INVALID_COMMAND_QUEUE:
- std::cerr << "invalid command queue\n";
- break;
- case CL_INVALID_KERNEL:
- std::cerr << "invalid kernel\n";
- break;
- case CL_INVALID_CONTEXT:
- std::cerr << "invalid context\n";
- break;
- case CL_INVALID_KERNEL_ARGS:
- std::cerr << "invalid kernel args\n";
- break;
- case CL_INVALID_WORK_DIMENSION:
- std::cerr << "invalid work dimension\n";
- break;
- case CL_INVALID_GLOBAL_WORK_SIZE:
- std::cerr << "invalid global work size\n";
- break;
- case CL_INVALID_GLOBAL_OFFSET:
- std::cerr << "invalid global offset\n";
- break;
- case CL_INVALID_WORK_GROUP_SIZE:
- std::cerr << "invalid work group size\n";
- break;
- case CL_INVALID_WORK_ITEM_SIZE:
- std::cerr << "invalid work item size\n";
- break;
- case CL_MISALIGNED_SUB_BUFFER_OFFSET:
- std::cerr << "misaligned sub buffer offset\n";
- break;
- default:
- std::cerr << "Unknown\n";
- break;
- }
- return false;
- }
+ if (err = clEnqueueNDRangeKernel(queue, kernel, 1, nullptr, &global_size,
+ &local_size, 0, nullptr, nullptr);
+ err != CL_SUCCESS) {
+ std::cerr << "OpenCL: Failed to enqueue task: ";
+ switch (err) {
+ case CL_INVALID_PROGRAM_EXECUTABLE:
+ std::cerr << "invalid program executable\n";
+ break;
+ case CL_INVALID_COMMAND_QUEUE:
+ std::cerr << "invalid command queue\n";
+ break;
+ case CL_INVALID_KERNEL:
+ std::cerr << "invalid kernel\n";
+ break;
+ case CL_INVALID_CONTEXT:
+ std::cerr << "invalid context\n";
+ break;
+ case CL_INVALID_KERNEL_ARGS:
+ std::cerr << "invalid kernel args\n";
+ break;
+ case CL_INVALID_WORK_DIMENSION:
+ std::cerr << "invalid work dimension\n";
+ break;
+ case CL_INVALID_GLOBAL_WORK_SIZE:
+ std::cerr << "invalid global work size\n";
+ break;
+ case CL_INVALID_GLOBAL_OFFSET:
+ std::cerr << "invalid global offset\n";
+ break;
+ case CL_INVALID_WORK_GROUP_SIZE:
+ std::cerr << "invalid work group size\n";
+ break;
+ case CL_INVALID_WORK_ITEM_SIZE:
+ std::cerr << "invalid work item size\n";
+ break;
+ case CL_MISALIGNED_SUB_BUFFER_OFFSET:
+ std::cerr << "misaligned sub buffer offset\n";
+ break;
+ default:
+ std::cerr << "Unknown\n";
+ break;
+ }
+ return false;
+ }
- clFinish(queue);
+ clFinish(queue);
- clEnqueueReadBuffer(queue, d_filter_out, CL_TRUE, 0,
- count * sizeof(float), &filter[0], 0, nullptr,
- nullptr);
+ clEnqueueReadBuffer(queue, d_filter_out, CL_TRUE, 0, count * sizeof(float),
+ &filter[0], 0, nullptr, nullptr);
- return true;
- };
+ return true;
+ };
- {
+ {
#ifndef NDEBUG
- printf("Inserting %d pixels into image of max count %d\n", pixel_count,
- count);
- // generate image from randomized pbp
- FILE *random_noise_image = fopen("random_noise.pbm", "w");
- fprintf(random_noise_image, "P1\n%d %d\n", width, height);
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- fprintf(random_noise_image, "%d ",
- pbp[utility::twoToOne(x, y, width, height)] ? 1 : 0);
- }
- fputc('\n', random_noise_image);
- }
- fclose(random_noise_image);
-#endif
+ printf("Inserting %d pixels into image of max count %d\n", pixel_count,
+ count);
+ // generate image from randomized pbp
+ FILE *random_noise_image = fopen("random_noise.pbm", "w");
+ fprintf(random_noise_image, "P1\n%d %d\n", width, height);
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ fprintf(random_noise_image, "%d ",
+ pbp[utility::twoToOne(x, y, width, height)] ? 1 : 0);
+ }
+ fputc('\n', random_noise_image);
}
+ fclose(random_noise_image);
+#endif
+ }
- if (!get_filter()) {
- std::cerr << "OpenCL: Failed to execute do_filter (at start)\n";
- clReleaseKernel(kernel);
- clReleaseMemObject(d_pbp);
- clReleaseMemObject(d_precomputed);
- clReleaseMemObject(d_filter_out);
- clReleaseCommandQueue(queue);
- return {};
- } else {
+ if (!get_filter()) {
+ std::cerr << "OpenCL: Failed to execute do_filter (at start)\n";
+ clReleaseKernel(kernel);
+ clReleaseMemObject(d_pbp);
+ clReleaseMemObject(d_precomputed);
+ clReleaseMemObject(d_filter_out);
+ clReleaseCommandQueue(queue);
+ return {};
+ } else {
#ifndef NDEBUG
- internal::write_filter(filter, width, "filter_out_start.pgm");
+ internal::write_filter(filter, width, "filter_out_start.pgm");
#endif
- }
+ }
- int iterations = 0;
+ int iterations = 0;
- std::cout << "Begin BinaryArray generation loop\n";
- while (true) {
+ std::cout << "Begin BinaryArray generation loop\n";
+ while (true) {
#ifndef NDEBUG
- printf("Iteration %d\n", ++iterations);
+ printf("Iteration %d\n", ++iterations);
#endif
- if (!get_filter()) {
- std::cerr << "OpenCL: Failed to execute do_filter\n";
- break;
- }
-
- int min, max;
- std::tie(min, max) = internal::filter_minmax(filter, pbp);
+ if (!get_filter()) {
+ std::cerr << "OpenCL: Failed to execute do_filter\n";
+ break;
+ }
- pbp[max] = false;
+ int min, max;
+ std::tie(min, max) = internal::filter_minmax(filter, pbp);
- if (!get_filter()) {
- std::cerr << "OpenCL: Failed to execute do_filter\n";
- break;
- }
+ pbp[max] = false;
- // get second buffer's min
- int second_min;
- std::tie(second_min, std::ignore) =
- internal::filter_minmax(filter, pbp);
+ if (!get_filter()) {
+ std::cerr << "OpenCL: Failed to execute do_filter\n";
+ break;
+ }
- if (second_min == max) {
- pbp[max] = true;
- break;
- } else {
- pbp[second_min] = true;
- }
+ // get second buffer's min
+ int second_min;
+ std::tie(second_min, std::ignore) = internal::filter_minmax(filter, pbp);
- if (iterations % 100 == 0) {
-#ifndef NDEBUG
- std::cout << "max was " << max << ", second_min is " << second_min
- << std::endl;
- // generate blue_noise image from pbp
- FILE *blue_noise_image = fopen("blue_noise.pbm", "w");
- fprintf(blue_noise_image, "P1\n%d %d\n", width, height);
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- fprintf(blue_noise_image, "%d ",
- pbp[utility::twoToOne(x, y, width, height)] ? 1
- : 0);
- }
- fputc('\n', blue_noise_image);
- }
- fclose(blue_noise_image);
-#endif
- }
+ if (second_min == max) {
+ pbp[max] = true;
+ break;
+ } else {
+ pbp[second_min] = true;
}
- if (!get_filter()) {
- std::cerr << "OpenCL: Failed to execute do_filter (at end)\n";
- } else {
+ if (iterations % 100 == 0) {
#ifndef NDEBUG
- internal::write_filter(filter, width, "filter_out_final.pgm");
- FILE *blue_noise_image = fopen("blue_noise.pbm", "w");
- fprintf(blue_noise_image, "P1\n%d %d\n", width, height);
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- fprintf(blue_noise_image, "%d ",
- pbp[utility::twoToOne(x, y, width, height)] ? 1 : 0);
- }
- fputc('\n', blue_noise_image);
+ std::cout << "max was " << max << ", second_min is " << second_min
+ << std::endl;
+ // generate blue_noise image from pbp
+ FILE *blue_noise_image = fopen("blue_noise.pbm", "w");
+ fprintf(blue_noise_image, "P1\n%d %d\n", width, height);
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ fprintf(blue_noise_image, "%d ",
+ pbp[utility::twoToOne(x, y, width, height)] ? 1 : 0);
}
- fclose(blue_noise_image);
+ fputc('\n', blue_noise_image);
+ }
+ fclose(blue_noise_image);
#endif
}
+ }
+ if (!get_filter()) {
+ std::cerr << "OpenCL: Failed to execute do_filter (at end)\n";
+ } else {
#ifndef NDEBUG
- {
- image::Bl pbp_image = toBl(pbp, width);
- pbp_image.writeToFile(image::file_type::PNG, true,
- "debug_pbp_before.png");
+ internal::write_filter(filter, width, "filter_out_final.pgm");
+ FILE *blue_noise_image = fopen("blue_noise.pbm", "w");
+ fprintf(blue_noise_image, "P1\n%d %d\n", width, height);
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ fprintf(blue_noise_image, "%d ",
+ pbp[utility::twoToOne(x, y, width, height)] ? 1 : 0);
+ }
+ fputc('\n', blue_noise_image);
}
+ fclose(blue_noise_image);
#endif
+ }
- std::cout << "Generating dither_array...\n";
#ifndef NDEBUG
- std::unordered_set<unsigned int> set;
+ {
+ image::Bl pbp_image = toBl(pbp, width);
+ pbp_image.writeToFile(image::file_type::PNG, true, "debug_pbp_before.png");
+ }
#endif
- std::vector<unsigned int> dither_array(count, 0);
- int min, max;
- {
- std::vector<bool> pbp_copy(pbp);
- std::cout << "Ranking minority pixels...\n";
- for (unsigned int i = pixel_count; i-- > 0;) {
+
+ std::cout << "Generating dither_array...\n";
#ifndef NDEBUG
- std::cout << i << ' ';
+ std::unordered_set<unsigned int> set;
#endif
- get_filter();
- std::tie(std::ignore, max) = internal::filter_minmax(filter, pbp);
- pbp.at(max) = false;
- dither_array.at(max) = i;
+ std::vector<unsigned int> dither_array(count, 0);
+ int min, max;
+ {
+ std::vector<bool> pbp_copy(pbp);
+ std::cout << "Ranking minority pixels...\n";
+ for (unsigned int i = pixel_count; i-- > 0;) {
#ifndef NDEBUG
- if (set.find(max) != set.end()) {
- std::cout << "\nWARNING: Reusing index " << max << '\n';
- } else {
- set.insert(max);
- }
+ std::cout << i << ' ';
#endif
- }
- pbp = pbp_copy;
+ get_filter();
+ std::tie(std::ignore, max) = internal::filter_minmax(filter, pbp);
+ pbp.at(max) = false;
+ dither_array.at(max) = i;
#ifndef NDEBUG
- image::Bl min_pixels = internal::rangeToBl(dither_array, width);
- min_pixels.writeToFile(image::file_type::PNG, true,
- "da_min_pixels.png");
+ if (set.find(max) != set.end()) {
+ std::cout << "\nWARNING: Reusing index " << max << '\n';
+ } else {
+ set.insert(max);
+ }
#endif
}
- std::cout << "\nRanking remainder of first half of pixels...\n";
- for (unsigned int i = pixel_count; i < (unsigned int)((count + 1) / 2);
- ++i) {
+ pbp = pbp_copy;
#ifndef NDEBUG
- std::cout << i << ' ';
+ image::Bl min_pixels = internal::rangeToBl(dither_array, width);
+ min_pixels.writeToFile(image::file_type::PNG, true, "da_min_pixels.png");
#endif
- get_filter();
- std::tie(min, std::ignore) = internal::filter_minmax(filter, pbp);
- pbp.at(min) = true;
- dither_array.at(min) = i;
+ }
+ std::cout << "\nRanking remainder of first half of pixels...\n";
+ for (unsigned int i = pixel_count; i < (unsigned int)((count + 1) / 2); ++i) {
#ifndef NDEBUG
- if (set.find(min) != set.end()) {
- std::cout << "\nWARNING: Reusing index " << min << '\n';
- } else {
- set.insert(min);
- }
+ std::cout << i << ' ';
#endif
- }
+ get_filter();
+ std::tie(min, std::ignore) = internal::filter_minmax(filter, pbp);
+ pbp.at(min) = true;
+ dither_array.at(min) = i;
#ifndef NDEBUG
- {
- image::Bl min_pixels = internal::rangeToBl(dither_array, width);
- min_pixels.writeToFile(image::file_type::PNG, true,
- "da_mid_pixels.png");
- get_filter();
- internal::write_filter(filter, width, "filter_mid.pgm");
- image::Bl pbp_image = toBl(pbp, width);
- pbp_image.writeToFile(image::file_type::PNG, true, "debug_pbp_mid.png");
+ if (set.find(min) != set.end()) {
+ std::cout << "\nWARNING: Reusing index " << min << '\n';
+ } else {
+ set.insert(min);
}
#endif
- std::cout << "\nRanking last half of pixels...\n";
- reversed_pbp = true;
- for (unsigned int i = (count + 1) / 2; i < (unsigned int)count; ++i) {
+ }
#ifndef NDEBUG
- std::cout << i << ' ';
+ {
+ image::Bl min_pixels = internal::rangeToBl(dither_array, width);
+ min_pixels.writeToFile(image::file_type::PNG, true, "da_mid_pixels.png");
+ get_filter();
+ internal::write_filter(filter, width, "filter_mid.pgm");
+ image::Bl pbp_image = toBl(pbp, width);
+ pbp_image.writeToFile(image::file_type::PNG, true, "debug_pbp_mid.png");
+ }
#endif
- get_filter();
- std::tie(std::ignore, max) = internal::filter_minmax(filter, pbp);
- pbp.at(max) = true;
- dither_array.at(max) = i;
+ std::cout << "\nRanking last half of pixels...\n";
+ reversed_pbp = true;
+ for (unsigned int i = (count + 1) / 2; i < (unsigned int)count; ++i) {
#ifndef NDEBUG
- if (set.find(max) != set.end()) {
- std::cout << "\nWARNING: Reusing index " << max << '\n';
- } else {
- set.insert(max);
- }
+ std::cout << i << ' ';
#endif
+ get_filter();
+ std::tie(std::ignore, max) = internal::filter_minmax(filter, pbp);
+ pbp.at(max) = true;
+ dither_array.at(max) = i;
+#ifndef NDEBUG
+ if (set.find(max) != set.end()) {
+ std::cout << "\nWARNING: Reusing index " << max << '\n';
+ } else {
+ set.insert(max);
}
- std::cout << std::endl;
+#endif
+ }
+ std::cout << std::endl;
#ifndef NDEBUG
- {
- get_filter();
- internal::write_filter(filter, width, "filter_after.pgm");
- image::Bl pbp_image = toBl(pbp, width);
- pbp_image.writeToFile(image::file_type::PNG, true,
- "debug_pbp_after.png");
- }
+ {
+ get_filter();
+ internal::write_filter(filter, width, "filter_after.pgm");
+ image::Bl pbp_image = toBl(pbp, width);
+ pbp_image.writeToFile(image::file_type::PNG, true, "debug_pbp_after.png");
+ }
#endif
- clReleaseKernel(kernel);
- clReleaseMemObject(d_pbp);
- clReleaseMemObject(d_precomputed);
- clReleaseMemObject(d_filter_out);
- clReleaseCommandQueue(queue);
- return dither_array;
+ clReleaseKernel(kernel);
+ clReleaseMemObject(d_pbp);
+ clReleaseMemObject(d_precomputed);
+ clReleaseMemObject(d_filter_out);
+ clReleaseCommandQueue(queue);
+ return dither_array;
}
#endif
#ifndef BLUE_NOISE_HPP
#define BLUE_NOISE_HPP
-#include <limits>
-#include <vector>
-#include <functional>
-#include <unordered_set>
-#include <condition_variable>
-#include <mutex>
-#include <thread>
+#include <CL/opencl.h>
+#include <sys/sysinfo.h>
+
+#include <cassert>
#include <chrono>
+#include <cmath>
+#include <condition_variable>
#include <cstdio>
+#include <functional>
+#include <iostream>
+#include <limits>
+#include <mutex>
#include <queue>
#include <random>
-#include <cassert>
#include <stdexcept>
-#include <iostream>
-#include <cmath>
-
-#include <sys/sysinfo.h>
-
-#include <CL/opencl.h>
+#include <thread>
+#include <unordered_set>
+#include <vector>
-#include "utility.hpp"
#include "image.hpp"
+#include "utility.hpp"
namespace dither {
-image::Bl blue_noise(int width, int height, int threads = 1, bool use_opencl = true);
+image::Bl blue_noise(int width, int height, int threads = 1,
+ bool use_opencl = true);
namespace internal {
- std::vector<unsigned int> blue_noise_impl(int width, int height, int threads = 1);
- std::vector<unsigned int> blue_noise_cl_impl(
- const int width, const int height, const int filter_size,
- cl_context context, cl_device_id device, cl_program program);
-
- inline std::vector<bool> random_noise(int size, int subsize) {
- std::vector<bool> pbp(size);
- std::default_random_engine re(std::random_device{}());
- std::uniform_int_distribution<int> dist(0, size - 1);
-
- // initialize pbp
- for(int i = 0; i < size; ++i) {
- if(i < subsize) {
- pbp[i] = true;
- } else {
- pbp[i] = false;
- }
- }
- // randomize pbp
- for(int i = 0; i < size-1; ++i) {
- decltype(dist)::param_type range{i+1, size-1};
- int ridx = dist(re, range);
- // probably can't use std::swap since using std::vector<bool>
- bool temp = pbp[i];
- pbp[i] = pbp[ridx];
- pbp[ridx] = temp;
- }
-
- return pbp;
+std::vector<unsigned int> blue_noise_impl(int width, int height,
+ int threads = 1);
+std::vector<unsigned int> blue_noise_cl_impl(const int width, const int height,
+ const int filter_size,
+ cl_context context,
+ cl_device_id device,
+ cl_program program);
+
+inline std::vector<bool> random_noise(int size, int subsize) {
+ std::vector<bool> pbp(size);
+ std::default_random_engine re(std::random_device{}());
+ std::uniform_int_distribution<int> dist(0, size - 1);
+
+ // initialize pbp
+ for (int i = 0; i < size; ++i) {
+ if (i < subsize) {
+ pbp[i] = true;
+ } else {
+ pbp[i] = false;
}
-
- constexpr float mu = 1.5F;
- constexpr float mu_squared = mu * mu;
- constexpr float double_mu_squared = 2.0F * mu * mu;
-
- inline float gaussian(float x, float y) {
- return std::exp(-(x*x + y*y)/(double_mu_squared));
+ }
+ // randomize pbp
+ for (int i = 0; i < size - 1; ++i) {
+ decltype(dist)::param_type range{i + 1, size - 1};
+ int ridx = dist(re, range);
+ // probably can't use std::swap since using std::vector<bool>
+ bool temp = pbp[i];
+ pbp[i] = pbp[ridx];
+ pbp[ridx] = temp;
+ }
+
+ return pbp;
+}
+
+constexpr float mu = 1.5F;
+constexpr float mu_squared = mu * mu;
+constexpr float double_mu_squared = 2.0F * mu * mu;
+
+inline float gaussian(float x, float y) {
+ return std::exp(-(x * x + y * y) / (double_mu_squared));
+}
+
+inline std::vector<float> precompute_gaussian(int size) {
+ std::vector<float> precomputed;
+ if (size % 2 == 0) {
+ ++size;
+ }
+ precomputed.reserve(size * size);
+
+ for (int i = 0; i < size * size; ++i) {
+ auto xy = utility::oneToTwo(i, size);
+ precomputed.push_back(
+ gaussian(xy.first - (size / 2), xy.second - (size / 2)));
+ }
+
+ return precomputed;
+}
+
+inline float filter(const std::vector<bool> &pbp, int x, int y, int width,
+ int height, int filter_size) {
+ float sum = 0.0f;
+
+ if (filter_size % 2 == 0) {
+ ++filter_size;
+ }
+
+ // Should be range -M/2 to M/2, but size_t cannot be negative, so range
+ // is 0 to M.
+ // p' = (M + x - (p - M/2)) % M = (3M/2 + x - p) % M
+ // q' = (N + y - (q - M/2)) % N = (N + M/2 + y - q) % N
+ for (int q = 0; q < filter_size; ++q) {
+ int q_prime = (height - filter_size / 2 + y + q) % height;
+ for (int p = 0; p < filter_size; ++p) {
+ int p_prime = (width - filter_size / 2 + x + p) % width;
+ if (pbp[utility::twoToOne(p_prime, q_prime, width, height)]) {
+ sum += gaussian(p - filter_size / 2, q - filter_size / 2);
+ }
}
-
- inline std::vector<float> precompute_gaussian(int size) {
- std::vector<float> precomputed;
- if (size % 2 == 0) {
- ++size;
- }
- precomputed.reserve(size * size);
-
- for(int i = 0; i < size * size; ++i) {
- auto xy = utility::oneToTwo(i, size);
- precomputed.push_back(gaussian(
- xy.first - (size / 2),
- xy.second - (size / 2)));
- }
-
- return precomputed;
+ }
+
+ return sum;
+}
+
+inline float filter_with_precomputed(const std::vector<bool> &pbp, int x, int y,
+ int width, int height, int filter_size,
+ const std::vector<float> &precomputed) {
+ float sum = 0.0f;
+
+ if (filter_size % 2 == 0) {
+ ++filter_size;
+ }
+
+ for (int q = 0; q < filter_size; ++q) {
+ int q_prime = (height - filter_size / 2 + y + q) % height;
+ for (int p = 0; p < filter_size; ++p) {
+ int p_prime = (width - filter_size / 2 + x + p) % width;
+ if (pbp[utility::twoToOne(p_prime, q_prime, width, height)]) {
+ sum += precomputed[utility::twoToOne(p, q, filter_size, filter_size)];
+ }
}
-
- inline float filter(
- const std::vector<bool>& pbp,
- int x, int y,
- int width, int height, int filter_size) {
- float sum = 0.0f;
-
- if (filter_size % 2 == 0) {
- ++filter_size;
+ }
+
+ return sum;
+}
+
+inline void compute_filter(const std::vector<bool> &pbp, int width, int height,
+ int count, int filter_size,
+ std::vector<float> &filter_out,
+ const std::vector<float> *precomputed = nullptr,
+ int threads = 1) {
+ if (threads == 1) {
+ if (precomputed) {
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ filter_out[utility::twoToOne(x, y, width, height)] =
+ internal::filter_with_precomputed(pbp, x, y, width, height,
+ filter_size, *precomputed);
}
-
- // Should be range -M/2 to M/2, but size_t cannot be negative, so range
- // is 0 to M.
- // p' = (M + x - (p - M/2)) % M = (3M/2 + x - p) % M
- // q' = (N + y - (q - M/2)) % N = (N + M/2 + y - q) % N
- for(int q = 0; q < filter_size; ++q) {
- int q_prime = (height - filter_size / 2 + y + q) % height;
- for(int p = 0; p < filter_size; ++p) {
- int p_prime = (width - filter_size / 2 + x + p) % width;
- if(pbp[utility::twoToOne(p_prime, q_prime, width, height)]) {
- sum += gaussian(p - filter_size/2,
- q - filter_size/2);
- }
- }
+ }
+ } else {
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ filter_out[utility::twoToOne(x, y, width, height)] =
+ internal::filter(pbp, x, y, width, height, filter_size);
}
-
- return sum;
+ }
}
-
- inline float filter_with_precomputed(
- const std::vector<bool>& pbp,
- int x, int y,
- int width, int height, int filter_size,
- const std::vector<float> &precomputed) {
- float sum = 0.0f;
-
- if (filter_size % 2 == 0) {
- ++filter_size;
- }
-
- for(int q = 0; q < filter_size; ++q) {
- int q_prime = (height - filter_size / 2 + y + q) % height;
- for(int p = 0; p < filter_size; ++p) {
- int p_prime = (width - filter_size / 2 + x + p) % width;
- if(pbp[utility::twoToOne(p_prime, q_prime, width, height)]) {
- sum += precomputed[utility::twoToOne(p, q, filter_size, filter_size)];
- }
- }
- }
-
- return sum;
+ } else {
+ if (threads == 0) {
+ threads = 10;
}
-
- inline void compute_filter(
- const std::vector<bool> &pbp, int width, int height,
- int count, int filter_size, std::vector<float> &filter_out,
- const std::vector<float> *precomputed = nullptr,
- int threads = 1) {
- if(threads == 1) {
- if(precomputed) {
- for(int y = 0; y < height; ++y) {
- for(int x = 0; x < width; ++x) {
- filter_out[utility::twoToOne(x, y, width, height)] =
- internal::filter_with_precomputed(
- pbp, x, y, width, height, filter_size, *precomputed);
- }
- }
- } else {
- for(int y = 0; y < height; ++y) {
- for(int x = 0; x < width; ++x) {
- filter_out[utility::twoToOne(x, y, width, height)] =
- internal::filter(pbp, x, y, width, height, filter_size);
- }
- }
- }
- } else {
- if(threads == 0) {
- threads = 10;
- }
- int active_count = 0;
- std::mutex cv_mutex;
- std::condition_variable cv;
- if(precomputed) {
- for(int i = 0; i < count; ++i) {
- {
- std::unique_lock lock(cv_mutex);
- active_count += 1;
- }
- std::thread t([] (int *ac, std::mutex *cvm,
- std::condition_variable *cv, int i,
- const std::vector<bool> *pbp, int width,
- int height, int filter_size,
- std::vector<float> *fout,
- const std::vector<float> *precomputed) {
- int x, y;
- std::tie(x, y) = utility::oneToTwo(i, width);
- (*fout)[i] = internal::filter_with_precomputed(
- *pbp, x, y, width, height, filter_size, *precomputed);
- std::unique_lock lock(*cvm);
- *ac -= 1;
- cv->notify_all();
- },
- &active_count, &cv_mutex, &cv, i, &pbp, width, height,
- filter_size, &filter_out, precomputed);
- t.detach();
-
- std::unique_lock lock(cv_mutex);
- while(active_count >= threads) {
- cv.wait_for(lock, std::chrono::seconds(1));
- }
- }
- } else {
- for(int i = 0; i < count; ++i) {
- {
- std::unique_lock lock(cv_mutex);
- active_count += 1;
- }
- std::thread t([] (int *ac, std::mutex *cvm,
- std::condition_variable *cv, int i,
- const std::vector<bool> *pbp, int width,
- int height, int filter_size,
- std::vector<float> *fout) {
- int x, y;
- std::tie(x, y) = utility::oneToTwo(i, width);
- (*fout)[i] = internal::filter(
- *pbp, x, y, width, height, filter_size);
- std::unique_lock lock(*cvm);
- *ac -= 1;
- cv->notify_all();
- },
- &active_count, &cv_mutex, &cv, i, &pbp, width, height,
- filter_size, &filter_out);
- t.detach();
-
- std::unique_lock lock(cv_mutex);
- while(active_count >= threads) {
- cv.wait_for(lock, std::chrono::seconds(1));
- }
- }
- }
- std::unique_lock lock(cv_mutex);
- while(active_count > 0) {
- cv.wait_for(lock, std::chrono::seconds(1));
- }
+ int active_count = 0;
+ std::mutex cv_mutex;
+ std::condition_variable cv;
+ if (precomputed) {
+ for (int i = 0; i < count; ++i) {
+ {
+ std::unique_lock lock(cv_mutex);
+ active_count += 1;
}
-
- }
-
- inline std::pair<int, int> filter_minmax(const std::vector<float> &filter,
- std::vector<bool> pbp) {
- // ensure minority pixel is "true"
- unsigned int count = 0;
- for (bool value : pbp) {
- if(value) {
- ++count;
- }
+ std::thread t(
+ [](int *ac, std::mutex *cvm, std::condition_variable *cv, int i,
+ const std::vector<bool> *pbp, int width, int height,
+ int filter_size, std::vector<float> *fout,
+ const std::vector<float> *precomputed) {
+ int x, y;
+ std::tie(x, y) = utility::oneToTwo(i, width);
+ (*fout)[i] = internal::filter_with_precomputed(
+ *pbp, x, y, width, height, filter_size, *precomputed);
+ std::unique_lock lock(*cvm);
+ *ac -= 1;
+ cv->notify_all();
+ },
+ &active_count, &cv_mutex, &cv, i, &pbp, width, height, filter_size,
+ &filter_out, precomputed);
+ t.detach();
+
+ std::unique_lock lock(cv_mutex);
+ while (active_count >= threads) {
+ cv.wait_for(lock, std::chrono::seconds(1));
}
- if (count * 2 >= pbp.size()) {
- //std::cout << "MINMAX flip\n"; // DEBUG
- for (unsigned int i = 0; i < pbp.size(); ++i) {
- pbp[i] = !pbp[i];
- }
+ }
+ } else {
+ for (int i = 0; i < count; ++i) {
+ {
+ std::unique_lock lock(cv_mutex);
+ active_count += 1;
}
-
- float min = std::numeric_limits<float>::infinity();
- float max = -std::numeric_limits<float>::infinity();
- int min_index = -1;
- int max_index = -1;
-
- for(std::vector<float>::size_type i = 0; i < filter.size(); ++i) {
- if(!pbp[i] && filter[i] < min) {
- min_index = i;
- min = filter[i];
- }
- if(pbp[i] && filter[i] > max) {
- max_index = i;
- max = filter[i];
- }
+ std::thread t(
+ [](int *ac, std::mutex *cvm, std::condition_variable *cv, int i,
+ const std::vector<bool> *pbp, int width, int height,
+ int filter_size, std::vector<float> *fout) {
+ int x, y;
+ std::tie(x, y) = utility::oneToTwo(i, width);
+ (*fout)[i] =
+ internal::filter(*pbp, x, y, width, height, filter_size);
+ std::unique_lock lock(*cvm);
+ *ac -= 1;
+ cv->notify_all();
+ },
+ &active_count, &cv_mutex, &cv, i, &pbp, width, height, filter_size,
+ &filter_out);
+ t.detach();
+
+ std::unique_lock lock(cv_mutex);
+ while (active_count >= threads) {
+ cv.wait_for(lock, std::chrono::seconds(1));
}
-
- return {min_index, max_index};
+ }
}
-
- inline std::pair<int, int> filter_abs_minmax(
- const std::vector<float> &filter) {
- float min = std::numeric_limits<float>::infinity();
- float max = -std::numeric_limits<float>::infinity();
- int min_index = -1;
- int max_index = -1;
-
- std::default_random_engine re(std::random_device{}());
- std::size_t startIdx = std::uniform_int_distribution<std::size_t>(0, filter.size() - 1)(re);
-
- for(std::vector<float>::size_type i = startIdx; i < filter.size(); ++i) {
- if(filter[i] < min) {
- min_index = i;
- min = filter[i];
- }
- if(filter[i] > max) {
- max_index = i;
- max = filter[i];
- }
- }
- for(std::vector<float>::size_type i = 0; i < startIdx; ++i) {
- if(filter[i] < min) {
- min_index = i;
- min = filter[i];
- }
- if(filter[i] > max) {
- max_index = i;
- max = filter[i];
- }
- }
-
- return {min_index, max_index};
+ std::unique_lock lock(cv_mutex);
+ while (active_count > 0) {
+ cv.wait_for(lock, std::chrono::seconds(1));
}
-
- inline int get_one_or_zero(
- const std::vector<bool>& pbp, bool get_one,
- int idx, int width, int height) {
- std::queue<int> checking_indices;
-
- auto xy = utility::oneToTwo(idx, width);
- int count = 0;
- int loops = 0;
- enum { D_DOWN = 0, D_LEFT = 1, D_UP = 2, D_RIGHT = 3 } dir = D_RIGHT;
- int next;
-
- while(true) {
- if(count == 0) {
- switch(dir) {
- case D_RIGHT:
- xy.first = (xy.first + 1) % width;
- ++loops;
- count = loops * 2 - 1;
- dir = D_DOWN;
- break;
- case D_DOWN:
- xy.first = (xy.first + width - 1) % width;
- count = loops * 2 - 1;
- dir = D_LEFT;
- break;
- case D_LEFT:
- xy.second = (xy.second + height - 1) % height;
- count = loops * 2 - 1;
- dir = D_UP;
- break;
- case D_UP:
- xy.first = (xy.first + 1) % width;
- count = loops * 2 - 1;
- dir = D_RIGHT;
- break;
- }
- } else {
- switch(dir) {
- case D_DOWN:
- xy.second = (xy.second + 1) % height;
- --count;
- break;
- case D_LEFT:
- xy.first = (xy.first + width - 1) % width;
- --count;
- break;
- case D_UP:
- xy.second = (xy.second + height - 1) % height;
- --count;
- break;
- case D_RIGHT:
- xy.first = (xy.first + 1) % width;
- --count;
- break;
- }
- }
- next = utility::twoToOne(xy.first, xy.second, width, height);
- if((get_one && pbp[next]) || (!get_one && !pbp[next])) {
- return next;
- }
- }
- return idx;
+ }
+}
+
+inline std::pair<int, int> filter_minmax(const std::vector<float> &filter,
+ std::vector<bool> pbp) {
+ // ensure minority pixel is "true"
+ unsigned int count = 0;
+ for (bool value : pbp) {
+ if (value) {
+ ++count;
}
-
- inline void write_filter(const std::vector<float> &filter, int width, const char *filename) {
- int min, max;
- std::tie(min, max) = filter_abs_minmax(filter);
-
- printf("Writing to %s, min is %.3f, max is %.3f\n", filename, filter[min], filter[max]);
- FILE *filter_image = fopen(filename, "w");
- fprintf(filter_image, "P2\n%d %d\n255\n", width, (int)filter.size() / width);
- for(std::vector<float>::size_type i = 0; i < filter.size(); ++i) {
- fprintf(filter_image, "%d ",
- (int)(((filter[i] - filter[min])
- / (filter[max] - filter[min]))
- * 255.0f));
- if((i + 1) % width == 0) {
- fputc('\n', filter_image);
- }
- }
- fclose(filter_image);
+ }
+ if (count * 2 >= pbp.size()) {
+ // std::cout << "MINMAX flip\n"; // DEBUG
+ for (unsigned int i = 0; i < pbp.size(); ++i) {
+ pbp[i] = !pbp[i];
}
+ }
- inline image::Bl toBl(const std::vector<bool>& pbp, int width) {
- image::Bl bwImage(width, pbp.size() / width);
- assert((unsigned long)bwImage.getSize() >= pbp.size()
- && "New image::Bl size too small (pbp's size is not a multiple of width)");
-
- for(unsigned int i = 0; i < pbp.size(); ++i) {
- bwImage.getData()[i] = pbp[i] ? 255 : 0;
- }
+ float min = std::numeric_limits<float>::infinity();
+ float max = -std::numeric_limits<float>::infinity();
+ int min_index = -1;
+ int max_index = -1;
- return bwImage;
+ for (std::vector<float>::size_type i = 0; i < filter.size(); ++i) {
+ if (!pbp[i] && filter[i] < min) {
+ min_index = i;
+ min = filter[i];
}
+ if (pbp[i] && filter[i] > max) {
+ max_index = i;
+ max = filter[i];
+ }
+ }
- inline image::Bl rangeToBl(const std::vector<unsigned int> &values, int width) {
- int min = std::numeric_limits<int>::max();
- int max = std::numeric_limits<int>::min();
-
- for (int value : values) {
- if (value < min) {
- min = value;
- }
- if (value > max) {
- max = value;
- }
- }
-
-#ifndef NDEBUG
- std::cout << "rangeToBl: Got min == " << min << " and max == " << max << std::endl;
-#endif
-
- max -= min;
+ return {min_index, max_index};
+}
- image::Bl grImage(width, values.size() / width);
- assert((unsigned long)grImage.getSize() >= values.size()
- && "New image::Bl size too small (values' size is not a multiple of width)");
+inline std::pair<int, int> filter_abs_minmax(const std::vector<float> &filter) {
+ float min = std::numeric_limits<float>::infinity();
+ float max = -std::numeric_limits<float>::infinity();
+ int min_index = -1;
+ int max_index = -1;
- for(unsigned int i = 0; i < values.size(); ++i) {
- grImage.getData()[i] = std::round(((float)((int)(values[i]) - min) / (float)max) * 255.0F);
- }
+ std::default_random_engine re(std::random_device{}());
+ std::size_t startIdx =
+ std::uniform_int_distribution<std::size_t>(0, filter.size() - 1)(re);
- return grImage;
+ for (std::vector<float>::size_type i = startIdx; i < filter.size(); ++i) {
+ if (filter[i] < min) {
+ min_index = i;
+ min = filter[i];
+ }
+ if (filter[i] > max) {
+ max_index = i;
+ max = filter[i];
+ }
+ }
+ for (std::vector<float>::size_type i = 0; i < startIdx; ++i) {
+ if (filter[i] < min) {
+ min_index = i;
+ min = filter[i];
+ }
+ if (filter[i] > max) {
+ max_index = i;
+ max = filter[i];
}
+ }
+
+ return {min_index, max_index};
+}
+
+inline int get_one_or_zero(const std::vector<bool> &pbp, bool get_one, int idx,
+ int width, int height) {
+ std::queue<int> checking_indices;
+
+ auto xy = utility::oneToTwo(idx, width);
+ int count = 0;
+ int loops = 0;
+ enum { D_DOWN = 0, D_LEFT = 1, D_UP = 2, D_RIGHT = 3 } dir = D_RIGHT;
+ int next;
+
+ while (true) {
+ if (count == 0) {
+ switch (dir) {
+ case D_RIGHT:
+ xy.first = (xy.first + 1) % width;
+ ++loops;
+ count = loops * 2 - 1;
+ dir = D_DOWN;
+ break;
+ case D_DOWN:
+ xy.first = (xy.first + width - 1) % width;
+ count = loops * 2 - 1;
+ dir = D_LEFT;
+ break;
+ case D_LEFT:
+ xy.second = (xy.second + height - 1) % height;
+ count = loops * 2 - 1;
+ dir = D_UP;
+ break;
+ case D_UP:
+ xy.first = (xy.first + 1) % width;
+ count = loops * 2 - 1;
+ dir = D_RIGHT;
+ break;
+ }
+ } else {
+ switch (dir) {
+ case D_DOWN:
+ xy.second = (xy.second + 1) % height;
+ --count;
+ break;
+ case D_LEFT:
+ xy.first = (xy.first + width - 1) % width;
+ --count;
+ break;
+ case D_UP:
+ xy.second = (xy.second + height - 1) % height;
+ --count;
+ break;
+ case D_RIGHT:
+ xy.first = (xy.first + 1) % width;
+ --count;
+ break;
+ }
+ }
+ next = utility::twoToOne(xy.first, xy.second, width, height);
+ if ((get_one && pbp[next]) || (!get_one && !pbp[next])) {
+ return next;
+ }
+ }
+ return idx;
+}
+
+inline void write_filter(const std::vector<float> &filter, int width,
+ const char *filename) {
+ int min, max;
+ std::tie(min, max) = filter_abs_minmax(filter);
+
+ printf("Writing to %s, min is %.3f, max is %.3f\n", filename, filter[min],
+ filter[max]);
+ FILE *filter_image = fopen(filename, "w");
+ fprintf(filter_image, "P2\n%d %d\n255\n", width, (int)filter.size() / width);
+ for (std::vector<float>::size_type i = 0; i < filter.size(); ++i) {
+ fprintf(filter_image, "%d ",
+ (int)(((filter[i] - filter[min]) / (filter[max] - filter[min])) *
+ 255.0f));
+ if ((i + 1) % width == 0) {
+ fputc('\n', filter_image);
+ }
+ }
+ fclose(filter_image);
+}
+
+inline image::Bl toBl(const std::vector<bool> &pbp, int width) {
+ image::Bl bwImage(width, pbp.size() / width);
+ assert((unsigned long)bwImage.getSize() >= pbp.size() &&
+ "New image::Bl size too small (pbp's size is not a multiple of "
+ "width)");
+
+ for (unsigned int i = 0; i < pbp.size(); ++i) {
+ bwImage.getData()[i] = pbp[i] ? 255 : 0;
+ }
+
+ return bwImage;
+}
+
+inline image::Bl rangeToBl(const std::vector<unsigned int> &values, int width) {
+ int min = std::numeric_limits<int>::max();
+ int max = std::numeric_limits<int>::min();
+
+ for (int value : values) {
+ if (value < min) {
+ min = value;
+ }
+ if (value > max) {
+ max = value;
+ }
+ }
- inline std::pair<int, int> filter_minmax_in_range(int start, int width,
- int height,
- int range,
- const std::vector<float> &vec) {
- float max = -std::numeric_limits<float>::infinity();
- float min = std::numeric_limits<float>::infinity();
-
- int maxIdx = -1;
- int minIdx = -1;
-
- auto startXY = utility::oneToTwo(start, width);
- for(int y = startXY.second - range / 2; y <= startXY.second + range / 2; ++y) {
- for(int x = startXY.first - range / 2; x <= startXY.first + range / 2; ++x) {
- int idx = utility::twoToOne(x, y, width, height);
- if(idx == start) {
- continue;
- }
-
- if(vec[idx] < min) {
- min = vec[idx];
- minIdx = idx;
- }
-
- if(vec[idx] > max) {
- max = vec[idx];
- maxIdx = idx;
- }
- }
- }
+#ifndef NDEBUG
+ std::cout << "rangeToBl: Got min == " << min << " and max == " << max
+ << std::endl;
+#endif
- if(minIdx < 0) {
- throw std::runtime_error("Invalid minIdx value");
- } else if(maxIdx < 0) {
- throw std::runtime_error("Invalid maxIdx value");
- }
- return {minIdx, maxIdx};
+ max -= min;
+
+ image::Bl grImage(width, values.size() / width);
+ assert((unsigned long)grImage.getSize() >= values.size() &&
+ "New image::Bl size too small (values' size is not a multiple of "
+ "width)");
+
+ for (unsigned int i = 0; i < values.size(); ++i) {
+ grImage.getData()[i] =
+ std::round(((float)((int)(values[i]) - min) / (float)max) * 255.0F);
+ }
+
+ return grImage;
+}
+
+inline std::pair<int, int> filter_minmax_in_range(
+ int start, int width, int height, int range,
+ const std::vector<float> &vec) {
+ float max = -std::numeric_limits<float>::infinity();
+ float min = std::numeric_limits<float>::infinity();
+
+ int maxIdx = -1;
+ int minIdx = -1;
+
+ auto startXY = utility::oneToTwo(start, width);
+ for (int y = startXY.second - range / 2; y <= startXY.second + range / 2;
+ ++y) {
+ for (int x = startXY.first - range / 2; x <= startXY.first + range / 2;
+ ++x) {
+ int idx = utility::twoToOne(x, y, width, height);
+ if (idx == start) {
+ continue;
+ }
+
+ if (vec[idx] < min) {
+ min = vec[idx];
+ minIdx = idx;
+ }
+
+ if (vec[idx] > max) {
+ max = vec[idx];
+ maxIdx = idx;
+ }
}
-} // namespace dither::internal
-
-} // namespace dither
+ }
+
+ if (minIdx < 0) {
+ throw std::runtime_error("Invalid minIdx value");
+ } else if (maxIdx < 0) {
+ throw std::runtime_error("Invalid maxIdx value");
+ }
+ return {minIdx, maxIdx};
+}
+} // namespace internal
+
+} // namespace dither
#endif
#include "image.hpp"
+#include <png.h>
+
#include <cstdio>
-#include <random>
#include <iostream>
-
-#include <png.h>
+#include <random>
bool image::Base::isValid() const {
- return getWidth() > 0 && getHeight() > 0 && getSize() > 0;
+ return getWidth() > 0 && getHeight() > 0 && getSize() > 0;
}
-image::Bl::Bl() :
-data(),
-width(0),
-height(0)
-{}
-
-image::Bl::Bl(int width, int height) :
-data(width * height),
-width(width),
-height(height)
-{}
-
-image::Bl::Bl(const std::vector<uint8_t> &data, int width) :
-data(data),
-width(width),
-height(data.size() / width)
-{}
-
-image::Bl::Bl(std::vector<uint8_t> &&data, int width) :
-data(std::move(data)),
-width(width),
-height(data.size() / width)
-{}
-
-image::Bl::Bl(const std::vector<float> &data, int width) :
- data{},
- width(width),
- height(data.size() / width)
-{
- for(float gspixel : data) {
- this->data.push_back(static_cast<uint8_t>(255.0F * gspixel));
- }
-}
+image::Bl::Bl() : data(), width(0), height(0) {}
-void image::Bl::randomize() {
- if(!isValid()) {
- return;
- }
+image::Bl::Bl(int width, int height)
+ : data(width * height), width(width), height(height) {}
- std::default_random_engine re(std::random_device{}());
- std::uniform_int_distribution<unsigned int> dist;
+image::Bl::Bl(const std::vector<uint8_t> &data, int width)
+ : data(data), width(width), height(data.size() / width) {}
- for(unsigned int i = 0; i < data.size(); ++i) {
- data[i] = i < data.size() / 2 ? 255 : 0;
- }
+image::Bl::Bl(std::vector<uint8_t> &&data, int width)
+ : data(std::move(data)), width(width), height(data.size() / width) {}
- for(unsigned int i = 0; i < data.size() - 1; ++i) {
- int ridx = dist(re, decltype(dist)::param_type{i+1, (unsigned int)data.size()-1});
- uint8_t temp = data[i];
- data[i] = data[ridx];
- data[ridx] = temp;
- }
+image::Bl::Bl(const std::vector<float> &data, int width)
+ : data{}, width(width), height(data.size() / width) {
+ for (float gspixel : data) {
+ this->data.push_back(static_cast<uint8_t>(255.0F * gspixel));
+ }
}
-unsigned int image::Bl::getSize() const {
- return data.size();
+void image::Bl::randomize() {
+ if (!isValid()) {
+ return;
+ }
+
+ std::default_random_engine re(std::random_device{}());
+ std::uniform_int_distribution<unsigned int> dist;
+
+ for (unsigned int i = 0; i < data.size(); ++i) {
+ data[i] = i < data.size() / 2 ? 255 : 0;
+ }
+
+ for (unsigned int i = 0; i < data.size() - 1; ++i) {
+ int ridx = dist(
+ re, decltype(dist)::param_type{i + 1, (unsigned int)data.size() - 1});
+ uint8_t temp = data[i];
+ data[i] = data[ridx];
+ data[ridx] = temp;
+ }
}
-uint8_t* image::Bl::getData() {
- if(!isValid()) {
- return nullptr;
- }
- return &data[0];
-}
+unsigned int image::Bl::getSize() const { return data.size(); }
-const uint8_t* image::Bl::getDataC() const {
- if(!isValid()) {
- return nullptr;
- }
- return &data[0];
+uint8_t *image::Bl::getData() {
+ if (!isValid()) {
+ return nullptr;
+ }
+ return &data[0];
}
-unsigned int image::Bl::getWidth() const {
- return width;
+const uint8_t *image::Bl::getDataC() const {
+ if (!isValid()) {
+ return nullptr;
+ }
+ return &data[0];
}
-unsigned int image::Bl::getHeight() const {
- return height;
-}
+unsigned int image::Bl::getWidth() const { return width; }
+
+unsigned int image::Bl::getHeight() const { return height; }
bool image::Bl::canWriteFile(file_type type) {
- if(!isValid()) {
- std::cout << "Cannot write image because isValid() is false\n";
- return false;
- }
- switch(type) {
+ if (!isValid()) {
+ std::cout << "Cannot write image because isValid() is false\n";
+ return false;
+ }
+ switch (type) {
case file_type::PBM:
case file_type::PGM:
case file_type::PPM:
case file_type::PNG:
- return true;
+ return true;
default:
- std::cout << "Cannot write image because received invalid file_type\n";
- return false;
- }
+ std::cout << "Cannot write image because received invalid "
+ "file_type\n";
+ return false;
+ }
}
-bool image::Bl::writeToFile(file_type type, bool canOverwrite, const char *filename) {
- if(!isValid() || !canWriteFile(type)) {
- std::cout << "ERROR: Image is not valid or cannot write file type\n";
- return false;
+bool image::Bl::writeToFile(file_type type, bool canOverwrite,
+ const char *filename) {
+ if (!isValid() || !canWriteFile(type)) {
+ std::cout << "ERROR: Image is not valid or cannot write file type\n";
+ return false;
+ }
+
+ FILE *file = fopen(filename, "r");
+ if (file && !canOverwrite) {
+ fclose(file);
+ std::cout << "ERROR: Will not overwite existing file \"" << filename << "\""
+ << std::endl;
+ return false;
+ }
+
+ if (file) {
+ fclose(file);
+ }
+
+ if (type == file_type::PNG) {
+ FILE *outfile = fopen(filename, "wb");
+ if (outfile == nullptr) {
+ std::cout << "ERROR: Failed to open file for writing (png)\n";
+ return false;
+ }
+ const static auto pngErrorLFn = [](png_structp /* unused */,
+ png_const_charp message) {
+ std::cerr << "WARNING [libpng]: " << message << std::endl;
+ };
+ const static auto pngWarnLFn = [](png_structp /* unused */,
+ png_const_charp message) {
+ std::cerr << "ERROR [libpng]: " << message << std::endl;
+ };
+
+ png_structp png_ptr = png_create_write_struct(
+ PNG_LIBPNG_VER_STRING, nullptr, pngErrorLFn, pngWarnLFn);
+
+ if (png_ptr == nullptr) {
+ fclose(outfile);
+ std::cout << "ERROR: Failed to set up writing png file (png_ptr)\n";
+ return false;
}
- FILE *file = fopen(filename, "r");
- if(file && !canOverwrite) {
- fclose(file);
- std::cout << "ERROR: Will not overwite existing file \"" << filename
- << "\"" << std::endl;
- return false;
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == nullptr) {
+ png_destroy_write_struct(&png_ptr, nullptr);
+ fclose(outfile);
+ std::cout << "ERROR: Failed to set up writing png file (png_infop)\n";
+ return false;
}
- if(file) {
- fclose(file);
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ fclose(outfile);
+ std::cout << "ERROR: Failed to write image file (png error)\n";
+ return false;
}
- if(type == file_type::PNG) {
- FILE *outfile = fopen(filename, "wb");
- if (outfile == nullptr) {
- std::cout << "ERROR: Failed to open file for writing (png)\n";
- return false;
- }
- const static auto pngErrorLFn = [] (png_structp /* unused */,
- png_const_charp message) {
- std::cerr << "WARNING [libpng]: " << message << std::endl;
- };
- const static auto pngWarnLFn = [] (png_structp /* unused */,
- png_const_charp message) {
- std::cerr << "ERROR [libpng]: " << message << std::endl;
- };
-
- png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
- nullptr,
- pngErrorLFn,
- pngWarnLFn);
-
- if (png_ptr == nullptr) {
- fclose(outfile);
- std::cout << "ERROR: Failed to set up writing png file (png_ptr)\n";
- return false;
- }
-
- png_infop info_ptr = png_create_info_struct(png_ptr);
- if (info_ptr == nullptr) {
- png_destroy_write_struct(&png_ptr, nullptr);
- fclose(outfile);
- std::cout << "ERROR: Failed to set up writing png file (png_infop)\n";
- return false;
- }
-
- if (setjmp(png_jmpbuf(png_ptr))) {
- png_destroy_write_struct(&png_ptr, &info_ptr);
- fclose(outfile);
- std::cout << "ERROR: Failed to write image file (png error)\n";
- return false;
- }
-
- png_init_io(png_ptr, outfile);
-
- png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_GRAY,
- PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
- PNG_FILTER_TYPE_DEFAULT);
-
- png_write_info(png_ptr, info_ptr);
-
- //png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
-
- for (unsigned int j = 0; j < this->data.size() / this->width; ++j) {
- unsigned char *dataPtr = &this->data.at(j * this->width);
- png_write_rows(png_ptr, &dataPtr, 1);
- }
-
- png_write_end(png_ptr, nullptr);
-
- png_destroy_write_struct(&png_ptr, &info_ptr);
-
- fclose(outfile);
- return true;
+ png_init_io(png_ptr, outfile);
+
+ png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_GRAY,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT);
+
+ png_write_info(png_ptr, info_ptr);
+
+ // png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
+
+ for (unsigned int j = 0; j < this->data.size() / this->width; ++j) {
+ unsigned char *dataPtr = &this->data.at(j * this->width);
+ png_write_rows(png_ptr, &dataPtr, 1);
}
- switch(type) {
+ png_write_end(png_ptr, nullptr);
+
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+
+ fclose(outfile);
+ return true;
+ }
+
+ switch (type) {
case file_type::PBM:
- file = fopen(filename, "w");
- fprintf(file, "P1\n%d %d", width, height);
- break;
+ file = fopen(filename, "w");
+ fprintf(file, "P1\n%d %d", width, height);
+ break;
case file_type::PGM:
- file = fopen(filename, "wb");
- fprintf(file, "P5\n%d %d\n255\n", width, height);
- break;
+ file = fopen(filename, "wb");
+ fprintf(file, "P5\n%d %d\n255\n", width, height);
+ break;
case file_type::PPM:
- file = fopen(filename, "wb");
- fprintf(file, "P6\n%d %d\n255\n", width, height);
- break;
+ file = fopen(filename, "wb");
+ fprintf(file, "P6\n%d %d\n255\n", width, height);
+ break;
default:
+ fclose(file);
+ std::cout << "ERROR: Cannot write image file, invalid type\n";
+ return false;
+ }
+ for (unsigned int i = 0; i < data.size(); ++i) {
+ if (type == file_type::PBM && i % width == 0) {
+ fprintf(file, "\n");
+ }
+ switch (type) {
+ case file_type::PBM:
+ fprintf(file, "%d ", data[i] == 0 ? 0 : 1);
+ break;
+ case file_type::PGM:
+ // fprintf(file, "%c ", data[i]);
+ fputc(data[i], file);
+ break;
+ case file_type::PPM:
+ // fprintf(file, "%c %c %c ", data[i], data[i], data[i]);
+ fputc(data[i], file);
+ fputc(data[i], file);
+ fputc(data[i], file);
+ break;
+ default:
fclose(file);
std::cout << "ERROR: Cannot write image file, invalid type\n";
return false;
}
- for(unsigned int i = 0; i < data.size(); ++i) {
- if(type == file_type::PBM && i % width == 0) {
- fprintf(file, "\n");
- }
- switch(type) {
- case file_type::PBM:
- fprintf(file, "%d ", data[i] == 0 ? 0 : 1);
- break;
- case file_type::PGM:
- //fprintf(file, "%c ", data[i]);
- fputc(data[i], file);
- break;
- case file_type::PPM:
- //fprintf(file, "%c %c %c ", data[i], data[i], data[i]);
- fputc(data[i], file);
- fputc(data[i], file);
- fputc(data[i], file);
- break;
- default:
- fclose(file);
- std::cout << "ERROR: Cannot write image file, invalid type\n";
- return false;
- }
- }
+ }
- fclose(file);
- return true;
+ fclose(file);
+ return true;
}
-bool image::Bl::writeToFile(file_type type, bool canOverwrite, const std::string &filename) {
- return writeToFile(type, canOverwrite, filename.c_str());
+bool image::Bl::writeToFile(file_type type, bool canOverwrite,
+ const std::string &filename) {
+ return writeToFile(type, canOverwrite, filename.c_str());
}