레이블이 Opencv인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Opencv인 게시물을 표시합니다. 모든 게시물 표시

2017년 8월 20일 일요일

jetson tx2 board - face detect - face verification - hand gesture classification

 skt - nvidia 에서 진행한 t 해커톤에 참가했었습니다. 주제는 jetson tx2 board를 이용한 AI 제품 개발이 었고, 2017 8/26,27 무박 2일로 진행했습니다. 하루만에 시제품을 만들어내느라 엄청난 몰입을 했고, 좋은 팀원들과 협업도 해서 재미있는 경험이었습니다. (끝나고 나서는 엄청난 피로가 몰려왔지만ㅜ)

 jetson에 face detect, face verification 과 hand gesture classification으로 password 입력, face classification 해서 인물에 따라 안내 SMS 를 보내는 기능 등을 개발 했습니다.

 deep learning frame work는 caffe 를 사용했고, 메인 code는 qt에서 c++로 작성했습니다. source code는 여기 (https://github.com/ttagu99/dguardian) 에 있습니다.
  training도 caffe 이긴 한데 제가 bvlc 에서 fork해서 몇몇 기능을 추가한 거라, 이번에 사용한 train_val.prototxt는 bvlc caffe에서는 바로 안돌아 갑니다. 제 caffe code는 여기(https://github.com/ttagu99/caffe/tree/caffe_nccl)에 있습니다.

 아래 동영상은 집주인인 저의 얼굴을 인식, 인증하고, 제 Password인 손동작을 인식해서 문이 열리는 것입니다.





 아래 동영상은 집주인이 누군가와 같이 와서 Password를 보여주면 안되니 경고를 띄워주는 것과, 강도에게 협박 당해서 문을 열어야 할 경우 문은 열어 주되, 내부적으로 경찰에 신고 하는 겁니다. (지금은 진짜 경찰에 신고는 아니고 ㅎ, 제 핸드폰으로 문자를 보내는걸로 구현했습니다.)



 아래 동영상은 미리 등록된 범죄자의 얼굴을 인식해서, 집앞에 나타날 경우 경찰에 신고하는 동영상입니다. (이것도 물론 제 전화로 문자만)




 아래 동영상은 미리 등록된 택배기사님의 얼굴을 인식하고, 물건을 맡길 것을 안내하고, 집주인에게는 택배가 도착했다고 안내 SMS를 보내주는 시나리오입니다.





 아래 동영상은 외부인이 밖에 있을 경우, 집에서 나가려고 할때 밖에 누군가 있다고 알려주고, 문을 열지 물어봤을때 거절을 하는 시나리오입니다.




 아래 동영상은 외부인이 밖에 있을 경우, 집에서 나가려고 할때 밖에 누군가 있다고 알려주고, 문을 열지 물어봤을때 승락을 하는 시나리오입니다.






2017년 6월 3일 토요일

opencv Mat 를 caffe datum으로 변환, datum을 Mat로 변환

 기존 opencv에서 영상처리 하던게 익숙해서 caffe를 Mat로 변환 후에 Opencv 로 처리 할 경우가 많은데, bvlc caffe에 아직 float형 image와 multi channel(4개 이상) 이미지 변환함수가 안 만들어져 있어서 만들어 보았습니다.

 아래는 Mat를 datum으로 변환하는 것.
void CVMatToDatum(const cv::Mat& cv_img, Datum* datum, int depth)
{
 if (depth == CV_8U)
 {
  CVMatToDatum(cv_img, datum);
  return;
 }

 CHECK(cv_img.depth() == CV_32F) << "data type must be 32bit float";

 datum->set_channels(cv_img.channels());
 datum->set_height(cv_img.rows);
 datum->set_width(cv_img.cols);
 datum->clear_data();
 datum->clear_float_data();
 datum->set_encoded(false);
 int datum_channels = datum->channels();
 int datum_height = datum->height();
 int datum_width = datum->width();
 int datum_size = datum_channels * datum_height * datum_width;

 for (int h = 0; h < datum_height; ++h) 
 {
  const float* ptr = cv_img.ptr<float>(h);
  int img_index = 0;
  for (int w = 0; w < datum_width; ++w) {
   for (int c = 0; c < datum_channels; ++c) {
    datum->add_float_data( ptr[img_index++]);
   }
  }
 }
 return;
}

void CVMatToDatum(const cv::Mat& cv_img, Datum* datum) {
  CHECK(cv_img.depth() == CV_8U) << "Image data type must be unsigned byte";
  datum->set_channels(cv_img.channels());
  datum->set_height(cv_img.rows);
  datum->set_width(cv_img.cols);
  datum->clear_data();
  datum->clear_float_data();
  datum->set_encoded(false);
  int datum_channels = datum->channels();
  int datum_height = datum->height();
  int datum_width = datum->width();
  int datum_size = datum_channels * datum_height * datum_width;
  std::string buffer(datum_size, ' ');
  for (int h = 0; h < datum_height; ++h) {
    const uchar* ptr = cv_img.ptr<uchar>(h);
    int img_index = 0;
    for (int w = 0; w < datum_width; ++w) {
      for (int c = 0; c < datum_channels; ++c) {
        int datum_index = (c * datum_height + h) * datum_width + w;
        buffer[datum_index] = static_cast<char>(ptr[img_index++]);
      }
    }
  }
  datum->set_data(buffer);
}

 아래는 datum을 Mat로 변환하는 것.
cv::Mat DatumToCVMat(const Datum& datum)
{
 int datum_channels = datum.channels();
 int datum_height = datum.height();
 int datum_width = datum.width();

 string strData = datum.data();
 cv::Mat cv_img;
 if (strData.size() != 0)
 {
  cv_img.create(datum_height, datum_width, CV_8UC(datum_channels));
  const string& data = datum.data();
  std::vector<char> vec_data(data.c_str(), data.c_str() + data.size());

  for (int h = 0; h < datum_height; ++h) {
   uchar* ptr = cv_img.ptr<uchar>(h);
   int img_index = 0;
   for (int w = 0; w < datum_width; ++w) {
    for (int c = 0; c < datum_channels; ++c) {
     int datum_index = (c * datum_height + h) * datum_width + w;
     ptr[img_index++] = static_cast<uchar>(vec_data[datum_index]);
    }
   }
  }
 }
 else
 {
  cv_img.create(datum_height, datum_width, CV_32FC(datum_channels));
  for (int h = 0; h < datum_height; ++h) {
   float* ptr = cv_img.ptr<float>(h);
   int img_index = 0;
   for (int w = 0; w < datum_width; ++w) {
    for (int c = 0; c < datum_channels; ++c) {
     ptr[img_index++] = static_cast<float>(datum.float_data(img_index));
    }
   }
  }
 }
 return cv_img;
}

2016년 8월 5일 금요일

darknet train time image resize speed issue

 darknet yolo train을 할때, image size나 net 구조, gpu, cpu 사양에 따라 다르겠지만, 내 환경에서는 아래처럼 gpu 가 idle 되는 시간이 많이 발생 했다.

profiling 을 해보니 image를 random 하게 resize 하는 부분에서 cpu 처리가 오래 걸리는 것이 었다.  

resize 부분을 opencv +ipp +gpu 로 바꾸고 나서 아래처럼 개선 되었다.

내 환경에서는 4배정도 빨리 train되고 있다.

2016년 1월 19일 화요일

cuda7.0 visual studio 2015 opencv3.1

 Opencv 3.1 을 cuda 포함으로, vs2015로 컴파일 할때 아래 와 같은 문제가 생긴다.
아직 모두 test해본것은 아니지만 vs2015로 cuda7.0 포함하여 컴파일 할때만 문제가 생겼다.



테스트 해 본것은 두 종류..
1. cuda 를 빼고 vs2015로 할 경우 가능.
2. cuda 6.0, 6.5 포함 vs2010으로 할 경우 가능.

2015년 7월 3일 금요일

Qt 시작하기2 with Visual studio and Opencv

 개발 환경 : visual studio2013, qt5.4, opencv 3.0
1. 아래처럼 새프로젝트 만들기를 클릭하고 Qt5 Project를 생성한다.
 일단 sample test임으로 선택사항은 변경없이 finish


2. 프로젝트 생성후 탐색기를 보면 아래와 같이 파일들이 생겨있다.
이중 *.ui 파일을 클릭 해보면


3. qt designer 가 실행되고 dialog가 보인다.
여기에 버튼하나와 label 하나를 추가했다.

4. 아래 그림처럼 시그널/슬롯 편집버튼을 클릭하여 버튼과 다이얼로그를 연결한다.
그후에 연결설정을 클릭하여 slot에 새로운 함수(여기서는 LoadImage)를 넣는다.
5. 헤더(여기서는 qttest5.h) 파일을 열어 함수를 선언한다.
 수정부분은 검은색 표시부분




  • #ifndef QTTEST5_H
  • #define QTTEST5_H
  • #include <QtWidgets/QMainWindow>
  • #include "ui_qttest5.h"
  • class qtTest5 : public QMainWindow
  • {
  •     Q_OBJECT
  • public:
  •     qtTest5(QWidget *parent = 0);
  •     ~qtTest5();
  • private:
  •     Ui::qtTest5Class ui;
  •      private slots:
  •      void LoadImage();
  • };
  • #endif // QTTEST5_H

  • 6. cpp(여기서는 qttest5.cpp) 파일을 열어 함수를 선언한다.
     수정부분은 검은색 표시부분





  • #include "qttest5.h"
  •  #include "opencv2/opencv.hpp"
  •  using namespace cv;
  • qtTest5::qtTest5(QWidget *parent)
  •     : QMainWindow(parent)
  • {
  •     ui.setupUi(this);
  • }
  • qtTest5::~qtTest5()
  • {
  • }
  •  void qtTest5::LoadImage()
  •  {
  •      VideoCapture cap(0);
  •      Mat img;
  •      cap >> img;
  •      QImage qimg(img.data,    img.cols, img.rows, QImage::Format_RGB888);
  •      ui.label->setPixmap(QPixmap::fromImage(qimg));
  •  }

  • 컴파일 후 실행해보면 아래처럼, ui에 webcam으로 찍은 image가 display된다.




     참고사항 : opencv 빌드시 with qt를 해주어야 한다.
    현재(2015/07/09) 기준으로 opencv 3.0 을 다운로드 후 cmake로 열면 default는 with qt항목이 체크해제 되어있다. 체크 후 빌드가 필요하다. 그리고 qt 경로도 자동으로 찾지 못하였다. 아래 그림처럼 qt 경로들을 찾은후 입력 해주고 Generate를 한 후 다시 빌드해야한다.

    2015년 6월 24일 수요일

    opencv_videoio300.lib 링크에러

    opencv라이브러리를 정적으로 링크하여 개발 중
    다른 기능들은 이상없이 정적으로 개발하고 있었는데...

    VideoCapture 클래스를 사용하려고 할때
    opencv_videoio300.lib 링크에러가 났다..

    ---- 발생 에러들 중 일부 -----
    error LNK2019: ICOpen 외부 기호(참조 위치: "public: virtual struct _IplImage * __cdecl CvCaptureCAM_VFW::retrieveFrame(int)" (?retrieveFrame@CvCaptureCAM_VFW@@UEAAPEAU_IplImage@@H@Z) 함수)에서 확인하지 못했습니다.
    ---------------------

    분명 opencv_videoio300.lib 를 맞게 링크했는데도 이런 현상이 나온다면..
    아래 두개의 라이브러리도 추가 종속성에 추가해야한다.

    comctl32.lib
    vfw32.lib
    추가 후 해당 문제는 해결되었다..


    2015년 4월 10일 금요일

    opencv 3.0 에 blob or labeling algorithm 추가 되었다...

     이전 글에서 opencv 에 labeling algorithm이 없어서, 구현해서 사용한다고 했었는데..
    요즘 open source 에 기여를 해보고 싶어서, 만들어서 pull request 해봐야 겠다고 생각했었다.
    github에서 clone해서 시작해보려고 code들을 둘러 보는데 labeling 기능이 추가 되어있었다.
    이름은 익숙한 labeling이나 blob은 아니고 connected_compnents 이다.

    함수 사용은 아래와 같다.
    int cv::connectedComponents(InputArray image,
    OutputArray labels,
    int connectivity = 8,
    int ltype = CV_32S 
    )
    computes the connected components labeled image of boolean image
    image with 4 or 8 way connectivity - returns N, the total number of labels [0, N-1] where 0 represents the background label. ltype specifies the output label image type, an important consideration based on the total number of labels or alternatively the total number of pixels in the source image.
    Parameters
    imagethe image to be labeled
    labelsdestination labeled image
    connectivity8 or 4 for 8-way or 4-way connectivity respectively
    ltypeoutput image label type. Currently CV_32S and CV_16U are supported.

    blob 하고 나서 object 들의 feature들을 추출해야 할 경우가 대부분인데. 그때는 아래 함수를 사용하면 된다.

    int cv::connectedComponentsWithStats(InputArray image,
    OutputArray labels,
    OutputArray stats,
    OutputArray centroids,
    int connectivity = 8,
    int ltype = CV_32S 
    )
    This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.
    Parameters
    imagethe image to be labeled
    labelsdestination labeled image
    statsstatistics output for each label, including the background label, see below for available statistics. Statistics are accessed via stats(label, COLUMN) where COLUMN is one of cv::ConnectedComponentsTypes
    centroidsfloating point centroid (x,y) output for each label, including the background label
    connectivity8 or 4 for 8-way or 4-way connectivity respectively
    ltypeoutput image label type. Currently CV_32S and CV_16U are supported.
    실제 algorithm이 어떻게 구현되어있는지 궁금해서 source code 분석중인데.. 어렵다.
    현재 3.0 에 추가된 connectedComponents algorithm은 아래의 paper 이다.

    https://sdm.lbl.gov/~kewu/ps/LBNL-59102.pdf




    connectedComponentsWithStats 에는 blob에 대한 아래의 정보들을 추출해 준다.


    1. //! connected components algorithm output formats
    2. enum ConnectedComponentsTypes {
    3. CC_STAT_LEFT = 0, //!< The leftmost (x) coordinate which is the inclusive start of the bounding
    4. //!< box in the horizontal direction.
    5. CC_STAT_TOP = 1, //!< The topmost (y) coordinate which is the inclusive start of the bounding
    6. //!< box in the vertical direction.
    7. CC_STAT_WIDTH = 2, //!< The horizontal size of the bounding box
    8. CC_STAT_HEIGHT = 3, //!< The vertical size of the bounding box
    9. CC_STAT_AREA = 4, //!< The total area (in pixels) of the connected component
    10. CC_STAT_MAX = 5
    11. };

    blob에 대한 Gray 값 추출이 필요할 때가 많은데.. 그게 없어서 아쉽다.
    아래는 blobImage 로 각 blob의 Gray Sum을 뽑는 함수다.
    각 blob의 gray sum을 area 값으로 나누어서 평균 gray값으로 쓸수 있다.

    1.     Mat Stats;
    2.     Mat Center;
    3.     int nNum = connectedComponentsWithStats(m_BinImg,m_LabelImg,Stats,Center);
    4.     double *pContArray = new double [nNum];
    5.     ExtractObjMeanGray(pContArray,nNum);
    6.     delete [] pContArray;

    1. BOOL CBlobObjectFeature::ExtractObjMeanGray(double *pContArray, int nTotalBlobs)
    2. {
    3.     BOOL bRet = TRUE;

    4.     int nOriginType = m_OrginContImg.type();
    5.     int nLabelType = m_LabelImg.type();

    6.     ///////////////////////////////이부분 필요 없는데. 그냥 놔둔 이유..//////////////////////
    7.     ///////////////opencv3.1에서는 connectedComponents 함수는 0부터 1++ 된 blob 들만 나온다..////////
    8.     ///////////////하지만 다른 library의 blob알고리즘들은 1++가 아닌것이 더 많았다..///////////////////
    9.     /////////////// 혹 바뀔지도 모르거나 다른 lib들과 호환성을 위해 빼지 않았음////////////////
    10.     map<int, int> mapBlobIdx;
    11.     for(int y=0;y<m_LabelImg.rows;y++)
    12.     {
    13.         for(int x=0;x<m_LabelImg.cols;x++)
    14.         {
    15.             if(m_LabelImg.at<int>(Point(x,y))!=0)
    16.             {
    17.                 mapBlobIdx[m_LabelImg.at<int>(Point(x,y))]=0;
    18.             }
    19.         }
    20.     }

    21.     map<int,int>::iterator iter;
    22.     int n=1;
    23.     for(iter = mapBlobIdx.begin();iter!=mapBlobIdx.end();iter++)
    24.     {
    25.         iter->second = n;
    26.         n++;
    27.     }
    28.     ///////////////////////////////////////////////////////////////////////////////////////////////////////////
    29.     ZeroMemory(pContArray,sizeof(double)*nTotalBlobs);
    30.     for(int y=0;y<m_LabelImg.rows;y++)
    31.     {
    32.         for(int x=0;x<m_LabelImg.cols;x++)
    33.         {
    34.             int nLabel;
    35.             double dGray;
    36.             nLabel = m_LabelImg.at<int>(Point(x,y));
    37.             dGray = m_OrginContImg.at<uchar>(Point(x,y));
    38.             pContArray[mapBlobIdx[nLabel]] += dGray;
    39.         }
    40.     }
    41.     
    42.     return bRet;
    43. }