k_means.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. //---------------------------------------------------------------------------//
  2. // Copyright (c) 2013-2014 Kyle Lutz <kyle.r.lutz@gmail.com>
  3. //
  4. // Distributed under the Boost Software License, Version 1.0
  5. // See accompanying file LICENSE_1_0.txt or copy at
  6. // http://www.boost.org/LICENSE_1_0.txt
  7. //
  8. // See http://boostorg.github.com/compute for more information.
  9. //---------------------------------------------------------------------------//
  10. #include <opencv2/core/core.hpp>
  11. #include <opencv2/highgui/highgui.hpp>
  12. #include <opencv2/imgproc/imgproc.hpp>
  13. #include <boost/compute/system.hpp>
  14. #include <boost/compute/container/vector.hpp>
  15. #include <boost/compute/image/image2d.hpp>
  16. #include <boost/compute/interop/opencv/core.hpp>
  17. #include <boost/compute/interop/opencv/highgui.hpp>
  18. #include <boost/compute/random/default_random_engine.hpp>
  19. #include <boost/compute/random/uniform_real_distribution.hpp>
  20. #include <boost/compute/utility/dim.hpp>
  21. #include <boost/compute/utility/source.hpp>
  22. namespace compute = boost::compute;
  23. using compute::dim;
  24. using compute::int_;
  25. using compute::float_;
  26. using compute::float2_;
  27. // the k-means example implements the k-means clustering algorithm
  28. int main()
  29. {
  30. // number of clusters
  31. size_t k = 6;
  32. // number of points
  33. size_t n_points = 4500;
  34. // height and width of image
  35. size_t height = 800;
  36. size_t width = 800;
  37. // get default device and setup context
  38. compute::device gpu = compute::system::default_device();
  39. compute::context context(gpu);
  40. compute::command_queue queue(context, gpu);
  41. // generate random, uniformily-distributed points
  42. compute::default_random_engine random_engine(queue);
  43. compute::uniform_real_distribution<float_> uniform_distribution(0, 800);
  44. compute::vector<float2_> points(n_points, context);
  45. uniform_distribution.generate(
  46. compute::make_buffer_iterator<float_>(points.get_buffer(), 0),
  47. compute::make_buffer_iterator<float_>(points.get_buffer(), n_points * 2),
  48. random_engine,
  49. queue
  50. );
  51. // initialize all points to cluster 0
  52. compute::vector<int_> clusters(n_points, context);
  53. compute::fill(clusters.begin(), clusters.end(), 0, queue);
  54. // create initial means with the first k points
  55. compute::vector<float2_> means(k, context);
  56. compute::copy_n(points.begin(), k, means.begin(), queue);
  57. // k-means clustering program source
  58. const char k_means_source[] = BOOST_COMPUTE_STRINGIZE_SOURCE(
  59. __kernel void assign_clusters(__global const float2 *points,
  60. __global const float2 *means,
  61. const int k,
  62. __global int *clusters)
  63. {
  64. const uint gid = get_global_id(0);
  65. const float2 point = points[gid];
  66. // find the closest cluster
  67. float current_distance = 0;
  68. int closest_cluster = -1;
  69. // find closest cluster mean to the point
  70. for(int i = 0; i < k; i++){
  71. const float2 mean = means[i];
  72. int distance_to_mean = distance(point, mean);
  73. if(closest_cluster == -1 || distance_to_mean < current_distance){
  74. current_distance = distance_to_mean;
  75. closest_cluster = i;
  76. }
  77. }
  78. // write new cluster
  79. clusters[gid] = closest_cluster;
  80. }
  81. __kernel void update_means(__global const float2 *points,
  82. const uint n_points,
  83. __global float2 *means,
  84. __global const int *clusters)
  85. {
  86. const uint k = get_global_id(0);
  87. float2 sum = { 0, 0 };
  88. float count = 0;
  89. for(uint i = 0; i < n_points; i++){
  90. if(clusters[i] == k){
  91. sum += points[i];
  92. count += 1;
  93. }
  94. }
  95. means[k] = sum / count;
  96. }
  97. );
  98. // build the k-means program
  99. compute::program k_means_program =
  100. compute::program::build_with_source(k_means_source, context);
  101. // setup the k-means kernels
  102. compute::kernel assign_clusters_kernel(k_means_program, "assign_clusters");
  103. assign_clusters_kernel.set_arg(0, points);
  104. assign_clusters_kernel.set_arg(1, means);
  105. assign_clusters_kernel.set_arg(2, int_(k));
  106. assign_clusters_kernel.set_arg(3, clusters);
  107. compute::kernel update_means_kernel(k_means_program, "update_means");
  108. update_means_kernel.set_arg(0, points);
  109. update_means_kernel.set_arg(1, int_(n_points));
  110. update_means_kernel.set_arg(2, means);
  111. update_means_kernel.set_arg(3, clusters);
  112. // run the k-means algorithm
  113. for(int iteration = 0; iteration < 25; iteration++){
  114. queue.enqueue_1d_range_kernel(assign_clusters_kernel, 0, n_points, 0);
  115. queue.enqueue_1d_range_kernel(update_means_kernel, 0, k, 0);
  116. }
  117. // create output image
  118. compute::image2d image(
  119. context, width, height, compute::image_format(CL_RGBA, CL_UNSIGNED_INT8)
  120. );
  121. // program with two kernels, one to fill the image with white, and then
  122. // one the draw to points calculated in coordinates on the image
  123. const char draw_walk_source[] = BOOST_COMPUTE_STRINGIZE_SOURCE(
  124. __kernel void draw_points(__global const float2 *points,
  125. __global const int *clusters,
  126. __write_only image2d_t image)
  127. {
  128. const uint i = get_global_id(0);
  129. const float2 coord = points[i];
  130. // map cluster number to color
  131. uint4 color = { 0, 0, 0, 0 };
  132. switch(clusters[i]){
  133. case 0:
  134. color = (uint4)(255, 0, 0, 255);
  135. break;
  136. case 1:
  137. color = (uint4)(0, 255, 0, 255);
  138. break;
  139. case 2:
  140. color = (uint4)(0, 0, 255, 255);
  141. break;
  142. case 3:
  143. color = (uint4)(255, 255, 0, 255);
  144. break;
  145. case 4:
  146. color = (uint4)(255, 0, 255, 255);
  147. break;
  148. case 5:
  149. color = (uint4)(0, 255, 255, 255);
  150. break;
  151. }
  152. // draw a 3x3 pixel point
  153. for(int x = -1; x <= 1; x++){
  154. for(int y = -1; y <= 1; y++){
  155. if(coord.x + x > 0 && coord.x + x < get_image_width(image) &&
  156. coord.y + y > 0 && coord.y + y < get_image_height(image)){
  157. write_imageui(image, (int2)(coord.x, coord.y) + (int2)(x, y), color);
  158. }
  159. }
  160. }
  161. }
  162. __kernel void fill_gray(__write_only image2d_t image)
  163. {
  164. const int2 coord = { get_global_id(0), get_global_id(1) };
  165. if(coord.x < get_image_width(image) && coord.y < get_image_height(image)){
  166. uint4 gray = { 15, 15, 15, 15 };
  167. write_imageui(image, coord, gray);
  168. }
  169. }
  170. );
  171. // build the program
  172. compute::program draw_program =
  173. compute::program::build_with_source(draw_walk_source, context);
  174. // fill image with dark gray
  175. compute::kernel fill_kernel(draw_program, "fill_gray");
  176. fill_kernel.set_arg(0, image);
  177. queue.enqueue_nd_range_kernel(
  178. fill_kernel, dim(0, 0), dim(width, height), dim(1, 1)
  179. );
  180. // draw points colored according to cluster
  181. compute::kernel draw_kernel(draw_program, "draw_points");
  182. draw_kernel.set_arg(0, points);
  183. draw_kernel.set_arg(1, clusters);
  184. draw_kernel.set_arg(2, image);
  185. queue.enqueue_1d_range_kernel(draw_kernel, 0, n_points, 0);
  186. // show image
  187. compute::opencv_imshow("k-means", image, queue);
  188. // wait and return
  189. cv::waitKey(0);
  190. return 0;
  191. }