现在的位置: 首页 > 综合 > 正文

KMean clustering算法获取图片主色调

2013年01月24日 ⁄ 综合 ⁄ 共 5742字 ⁄ 字号 评论关闭

转自 http://www.kaixinwenda.com/article-lisztlee-8460245.html

前几天在研究chromium代码的时候看到了一个取PNG图片主色调(dominant color)的算法,这个算法不是取图片中所有点的平均RGB值,也不是取同一RGB值最多的点的RGB。chromium中取图片主色调用的算法用的是 KMean clustering。可以算是KMean clustering的一个实际应用,chromium自己将其取名为RGB KMean Algorithm。

     首先我们可以来简单了解一下KMean clustering算法(如果你已经足够熟悉,这里可以略过)。酷壳中有一篇K-Means 算法的文章专门介绍过这个算法,我最初对这个算法的了解也源于此,讲的也挺详细。如果你觉得这个还不能满足你的需求,可以再继续看看维基k-means clustering。

     KMean clustering算法的基本思想就是:

          1、随机在图中取K个种子点。
          2、然后对图中的所有点求到这K个种子点的距离,假如点Pi离种子点Si最近,那么Pi属于Si点群。

          3、接下来,我们要移动种子点到属于他的“点群”的中心。

          4、然后重复第2)和第3)步,直到,种子点没有移动。


     接下来我们再来了解一下chromium是如何利用KMean clustering算法来算出图片主色调的。

     之前我们了解到的KMean clustering算法是从坐标空间来计算的。而主色调算法RGB KMean Algorithm则需要从RGB空间来计算的。总体思想还是一至,就是将坐标空间向RGB空间做了一个转换。

     RGB KMean Algorithm的基本执行步骤如下:

          1、定义一个迭代次数上限M。

          2、随机在图中找N个点,取出它的RGB值作为种子点。
          3、然后对图中的每个点找到一个RGB值和它最相近的种子点,并将这个点加到RGB值最相近得种子点所在点群中。假如点Pi离种子点Si的RGB值最相 近,那么Pi属于Si点群。(我们为每个点群记录一个累加总值以及一个计数器,每加入一个点时我们都要更新这两个值)。

          4、计算种子点群的平均RGB值(累加总值/点的个数),并将这个RGB值作为新的种子点。

          5、比较这个新的值是否和旧值是否相等。

           a) 如果相等,则种子点收敛完成,进入第6步。

           b) 如果不等,则继续执行第2步,直至迭代次数达到M次。

          6、当种子点收敛完成或者迭代次数达到M次,我们对所有种子点的权重做一个排序(权重即其中包含点的个数)。

          7、取出权重最高的种子点的值,这个值即我们所需的图片的主色调。


     chromium中的代码设计的比较多,我会把其中主要的代码贴出来,其中还涉及到了其他的一些实现,可以直接到chromium的代码color_analysis.h,color_analysis.cc中查看(找到CalculateKMeanColorOfPNG方法的实现即可)。如果还想搜其他的代码,你可以根据自己的需求在google codesearch中查找。

  

     chromium中实现代码如下:

  1. <span style="font-family:SimSun;font-size:18px;">// For a 16x16 icon on an Intel Core i5 this function takes approximately  
  2. // 0.5 ms to run.  
  3. // TODO(port): This code assumes the CPU architecture is little-endian.  
  4. SkColor CalculateKMeanColorOfBuffer(uint8_t* decoded_data,  
  5.                                     int img_width,  
  6.                                     int img_height,  
  7.                                     uint32_t darkness_limit,  
  8.                                     uint32_t brightness_limit,  
  9.                                     KMeanImageSampler* sampler) {  
  10.   SkColor color = kDefaultBgColor;  
  11.   if (img_width > 0 && img_height > 0) {  
  12.     std::vector<KMeanCluster> clusters;  
  13.     clusters.resize(kNumberOfClusters, KMeanCluster());  
  14.   
  15.     // Pick a starting point for each cluster  
  16.     std::vector<KMeanCluster>::iterator cluster = clusters.begin();  
  17.     while (cluster != clusters.end()) {  
  18.       // Try up to 10 times to find a unique color. If no unique color can be  
  19.       // found, destroy this cluster.  
  20.       bool color_unique = false;  
  21.       for (int i = 0; i < 10; ++i) {  
  22.         int pixel_pos = sampler->GetSample(img_width, img_height) %  
  23.             (img_width * img_height);  
  24.   
  25.         uint8_t b = decoded_data[pixel_pos * 4];  
  26.         uint8_t g = decoded_data[pixel_pos * 4 + 1];  
  27.         uint8_t r = decoded_data[pixel_pos * 4 + 2];  
  28.         uint8_t a = decoded_data[pixel_pos * 4 + 3];  
  29.         // Skip fully transparent pixels as they usually contain black in their  
  30.         // RGB channels but do not contribute to the visual image.  
  31.         if (a == 0)  
  32.           continue;  
  33.   
  34.         // Loop through the previous clusters and check to see if we have seen  
  35.         // this color before.  
  36.         color_unique = true;  
  37.         for (std::vector<KMeanCluster>::iterator  
  38.             cluster_check = clusters.begin();  
  39.             cluster_check != cluster; ++cluster_check) {  
  40.           if (cluster_check->IsAtCentroid(r, g, b)) {  
  41.             color_unique = false;  
  42.             break;  
  43.           }  
  44.         }  
  45.   
  46.         // If we have a unique color set the center of the cluster to  
  47.         // that color.  
  48.         if (color_unique) {  
  49.           cluster->SetCentroid(r, g, b);  
  50.           break;  
  51.         }  
  52.       }  
  53.   
  54.       // If we don't have a unique color erase this cluster.  
  55.       if (!color_unique) {  
  56.         cluster = clusters.erase(cluster);  
  57.       } else {  
  58.         // Have to increment the iterator here, otherwise the increment in the  
  59.         // for loop will skip a cluster due to the erase if the color wasn't  
  60.         // unique.  
  61.         ++cluster;  
  62.       }  
  63.     }  
  64.   
  65.     // If all pixels in the image are transparent we will have no clusters.  
  66.     if (clusters.empty())  
  67.       return color;  
  68.   
  69.     bool convergence = false;  
  70.     for (int iteration = 0;  
  71.         iteration < kNumberOfIterations && !convergence;  
  72.         ++iteration) {  
  73.   
  74.       // Loop through each pixel so we can place it in the appropriate cluster.  
  75.       uint8_t* pixel = decoded_data;  
  76.       uint8_t* decoded_data_end = decoded_data + (img_width * img_height * 4);  
  77.       while (pixel < decoded_data_end) {  
  78.         uint8_t b = *(pixel++);  
  79.         uint8_t g = *(pixel++);  
  80.         uint8_t r = *(pixel++);  
  81.         uint8_t a = *(pixel++);  
  82.         // Skip transparent pixels, see above.  
  83.         if (a == 0)  
  84.           continue;  
  85.   
  86.         uint32_t distance_sqr_to_closest_cluster = UINT_MAX;  
  87.         std::vector<KMeanCluster>::iterator closest_cluster = clusters.begin();  
  88.   
  89.         // Figure out which cluster this color is closest to in RGB space.  
  90.         for (std::vector<KMeanCluster>::iterator cluster = clusters.begin();  
  91.             cluster != clusters.end(); ++cluster) {  
  92.           uint32_t distance_sqr = cluster->GetDistanceSqr(r, g, b);  
  93.   
  94.           if (distance_sqr < distance_sqr_to_closest_cluster) {  
  95.             distance_sqr_to_closest_cluster = distance_sqr;  
  96.             closest_cluster = cluster;  
  97.           }  
  98.         }  
  99.   
  100.         closest_cluster->AddPoint(r, g, b);  
  101.       }  
  102.   
  103.       // Calculate the new cluster centers and see if we've converged or not.  
  104.       convergence = true;  
  105.       for (std::vector<KMeanCluster>::iterator cluster = clusters.begin();  
  106.           cluster != clusters.end(); ++cluster) {  
  107.         convergence &= cluster->CompareCentroidWithAggregate();  
  108.   
  109.       

抱歉!评论已关闭.