C++ 基于颜色查找图像中的所有对象

C++ 基于颜色查找图像中的所有对象,c++,opencv,image-processing,C++,Opencv,Image Processing,我正在寻找一种方法来拍摄图像,并通过颜色获得其中所有对象的遮罩。我的目标是能够将颜色相似的对象分割成不同的层,以便进一步检查每个层。计划是对原始图像使用每个遮罩来创建每个对象的颜色直方图,并确定与图像中其他对象的相似性。如果某个对象足够相似,它将与其他对象组合以形成一个层 问题是,我无法在opencv中找到一个函数来根据颜色连续性查找图像中的所有对象。我确信这样的算法是存在的,但它似乎在逃避我。有人知道这样的算法或函数吗?我建议您将图像转换为HSV空间(色调饱和度值)。然后根据色调值制作直方图,

我正在寻找一种方法来拍摄图像,并通过颜色获得其中所有对象的遮罩。我的目标是能够将颜色相似的对象分割成不同的层,以便进一步检查每个层。计划是对原始图像使用每个遮罩来创建每个对象的颜色直方图,并确定与图像中其他对象的相似性。如果某个对象足够相似,它将与其他对象组合以形成一个层


问题是,我无法在opencv中找到一个函数来根据颜色连续性查找图像中的所有对象。我确信这样的算法是存在的,但它似乎在逃避我。有人知道这样的算法或函数吗?

我建议您将图像转换为HSV空间(色调饱和度值)。然后根据色调值制作直方图,在线查找阈值,或者在之前定义阈值(取决于这是一般问题还是给定问题)

为每个要形成的层装入一个通道图像。(设置为黑色)

然后使用HSV图像并根据阈值标记图层。您可能还需要为值和饱和度添加一些恒定阈值(以避免暗区和亮区)


这对你有意义吗

我认为你应该按照以下步骤进行:

  • 平滑你的图像,如果它有太多的细节

  • 找边

  • 找到所有轮廓

  • 尝试找到每个轮廓的颜色。假设您希望保留所有红色轮廓。所以,只保留那些红色的轮廓

  • 找到要保留的轮廓后,根据要保留的轮廓创建遮罩图像

  • 使用遮罩图像,从原始图像中提取所需的对象


    • 我发现最好的方法是。这会根据颜色将图像分为不同的层。它使用k-邻域算法来实现这一点。有了这个,我能够有效地将图像分割成几个颜色相似的层

      #define numClusters 7
      
      cv::Mat src = cv::imread("img0.png");
      
      cv::Mat kMeansSrc(src.rows * src.cols, 3, CV_32F);
      //resize the image to src.rows*src.cols x 3
      //cv::kmeans expects an image that is in rows with 3 channel columns
      //this rearranges the image into (rows * columns, numChannels)
      for( int y = 0; y < src.rows; y++ )
      {
          for( int x = 0; x < src.cols; x++ )
          {
              for( int z = 0; z < 3; z++)
                  kMeansSrc.at<float>(y + x*src.rows, z) = src.at<Vec3b>(y,x)[z];
          }
      }
      
      cv::Mat labels;
      cv::Mat centers;
      int attempts = 2;
      //perform kmeans on kMeansSrc where numClusters is defined previously as 7
      //end either when desired accuracy is met or the maximum number of iterations is reached
      cv::kmeans(kMeansSrc, numClusters, labels, cv::TermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 8, 1), attempts, KMEANS_PP_CENTERS, centers );
      
      //create an array of numClusters colors
      int colors[numClusters];
      for(int i = 0; i < numClusters; i++) {
              colors[i] = 255/(i+1);
      }
      
      std::vector<cv::Mat> layers;
      
      for(int i = 0; i < numClusters; i++)
      {
          layers.push_back(cv::Mat::zeros(src.rows,src.cols,CV_32F));
      }
      
      //use the labels to draw the layers
      //using the array of colors, draw the pixels onto each label image
      for( int y = 0; y < src.rows; y++ )
      {
          for( int x = 0; x < src.cols; x++ )
          { 
              int cluster_idx = labels.at<int>(y + x*src.rows,0);
              layers[cluster_idx].at<float>(y, x) = (float)(colors[cluster_idx]);;
          }
      }
      
      std::vector<cv::Mat> srcLayers;
      
      //each layer to mask a portion of the original image
      //this leaves us with sections of similar color from the original image
      for(int i = 0; i < numClusters; i++)
      {
          layers[i].convertTo(layers[i], CV_8UC1);
          srcLayers.push_back(cv::Mat());
          src.copyTo(srcLayers[i], layers[i]);
      }
      
      #定义numClusters 7
      cv::Mat src=cv::imread(“img0.png”);
      cv::Mat Kmeansrc(src.rows*src.cols,3,cv_32F);
      //将图像大小调整为src.rows*src.cols x 3
      //cv::kmeans需要一个包含3个通道列的行中的图像
      //这会将图像重新排列为(行*列,numChannels)
      对于(int y=0;y
      how-about:,它是用python编写的,但使用opencvI。我理解这一点,但我的问题是,我真的不想有预定义的层。对于我的应用程序,我希望检测某些东西,例如颜色/灯光随完全动态背景变化的皮肤。也许我可以使用某种类似网格的泛光填充函数来填充图像的某些部分,并在填充后将其遮住。对于每一个充满洪水的片段,也许我可以使用它们作为直方图相似性算法的片段。目标是能够区分背景/前景,这样我就可以创建背景的颜色空间。当然,你可以试试泛洪填充法,我不适合那种方法。有一些阈值算法(在线)可用于设置大津或模拟退火级别。或者使用多个p-tile。你听说过吗?我听说过大津阈值法,但没有其他方法。我遇到了K-means聚类,这似乎给了我想要的结果。我很高兴你自己解决了它;)请提供你的答案并关闭这篇文章。我在过去也使用过类似的方法,但不幸的是,它不够好用。K均值聚类似乎给了我很好的结果。我将发布一个链接。这似乎是一个非常好的方法…你能分享代码和你的结果吗?我意识到名称空间处理有点混乱。我通常不定义名称空间,但我使用的一些代码定义了名称空间。我只是没有编辑所有内容以匹配样式。应该没问题谢谢你的更新。您是否可以通过添加更多注释来为代码添加更多描述…这样对包括我在内的其他人将非常有帮助。因为,我对此了解不多,所以我几乎无法理解你每一步背后的原因我清理了代码并添加了更多注释。如果你有任何问题,请告诉我。谢谢,看起来不错。如果我以后还有其他疑问,我会在这里询问。:)