基于opencv的HSV颜色分类

基于opencv的HSV颜色分类,opencv,colors,Opencv,Colors,我试图识别图片中的四个HSV间隔,结果图像应该由0,1,2,3组成,其中这些代表已识别的颜色指数 为了减少执行时间和循环开销,我希望尽可能多地使用opencv API。我尝试过使用inRange和addWeighted,但没有找到函数算法。你能帮我吗 例如,假设我想要分类 110<H<130 && S > 100 && V > 100 as color0 140<H<160 && S > 100 &

我试图识别图片中的四个HSV间隔,结果图像应该由0,1,2,3组成,其中这些代表已识别的颜色指数

为了减少执行时间和循环开销,我希望尽可能多地使用opencv API。我尝试过使用inRange和addWeighted,但没有找到函数算法。你能帮我吗

例如,假设我想要分类

110<H<130 && S > 100 && V > 100 as color0
140<H<160 && S > 100 && V > 100 as color1
50<H<70 && S > 100 && V > 100 as color2
110 100作为颜色0
140 100作为颜色1
50 100作为颜色2

问题是,我必须以不同的时间间隔同时处理所有三个通道,因此我假设LUT方式(1个通道或3个不同通道)不是从3D输入矩阵获取一维分类输出矩阵的正确方式。

我没有尝试过,但我认为查找表(LUT)将是理想的。假设您的范围是0-30、31-78、79-91和92-100,您将创建一个256元素的LUT,其中前31个条目为零,接下来的48个条目为一,依此类推。然后调用
cv::LUT()

LUT的优点是,输出值仅从由色调值索引的表中获取,这意味着每个像素没有多个
if
语句,这意味着CPU在确定分支目标时不会暂停-因此它应该非常快


我做了一些实验,无法让内置的
LUT()
函数在此设置中快速执行-还必须使用
split()
,这会减慢速度。我用这个HSL彩色轮子作为测试图像

这是代码。首先,它使用
if
语句处理条件和时间,然后重复处理,但使用3个LUT确定输出值:

// https://stackoverflow.com/q/49333257/2836621
#include <iostream>
#include <cstdio>
#include <chrono>
#include <thread>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int
main(int argc,char*argv[])
{
   // Open input and check success
   Mat img = cv::imread("start.png",-CV_LOAD_IMAGE_ANYDEPTH);
   if (img.empty()){
      cerr << "ERROR: Failed to open start.png" << endl;
      exit(1);
   }

   // Create HSV version
   Mat hsvMat;
   cv::cvtColor(img,hsvMat,CV_BGR2HSV);

   // Create result Mat, same dimensions as input, and pointer to its data
   Mat result;
   cv::cvtColor(img,result,CV_BGR2GRAY);
   unsigned char* r;

   // Various constants
   const int Sthreshold = 100;
   const int Vthreshold = 100;
   const int Hthreshold[]={110,130,140,160,50,70};

   // Process with if statements and time it
   double t = (double)getTickCount();

   // Iterate over rows and columns of hsvMat
   Vec3b *thisRow;
   for(int j=0;j<hsvMat.rows;++j)
   {
       // Get pointers to input and output rows
       thisRow = hsvMat.ptr<Vec3b>(j);
       r = result.ptr<unsigned char>(j);
       // Iterate over cols
       for(int i=0;i<hsvMat.cols;++i)
       {
          auto H=thisRow[i][0];  // Pick up this pixel's Hue
          auto S=thisRow[i][1];  // Pick up this pixel's Sat
          auto V=thisRow[i][2];  // Pick up this pixel's Value
          // Pre-set output colour to black
          unsigned char v=0;
          if((V>Vthreshold) && (S>Sthreshold)){
             // Set result based on Hue
             if((H>Hthreshold[0]) && (H<Hthreshold[1])){
                v=64;
             } else if((H>Hthreshold[2]) && (H<Hthreshold[3])){
                v=128;
             } else if((H>Hthreshold[4]) && (H<Hthreshold[5])){
                v=192;
             }
          }
          r[i]=v;
       }
   }

   t = (((double)getTickCount() - t)*1000000)/getTickFrequency();
   cout << "Time with if statements: " << t << "us" << std::endl;

   // Write to disk for checking
   imwrite("resultptr.png",result);

   // Now do setup for LUT method

   // Pre-create LUTs for Hue, Saturation, Value
   unsigned char HLUT[256]={0};
   unsigned char SLUT[256]={0};
   unsigned char VLUT[256]={0};
   for(int i=0;i<256;i++){
      if(i>Sthreshold){SLUT[i]=1;}
      if(i>Vthreshold){VLUT[i]=1;}
      if((i>Hthreshold[0]) && (i<Hthreshold[1])){
         HLUT[i]=64;
      } else if((i>Hthreshold[2]) && (i<Hthreshold[3])){
         HLUT[i]=128;
      } else if((i>Hthreshold[4]) && (i<Hthreshold[5])){
         HLUT[i]=192;
      }
   }

   // Process with LUT and time it
   t = (double)getTickCount();

   // Iterate over rows and columns of hsvMat
   for(int j=0;j<hsvMat.rows;++j)
   {
       // Get pointers to input and output rows
       thisRow = hsvMat.ptr<Vec3b>(j);
       r = result.ptr<unsigned char>(j);
       // Iterate over cols
       for(int i=0;i<hsvMat.cols;++i)
       {
          auto H=thisRow[i][0];              // Pick up this pixel's Hue
          auto S=thisRow[i][1];              // Pick up this pixel's Sat
          auto V=thisRow[i][2];              // Pick up this pixel's Value
          r[i]=HLUT[H] * SLUT[S] * VLUT[V];  // Lookup output pixel
       }
   }

   t = (((double)getTickCount() - t)*1000000)/getTickFrequency();
   cout << "Time with LUT: " << t << "us" << std::endl;

   // Write to disk for checking
   imwrite("resultLUT.png",result);
}

如果有人想使用代码并尝试第二组循环的替代方法,请尝试并发布您得到的内容,但保持第一组循环不变以供参考。

我没有尝试过,但我认为查找表(LUT)将是理想的。假设您的范围是0-30、31-78、79-91和92-100,您将创建一个256元素的LUT,其中前31个条目为零,接下来的48个条目为一,依此类推。然后调用
cv::LUT()

LUT的优点是,输出值仅从由色调值索引的表中获取,这意味着每个像素没有多个
if
语句,这意味着CPU在确定分支目标时不会暂停-因此它应该非常快


我做了一些实验,无法让内置的
LUT()
函数在此设置中快速执行-还必须使用
split()
,这会减慢速度。我用这个HSL彩色轮子作为测试图像

这是代码。首先,它使用
if
语句处理条件和时间,然后重复处理,但使用3个LUT确定输出值:

// https://stackoverflow.com/q/49333257/2836621
#include <iostream>
#include <cstdio>
#include <chrono>
#include <thread>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int
main(int argc,char*argv[])
{
   // Open input and check success
   Mat img = cv::imread("start.png",-CV_LOAD_IMAGE_ANYDEPTH);
   if (img.empty()){
      cerr << "ERROR: Failed to open start.png" << endl;
      exit(1);
   }

   // Create HSV version
   Mat hsvMat;
   cv::cvtColor(img,hsvMat,CV_BGR2HSV);

   // Create result Mat, same dimensions as input, and pointer to its data
   Mat result;
   cv::cvtColor(img,result,CV_BGR2GRAY);
   unsigned char* r;

   // Various constants
   const int Sthreshold = 100;
   const int Vthreshold = 100;
   const int Hthreshold[]={110,130,140,160,50,70};

   // Process with if statements and time it
   double t = (double)getTickCount();

   // Iterate over rows and columns of hsvMat
   Vec3b *thisRow;
   for(int j=0;j<hsvMat.rows;++j)
   {
       // Get pointers to input and output rows
       thisRow = hsvMat.ptr<Vec3b>(j);
       r = result.ptr<unsigned char>(j);
       // Iterate over cols
       for(int i=0;i<hsvMat.cols;++i)
       {
          auto H=thisRow[i][0];  // Pick up this pixel's Hue
          auto S=thisRow[i][1];  // Pick up this pixel's Sat
          auto V=thisRow[i][2];  // Pick up this pixel's Value
          // Pre-set output colour to black
          unsigned char v=0;
          if((V>Vthreshold) && (S>Sthreshold)){
             // Set result based on Hue
             if((H>Hthreshold[0]) && (H<Hthreshold[1])){
                v=64;
             } else if((H>Hthreshold[2]) && (H<Hthreshold[3])){
                v=128;
             } else if((H>Hthreshold[4]) && (H<Hthreshold[5])){
                v=192;
             }
          }
          r[i]=v;
       }
   }

   t = (((double)getTickCount() - t)*1000000)/getTickFrequency();
   cout << "Time with if statements: " << t << "us" << std::endl;

   // Write to disk for checking
   imwrite("resultptr.png",result);

   // Now do setup for LUT method

   // Pre-create LUTs for Hue, Saturation, Value
   unsigned char HLUT[256]={0};
   unsigned char SLUT[256]={0};
   unsigned char VLUT[256]={0};
   for(int i=0;i<256;i++){
      if(i>Sthreshold){SLUT[i]=1;}
      if(i>Vthreshold){VLUT[i]=1;}
      if((i>Hthreshold[0]) && (i<Hthreshold[1])){
         HLUT[i]=64;
      } else if((i>Hthreshold[2]) && (i<Hthreshold[3])){
         HLUT[i]=128;
      } else if((i>Hthreshold[4]) && (i<Hthreshold[5])){
         HLUT[i]=192;
      }
   }

   // Process with LUT and time it
   t = (double)getTickCount();

   // Iterate over rows and columns of hsvMat
   for(int j=0;j<hsvMat.rows;++j)
   {
       // Get pointers to input and output rows
       thisRow = hsvMat.ptr<Vec3b>(j);
       r = result.ptr<unsigned char>(j);
       // Iterate over cols
       for(int i=0;i<hsvMat.cols;++i)
       {
          auto H=thisRow[i][0];              // Pick up this pixel's Hue
          auto S=thisRow[i][1];              // Pick up this pixel's Sat
          auto V=thisRow[i][2];              // Pick up this pixel's Value
          r[i]=HLUT[H] * SLUT[S] * VLUT[V];  // Lookup output pixel
       }
   }

   t = (((double)getTickCount() - t)*1000000)/getTickFrequency();
   cout << "Time with LUT: " << t << "us" << std::endl;

   // Write to disk for checking
   imwrite("resultLUT.png",result);
}

如果有人想使用代码并尝试第二组循环的替代方法,请尝试并发布您得到的内容,但保持第一组循环不变以供参考。

您的意思是1)所有图像的4个间隔都是预设和固定的,例如0-90、90-180等,或者它们2)在处理之前已知,并且每个图像可能不同,或者3)间隔是根据每个图片的内容确定的?@MarkSetchell它们是已知的间隔,并且对于每一帧都是相同的。
inRange()
将返回一个图像,该图像的值在指定范围外时为0,在指定范围内时为255。只需将图像除以255,然后加上1、2、3或其他索引。我的答案解决了你的问题吗?如果是这样,请考虑接受它作为您的答案-点击空心蜱/支票旁边的选票计数。如果没有,请说出什么不起作用,以便我或其他人可以进一步帮助您。谢谢@对不起,我忘记标记了!您的意思是1)所有图像(例如0-90、90-180等)的4个间隔都是预设和固定的,还是2)在处理之前已知,并且每个图像可能不同,或3)间隔是根据每张图片的内容确定的?@MarkSetchell它们是已知的间隔,并且每帧的间隔相同。
inRange()
将返回一个值在指定范围外为0,在指定范围内为255的图像。只需将图像除以255,然后加上1、2、3或其他索引。我的答案解决了你的问题吗?如果是这样,请考虑接受它作为您的答案-点击空心蜱/支票旁边的选票计数。如果没有,请说出什么不起作用,以便我或其他人可以进一步帮助您。谢谢@对不起,我忘记标记了!对不起,我错了。。我认为我还需要确保饱和度和值大于某个固定值,以便正确分类。。我该怎么做?阈值+lut?取决于数字。试着在一张有两列的纸上做一个小表格,在左列将原始值放在图像中,然后试着想想右列中的对应条目是什么,然后就有了查找表。否则,请单击问题下方的
edit
,输入一些具有代表性的数字,以便我或其他人可以进一步帮助您。马克,我用我的疑问更新了问题!你能检查一下吗?我听不懂你的编辑,所以我按我认为你的意思重编了一遍-我希望它是正确的。您可以使用LUT作为色调,如我的回答中所示。然后使用100的阈值作为饱和度,这样在饱和度小于100时得到0,在饱和度大于100时得到1。同样,对于价值也是如此。然后将3个垫子相乘,计算出的色调将仅显示在Sa