C++ C+中局部阈值的实现+;/OpenCV

C++ C+中局部阈值的实现+;/OpenCV,c++,opencv,C++,Opencv,我想实现一个局部阈值算法,我需要你的专业知识 我的图像大小调整为600x400,灰度 本地化的基本思想过程: 使用在每个像素处拍摄的9x9 ROI分割图像,并计算该区域的最大强度 创建一个9x9内核 条件: 如果遮罩的中心像素高于最大强度的50%,请将中心像素设置为真。(应用遮罩) 我的问题是: 我应该如何选择内核/掩码 cv::Mat ROI; cv::Mat mask(input.size(),CV_8UC1, cv::Scalar::all(0)); // create

我想实现一个局部阈值算法,我需要你的专业知识

我的图像大小调整为600x400,灰度

本地化的基本思想过程:

  • 使用在每个像素处拍摄的9x9 ROI分割图像,并计算该区域的最大强度
  • 创建一个9x9内核

    条件:

    • 如果遮罩的中心像素高于最大强度的50%,请将中心像素设置为真。(应用遮罩)
我的问题是:

  • 我应该如何选择内核/掩码

      cv::Mat ROI;
      cv::Mat mask(input.size(),CV_8UC1, cv::Scalar::all(0)); // create mask of 0s at first
      const int kerneldepth = 1;
      const int kernelsize = 9;
      cv::Mat kernel = cv::Mat::ones( kernelsize, kernelsize, CV_8UC1 );
    
    
    //take ROI of 9x9 and apply a threshold
    
    for( double x = 9; x < input.cols -9; x++ ){
         for( double y = 9 ; y < input.rows - 9 ; y++ ){
    
            try{
              double x_left = x - 4;
              double x_right = x + 4;
              double y_up = y + 4;
              double y_down = y - 4;
              double maxVal;
              double minVal;
              cv::Point anchor(kernelsize/2,kernelsize/2);
    
              cv::Rect ROI = cv::Rect(x_left,y_down,9,9);
              cv::Mat ROI_Mat = input(ROI);                                // a new matrix for ROI
              cv::Scalar avgPixelIntensity = cv::mean( ROI_Mat );          // calculate mean
              cv::minMaxLoc(ROI_Mat,&minVal,&maxVal);
    
              if( input.at<uchar>(x,y) >= 0.5*maxVal){
    
              cv::filter2D(input,mask,-1,kernel,anchor,0);
        } else { break;}
    
        }
    
        catch (cv::Exception &e){
    
            e.what();
        }
    
    
       }
    
      *****************************UPDATED CODE: ******************************************
    
        applyLocalThresh(cv::Mat &src, cv::Mat& out){
          double maxVal, minVal;
          cv::Mat output;
          int top, bottom, left , right;
          int borderType = cv::BORDER_CONSTANT;
          cv::Scalar value;
          top = (int) (9); bottom = (int) (9);
          left = (int) (9); right = (int) (9);
          output = src;
          out = src;
          value = 0;
          cv::copyMakeBorder(src,output,top,bottom,left,right,borderType,value);
    
         for(int y = 9; y < src.rows; y++) {
    
            for(int x = 9; x < src.cols; x ++) {
    
                    cv::Mat ROI = src(cv::Rect(cv::Point(x-4,y-4),cv::Size(9,9)));
                    cv::minMaxLoc(ROI,&minVal,&maxVal);
    
            if(src.at<uchar>(cv::Point(x-4,y-4)) >= 0.6*maxVal){
    
            out.at<uchar>(cv::Point(x-4,y-4)) = 255;
        }else{
            out.at<uchar>(cv::Point(x-4,y-4));
    
            }
        }
    }
     }
    
    cv::Mat-ROI;
    cv::Mat掩码(input.size(),cv_8UC1,cv::Scalar::all(0));//首先创建0的掩码
    常数int kerneldepth=1;
    const int kernelsize=9;
    cv::Mat kernel=cv::Mat::ones(kernelsize,kernelsize,cv_8UC1);
    //采用9x9的投资回报率并应用阈值
    for(双x=9;x=0.5*maxVal){
    cv::filter2D(输入,掩码,-1,内核,锚点,0);
    }else{break;}
    }
    捕获(cv::异常和e){
    e、 什么();
    }
    }
    *****************************更新代码:******************************************
    applyLocalThresh(cv::Mat&src,cv::Mat&out){
    双最大值,最小值;
    cv::Mat输出;
    int顶部、底部、左侧、右侧;
    int borderType=cv::BORDER_常量;
    cv::标量值;
    顶部=(整数)(9);底部=(整数)(9);
    左=(int)(9);右=(int)(9);
    输出=src;
    out=src;
    数值=0;
    cv::copyMakeBorder(src、输出、顶部、底部、左侧、右侧、borderType、值);
    对于(int y=9;y=0.6*maxVal){
    在(cv::点(x-4,y-4))=255;
    }否则{
    在(cv::点(x-4,y-4));
    }
    }
    }
    }
    

    • 我担心这种方法不完全正确。让我解释一下:对于涉及内核的操作,必须小心地将内核的中心放置在将要转换的像素上。这是因为3x3、5x5、7x7、9x9(…)内核只计算图像中一个像素的值,该像素位于内核的中心[0,0]

      如果您考虑如何计算图像的第一个像素的值,9x9内核的中心将被放置在坐标[0,0]处。这意味着内核的3/4将被放置在负坐标处,即引用不存在的像素的坐标:

       [-4,-4][-3,-4][-2,-4][-1,-4][ 0,-4][ 1,-4][ 2,-4][ 3,-4][ 4,-4]
       [-4,-3][-3,-3][-2,-3][-1,-3][ 0,-3][ 1,-3][ 2,-3][ 3,-3][ 4,-3]
       [-4,-2][-3,-2][-2,-2][-1,-2][ 0,-2][ 1,-2][ 2,-2][ 3,-2][ 4,-2]
       [-4,-1][-3,-1][-2,-1][-1,-1][ 0,-1][ 1,-1][ 2,-1][ 3,-1][ 4,-1]
       [-4, 0][-3, 0][-2, 0][-1, 0][ 0, 0][ 1, 0][ 2, 0][ 3, 0][ 4, 0]
       [-4, 1][-3, 1][-2, 1][-1, 1][ 0, 1][ 1, 1][ 2, 1][ 3, 1][ 4, 1]
       [-4, 2][-3, 2][-2, 2][-1, 2][ 0, 2][ 1, 2][ 2, 2][ 3, 2][ 4, 2]
       [-4, 3][-3, 3][-2, 3][-1, 3][ 0, 3][ 1, 3][ 2, 3][ 3, 3][ 4, 3]
       [-4, 4][-3, 4][-2, 4][-1, 4][ 0, 4][ 1, 4][ 2, 4][ 3, 4][ 4, 4]
      
      图像边界附近的像素总是会发生这种情况。因此,对于第一个像素的计算,我们必须将计算限制为内核的1/4,即目标图像中的有效坐标:

       [     ][     ][     ][     ][     ][     ][     ][     ][     ]
       [     ][     ][     ][     ][     ][     ][     ][     ][     ]
       [     ][     ][     ][     ][     ][     ][     ][     ][     ]
       [     ][     ][     ][     ][     ][     ][     ][     ][     ]
       [     ][     ][     ][     ][ 0, 0][ 1, 0][ 2, 0][ 3, 0][ 4, 0]
       [     ][     ][     ][     ][ 0, 1][ 1, 1][ 2, 1][ 3, 1][ 4, 1]
       [     ][     ][     ][     ][ 0, 2][ 1, 2][ 2, 2][ 3, 2][ 4, 2]
       [     ][     ][     ][     ][ 0, 3][ 1, 3][ 2, 3][ 3, 3][ 4, 3]
       [     ][     ][     ][     ][ 0, 4][ 1, 4][ 2, 4][ 3, 4][ 4, 4]
      
      因此,当前方法的问题是,在某个时刻,您将设置一个具有负坐标的ROI,当执行这些指令时,您将看到一个很好的崩溃:

      cv::Mat ROI_Mat = input(ROI);  // crash 
      
      解决方案不是使用ROI,而是自己实现该算法。我只是看不到这种自定义计算与
      cv::filter2D()
      一起工作。这里有一些小东西可以帮助您开始:

      void local_threshold(const cv::Mat& input, cv::Mat& output)
      {
          if (input.channels() != 1)
          {
              std::cout << "local_threshold !!! input image must be single channel" << std::endl;
              return;
          }
      
          output = cv::Mat(input.rows, input.cols, CV_8UC1);
          double min_val = 0, max_val = 0;
      
          for (int i = 0; i < input.rows; i++)
              for (int j = 0; j < input.cols; j++)
              {
                  cv::Mat kernel = Mat::zeros(9, 9, output.type());
      
                  // Implement logic to fill the 9x9 kernel with
                  // values from the input Mat, respecting boundaries.
      
                  cv::Scalar avg_intensity = cv::mean(kernel);
                  cv::minMaxLoc(kernel, &min_val,&max_val);
      
                  if (input.at<uchar>(i,j) > (max_val / 2))
                      output.at<unsigned char>(i,j) = 255;
                  else
                      output.at<unsigned char>(i,j) = 0;
              }
      }
      
      void local_阈值(常量cv::Mat&输入,cv::Mat&输出)
      {
      如果(input.channels()!=1)
      {
      
      std::cout您可以通过在OpenCV中进行扩张和比较来实现这一点

      im = load image here;
      di = dilate im with a 9x9 kernel;
      bw = im > (di * 0.5); // in OpenCV, pixels of bw are set to 255 or 0
      
      用Matlab/Octave中的4x6图像和3x3内核来说明这一点的一个简单示例:

      即时通讯=

      di=

      th=di*.5

      th=

      bw=im>th

      体重=


      在进一步思考并了解如何利用编程方面的基本知识后,我提出了这段代码,它不是最有效的,但可以完成工作

      我的方法的主要问题是什么

      • 边界像素,其中一个主要问题以及内核和掩码之间的整个索引操作引起了轻微的头痛
      我解决那件事的方法是什么

      • 我的阈值表示需要相对较高的强度级别来设置真实像素。因此,我用一些假想的负像素填充图像,并使算法从原始图像的第一个像素开始。然后将结果保存到遮罩中
      结果:

      • 成功
      代码:

           double maxVal, minVal;
           cv::Mat output;
           int top, bottom, left , right;
           int borderType = cv::BORDER_CONSTANT;
           cv::Scalar value;
           top = (int) (4); bottom = (int) (4);
           left = (int) (4); right = (int) (4);
           output = src;
           out = src;
           value = 0;
           cv::copyMakeBorder(src,output,top,bottom,left,right,borderType,value);
      
      for(int y = 4; y < output.rows - 4; y++) {
      
          for(int x = 4; x < output.cols - 4; x ++) {
      
              // apply local ROI
      
              cv::Mat ROI = output(cv::Rect(cv::Point(x-4,y-4),cv::Size(9,9)));
              cv::minMaxLoc(ROI,&minVal,&maxVal);    // extract max intensity values in the ROI
      
              if(src.at<uchar>(cv::Point(x-4,y-4)) >= 0.5*maxVal){    // apply local threshold w.r.t highest intensity level
      
                  out.at<uchar>(cv::Point(x-4,y-4)) = 255;            // change pixel value in mask if true
              }else{
                  out.at<uchar>(cv::Point(x-4,y-4)) = 0;
      
                  }
      
              }
          }
      
      
      
      }
      
      double maxVal,minVal;
      cv::Mat输出;
      int顶部、底部、左侧、右侧;
      int borderType=cv::BORDER_常量;
      cv::标量值;
      顶部=(整数)(4);底部=(整数)(4);
      左=(int)(4);右=(int)(4);
      输出=src;
      out=src;
      数值=0;
      cv::copyMakeBorder(src、输出、顶部、底部、左侧、右侧、borderType、值);
      对于(int y=4;y=0.5*maxVal){//应用局部阈值w.r.t最高强度水平
      在(cv::点(x-4,y-4))=255;
      
       3     4     5     6     7     7
       4     5     6     7     8     8
       5     6     7     8     9     9
       5     6     7     8     9     9
      
      1.5000    2.0000    2.5000    3.0000    3.5000    3.5000
      2.0000    2.5000    3.0000    3.5000    4.0000    4.0000
      2.5000    3.0000    3.5000    4.0000    4.5000    4.5000
      2.5000    3.0000    3.5000    4.0000    4.5000    4.5000
      
       0     0     1     1     1     1
       0     1     1     1     1     1
       1     1     1     1     1     1
       1     1     1     1     1     1
      
           double maxVal, minVal;
           cv::Mat output;
           int top, bottom, left , right;
           int borderType = cv::BORDER_CONSTANT;
           cv::Scalar value;
           top = (int) (4); bottom = (int) (4);
           left = (int) (4); right = (int) (4);
           output = src;
           out = src;
           value = 0;
           cv::copyMakeBorder(src,output,top,bottom,left,right,borderType,value);
      
      for(int y = 4; y < output.rows - 4; y++) {
      
          for(int x = 4; x < output.cols - 4; x ++) {
      
              // apply local ROI
      
              cv::Mat ROI = output(cv::Rect(cv::Point(x-4,y-4),cv::Size(9,9)));
              cv::minMaxLoc(ROI,&minVal,&maxVal);    // extract max intensity values in the ROI
      
              if(src.at<uchar>(cv::Point(x-4,y-4)) >= 0.5*maxVal){    // apply local threshold w.r.t highest intensity level
      
                  out.at<uchar>(cv::Point(x-4,y-4)) = 255;            // change pixel value in mask if true
              }else{
                  out.at<uchar>(cv::Point(x-4,y-4)) = 0;
      
                  }
      
              }
          }
      
      
      
      }