图像去雾 wenxiaoya2019-09-29 11:02:24 回复 3 查看
图像去雾


1.下载源代码

github地址:https://github.com/Cambricon/CNStream

2.进入到目录

cd /cnstream/samples/detection-demo

3.在后处理postprocess中增加去雾后处理的Dehazepost.cpp

image.png

定义自己的类PostprocDehaze继承父类Postproc,并且实现父类的虚函数Execute。(完整代码如下)

int PostprocDehaze::Execute(const std::vector<float*>& net_outputs,
                    const std::shared_ptr<libstream::ModelLoader>& model,
                    const CN InfoPtr& package)

#include "postproc.hpp"
#include <opencv2/opencv.hpp>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <string>
#include <utility>
#include <fstream>
#include <vector>
#include <memory>
#include <math.h>
#include "cnbase/cntypes.h"
namespace cnstream { 
using std::string; 
using std::vector; 
using namespace cv; 
class PostprocDehaze : public Postproc, virtual public libstream::ReflexObjectEx<Postproc> {
public:    
   int Execute(const std::vector<float*>& net_outputs,
               const std::shared_ptr<libstream::ModelLoader>& model,       
               const CNFrameInfoPtr& package); 
private:    
   void  Process(std::shared_ptr<CNFrameInfo> data, Mat mat);    
   double Acount(cv::Mat dark, cv::Mat img);    
   Mat  Guidedfilter(cv::Mat gray,cv::Mat mat);
   DECLARE_REFLEX_OBJECT_EX(PostprocDehaze, Postproc);
 };  // class PostprocDehaze

 Mat PostprocDehaze::Guidedfilter(cv::Mat img,cv::Mat mat){
    Mat gray;
    int height = img.rows;    
    int width = img.cols;
    cvtColor(img,gray,CV_BGR2GRAY);
    gray.convertTo(gray,CV_32FC1);    
    for (int i = 0; i<height; i++)  
    {      
      for (int j = 0; j<width; j++)
      {
          gray.at<float>(i, j)=gray.at<float>(i, j)/255.0;
        }
    }    
    int r = 60;    
    double eps = 0.0001;
    Mat mean_I;
    mean_I.convertTo(mean_I,CV_32FC1);
    boxFilter(gray,mean_I,-1,Size(r,r),Point(-1,-1),true,BORDER_DEFAULT);//方框滤波器来模糊一张图片
    Mat mean_p ;
    mean_p.convertTo(mean_p,CV_32FC1);
    boxFilter(mat,mean_p,-1,Size(r,r),Point(-1,-1),true,BORDER_DEFAULT);
    Mat mean_Ip;
    mean_Ip.convertTo(mean_Ip,CV_32FC1);
    Mat GM;
    GM.convertTo(GM,CV_32FC1);
    GM=gray.mul(mat);
    boxFilter(GM,mean_Ip,-1,Size(r,r),Point(-1,-1),true,BORDER_DEFAULT);
    Mat cov_Ip;
    cov_Ip.convertTo(cov_Ip,CV_32FC1);
    Mat MM;
    MM.convertTo(MM,CV_32FC1);
    MM=mean_I.mul(mean_p);
    cov_Ip = mean_Ip - MM;
    Mat mean_II;
    mean_II.convertTo(mean_II,CV_32FC1);
    Mat GG;
    GG.convertTo(GG,CV_32FC1);
    GG=gray.mul(gray);
    boxFilter(GG,mean_II,-1,Size(r,r),Point(-1,-1),true,BORDER_DEFAULT);
    Mat var_I;
    var_I.convertTo(var_I,CV_32FC1);
    Mat II;
    II.convertTo(II,CV_32FC1);
    II=mean_I.mul(mean_I);
    var_I = mean_II - II;
    Mat a;
    a.convertTo(a,CV_32FC1);
    a = cov_Ip/(var_I + eps);
    Mat b;
    b.convertTo(b,CV_32FC1);
    Mat AM;
    AM.convertTo(AM,CV_32FC1);
    AM=a.mul(mean_I);
    b = mean_p - AM;
    Mat mean_a ;
    mean_a.convertTo(mean_a,CV_32FC1);
    boxFilter(a,mean_a,-1,Size(r,r),Point(-1,-1),true,BORDER_DEFAULT);
    Mat mean_b ;
    mean_b.convertTo(mean_b,CV_32FC1);
    boxFilter(b,mean_b,-1,Size(r,r),Point(-1,-1),true,BORDER_DEFAULT);
    Mat q;
    q.convertTo(q,CV_32FC1);
    Mat MG;
    MG.convertTo(MG,CV_32FC1);
    MG=mean_a.mul(gray);
    q = MG + mean_b;
    q=cv::max(q,0.1);    return q;
    } 
    
    double PostprocDehaze::Acount(cv::Mat dark, cv::Mat img){    
    double sum=0;       //像素点符合条件A的和
    int pointNum = 0;   //满足要求的像素点数
    double A = 0;       //大气光强A
    double pix =0;      //暗通道图中照亮度的前0.1%范围的像素值
    CvScalar pixel;     //按图中符合A的点,在雾图中对应的像素
    float stretch_p[256], stretch_p1[256], stretch_num[256];    
    int height = img.rows;   
     int width = img.cols;   
      
     //清空三个数组,初始化填充数组元素为0    
    memset(stretch_p, 0, sizeof(stretch_p));    
    memset(stretch_p1, 0, sizeof(stretch_p1));    
    memset(stretch_num, 0, sizeof(stretch_num));

    IplImage tmp = IplImage(dark);
    CvArr* arr = (CvArr*)&tmp;
    IplImage tmp1 = IplImage(img);
    CvArr* arr1 = (CvArr*)&tmp1;    for (int i = 0; i<height; i++)
    {      
      for (int j = 0; j<width; j++)
      { 
        double  pixel0 = cvGetReal2D(arr, i, j);        
        int   pixel = (int)pixel0;
        stretch_num[pixel]++;
      }
    }    //统计各个灰度级出现的概率  

    for (int i = 0; i<256; i++)
    {
      stretch_p[i] = stretch_num[i] / (height*width);
    }    //统计各个灰度级的概率,从暗通道图中按照亮度的大小取前0.1%的像素,pix为分界点

    for (int i = 0; i<256; i++) 
    {      
      for (int j = 0; j <= i; j++)
      {
        stretch_p1[i] += stretch_p[j];        
        if (stretch_p1[i]>0.999)
        {
          pix = (double)i;
          i = 256;          
          break;
        }
      }
    }    
    for (int i = 0; i< height; i++)
    {      
      for (int j = 0; j < width; j++)
      {       
         double temp = cvGetReal2D(arr, i, j);        
         if (temp > pix )
         {
          pixel = cvGet2D(arr1, i, j);
          pointNum++;
          sum += pixel.val[0];
          sum += pixel.val[1];
          sum += pixel.val[2];
        }
      }
    }
    A = sum / (3 * pointNum);
    A = A/255.0;    if (A > 220.0)
    {
      A = 220.0; 
    }   
     printf("float: %f\n", A);    
     return A;
  } 
  
 int PostprocDehaze::Execute(const std::vector<float*>& net_outputs,      
                             const std::shared_ptr<libstream::ModelLoader>& model,      
                             const CNFrameInfoPtr& package) { 
     if (net_outputs.size() != 1) {      
         std::cerr << "[Warnning] Ssd neuron network only has one output,"
              " but get " +                 
              std:: to_string(net_outputs.size()) + "\n";     
         return -1;
    }    
    auto sp = model->output_shapes()[0];    
    auto data = net_outputs[0];    
    int height = sp.h();    
    int width = sp.w();
    Mat mat = Mat(height,width,CV_32FC1, data);//网络输出透射图
    std::cout<<"mat height::"<<height<<width<<std::endl;
    Process(package,mat);    
    return 0;
  } 
  
  void  PostprocDehaze::Process(std::shared_ptr<CNFrameInfo> data, Mat mat) {  
  int width = mat.cols;  
  int height = mat.rows;  
  std::cout<<"data height::"<<height<<width<<std::endl;
  cv::Mat img;
  img = *data->frame.ImageBGR();

  Mat q, Dehaze;  
  double A = 0.0;//大气光值
  imwrite("img.jpg",img); //得到原图
  cv::resize(img, img, Size(width,height));//将原图大小调整为和网络输出大小相同
  
  vector<Mat> rgb;
  split(img,rgb);
  Mat dc=cv::min(cv::min( rgb.at(0),rgb.at(1)),rgb.at(2));//暗通道实际上是在rgb三个通道中取最小值组成灰度图
  Mat kernel = getStructuringElement(cv::MORPH_RECT,Size(15,15));
  Mat dark;
  erode(dc,dark,kernel);//腐蚀就是最小值滤波

  A = Acount(dark,img);//1.从暗通道图中按照亮度大小取前0.1%的像素。2.在这些位置中,在原始图像中寻找对应具有最高亮度点的值,作为A值。
  q = Guidedfilter(img,mat);//引导滤波

  Dehaze = Mat(height,width,CV_32FC3);
  img.convertTo(img,CV_32FC3);  //将得到的原图进行归一化以便后续计算
  for (int i = 0; i<height; i++)  
  {  for (int j = 0; j<width; j++)
     {
        img.at<Vec3f>(i, j)[0]=img.at<Vec3f>(i, j)[0]/255.0;
        img.at<Vec3f>(i, j)[1]=img.at<Vec3f>(i, j)[1]/255.0;
        img.at<Vec3f>(i, j)[2]=img.at<Vec3f>(i, j)[2]/255.0;
      }
  }  
  for (int i = 0; i<height; i++)  
  {      
     for (int j = 0; j<width; j++)
     {
       Dehaze.at<Vec3f>(i, j)[0]=(((img.at<Vec3f>(i, j)[0]-A)/q.at<float>(i, j))+ A)*255;
       Dehaze.at<Vec3f>(i, j)[1]=(((img.at<Vec3f>(i, j)[1]-A)/q.at<float>(i, j))+ A)*255;
       Dehaze.at<Vec3f>(i, j)[2]=(((img.at<Vec3f>(i, j)[2]-A)/q.at<float>(i, j))+ A)*255;
     }
  }  
  static int i = 0;  
  std::string img_name = std::to_string(i++) + ".jpg";
  imwrite(img_name, Dehaze);  
 }
  IMPLEMENT_REFLEX_OBJECT_EX(PostprocDehaze, Postproc)
}  // namespace

net_outputs中可以获取网络输出,package中可以得到原图,流程如下:

a

Mat mat = Mat(height,width,CV_32FC1, data)

得到网络输出透射图t(x)

b
img = cv::Mat(data-> .height*3/2, data-> .width, CV_8UC1, img_data, data-> .strides[0]);

得到原图

c
Mat dc=cv::min(cv::min( rgb.at(0),rgb.at(1)),rgb.at(2));

暗通道实际上是在rgb三个通道中取最小值组成灰度图

d
A = Acount(dark,img);

1.从暗通道图中按照亮度大小取前0.1%的像素。 2.在这些位置中,在原始图像中寻找对应具有最高亮度点的值,作为A值。

q = Guidedfilter(img,mat);

引导滤波

e
Dehaze.at<Vec3f>(i, j)[0]=(((img.at<Vec3f>(i, j)[0]-A)/q.at<float>(i, j))+ A)*255;

根据投射图和原图以及大气光值恢复去雾后的图像

4.图片路径

我自己的图片存放在

/detection-demo/test_dehaze/images

需要写一个files.list_image指明图片路径

5.改写配置文件dehaze_config.json

       image.png

(a)next_modules:dehaze

(b)model_path:指明模型路径

(c)postproc_name:指明子类类名

6.运行脚本

      image.png

图片路径我们将以参数形式传入,当我们输入为图片时要将input_image改为true,同理,若输入为视频则将其改为false

7.编译运行

在目录/cnstream/build下进行

camke ..
make -j8

运行

./run_dehaze.sh files.list_image

8.运行结果

我们以imwrite的形式将图片保存下来,视频也是一帧帧保存为图片,后续会再进行改写

image.pngimage.png



官方微博 官方微信
版权所有 © 2019 寒武纪 Cambricon 备案/许可证号:京ICP备17003415
关闭