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

2017년 7월 11일 화요일

cvLoadImage, cvarrToMat, cvtColor

std::string filename의 image를 load하여 간단한 변환.

     IplImage* img = cvLoadImage(fileName.c_str());

     cv::Mat src = cv::cvarrToMat(img).clone();
     cv::Mat src_gray;

     cvReleaseImage(&img);

     if (src.channels() == 3)
     {
           cv::cvtColor(src, src_gray, CV_BGR2GRAY);
     }
     else if (src.channels() == 4)
     {
           cv::cvtColor(src, src_gray, CV_BGRA2GRAY);
     }
     else if (src.channels() == 1)
     {
           src_gray = src.clone();
     }

     // if (!findContours(src_gray, useFilter)) return false;

     cv::Mat sum_image;
     cv::cvtColor(src_gray, sum_image, CV_GRAY2RGB);

     for (int i = 0; i < m_candidatesTargets.size(); i++)
     {
           Target& t = m_candidatesTargets[i];
           if (m_candidatesTargets[i].type == Target::UNKNOWN)
           {
                ellipse(sum_image, t.ellipse, cv::Scalar(0, 255, 0), 1, 8);
           }
           else
           {
                ellipse(sum_image, t.ellipse, cv::Scalar(0, 0, 255), 1, 8);
           }

     }

     cv::namedWindow("sum_image", CV_WND_PROP_ASPECTRATIO);
     cv::imshow("sum_image", sum_image);
     cv::waitKey();

cv::saturate_cast

cv::saturate_cast<type> : type에 따른 유효한 값으로 보정

       cv::Mat sourceGray;

       ...

       double alpha = 2.0;
       int beta = 0;

       for (int y = 0; y < sourceGray.rows; y++) {
             for (int x = 0; x < sourceGray.cols; x++) {
                   sourceGray.at<uchar>(y, x) = cv::saturate_cast<uchar>(alpha * sourceGray.at<uchar>(y, x) + beta);
             }
       }





cv::Canny

imgproc.hpp
@param image 8-bit input image.
@param edges output edge map; single channels 8-bit image, which has the same size as image .
@param threshold1 first threshold for the hysteresis procedure.
@param threshold2 second threshold for the hysteresis procedure.
@param apertureSize aperture size for the Sobel operator.
@param L2gradient a flag, indicating whether a more accurate \f$L_2\f$ norm
\f$=\sqrt{(dI/dx)^2 + (dI/dy)^2}\f$ should be used to calculate the image gradient magnitude (
L2gradient=true ), or whether the default \f$L_1\f$ norm \f$=|dI/dx|+|dI/dy|\f$ is enough (
L2gradient=false ).cv::
 */
CV_EXPORTS_W void Canny( InputArray image, OutputArray edges,
                         double threshold1, double threshold2,
                         int apertureSize = 3, bool L2gradient = false );

example
cv::Canny(src, edge, 40, 100, 3);



 

double형 cv::Mat의 image 출력

ri라는 double형 cv::Mat를 imshow()함수로 출력한다.

     cv::Mat mat = ri->getClone();      // ri = cv::Mat(h, w, cv::DataType<double>::type)

     std::cout << "channels : " << mat.channels() << std::endl; // 1
     std::cout << "type : " << mat.type() << std::endl;         // 6
     std::cout << "depth : " << mat.depth() << std::endl;       // 6 CV_64F

     int h = mat.rows;
     int w = mat.cols;

     double t_max = DBL_MIN;
     double t_min = DBL_MAX;
     Concurrency::parallel_for(0, h, [&](int y)
     {
           for (int x = 0; x < w; x++)
           {
                if (mat.at<double>(y, x) < 0) continue; // error 값
                if (mat.at<double>(y, x) > t_max) t_max = mat.at<double>(y, x);
                if (mat.at<double>(y, x) < t_min) t_min = mat.at<double>(y, x);
           }
     });

     double scale = 255. / (t_max - t_min);

     cv::Mat sumImage(mat.rows, mat.cols, CV_8UC3); // 3channels
     sumImage.setTo(cv::Scalar(0,200,0));

     Concurrency::parallel_for(0, h, [&](int y)
     {
           for (int x = 0; x < w; x++)
           {
                if (mat.at<double>(y, x) < 0) continue; // error 값
                unsigned char intensity = cv::saturate_cast<uchar>((mat.at<double>(y, x) - t_min) * scale);
                sumImage.at<cv::Vec3b>(y, x)[0] = intensity;
                sumImage.at<cv::Vec3b>(y, x)[1] = intensity;
                sumImage.at<cv::Vec3b>(y, x)[2] = intensity;

           }
     });

     cv::imshow("hello", sumImage);
     cv::waitKey(0);





2017년 2월 15일 수요일

OpenCV example k-means clustering

K-평균 알고리즘(K-means algorithm)은 주어진 데이터를 k개의 클러스터로 묶는 알고리즘으로, 각 클러스터와 거리 차이의 분산을 최소화하는 방식으로 동작한다.
이 알고리즘은 자율 학습의 일종으로, 레이블이 달려 있지 않은 입력 데이터에 레이블을 달아주는 역할을 수행한다.
이 알고리즘은 EM 알고리즘을 이용한 클러스터링과 비슷한 구조를 가지고 있다.

알고리즘 순서만 보면..
1. 각 점들을 k개 집합으로 나눈다. 이때 임의로 나누거나, 어떤 휴리스틱 기법을 사용할 수도 있다.
2. 그 다음 각 집합의 무게 중심을 구한다.
3. 각각의 점들을 방금 구한 무게중심 가운데 제일 가까운 것에 연결지음으로써 새로이 집합을 나눌 수 있다.
4. 이 작업(2,3)을 반복하면 점들이 소속된 집합을 바꾸지 않거나, 무게중심이 변하지 않는 상태로 수렴될 수 있다.

자세한 알고리즘 설명은 다음을 참조

example.1
아래의 이미지를 사용하여 하얀 타원의 크기를 기준으로 k-means clustering 을 실행해 본다.
 
      cv::Mat source = cv::cvarrToMat(img);
      cv::Mat output(source);


      cv::Mat sourceGray;
      cv::cvtColor(source, sourceGray, CV_RGB2GRAY);


cv::blur(sourceGray, sourceGray, cv::Size(3, 3));


cv::Mat detectedEdges;
      int lowThreshold = 50;
      int ratio = 3;
      int kernelSize = 3;
      cv::Canny(sourceGray, detectedEdges, lowThreshold, lowThreshold * ratio, kernelSize);



      int width = detectedEdges.cols;
      int height = detectedEdges.rows;

      std::vector<Target> candiatesTargets;

      // 후보 타겟 선정;
      {
            std::vector<std::vector<cv::Point>> contours;
            std::vector<cv::Vec4i> hierarchy;

            cv::findContours(detectedEdges, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE, cv::Point(0, 0));

            std::vector<cv::RotatedRect> minRect(contours.size());
            std::vector<cv::RotatedRect> minEllipse(contours.size());

            for (int i = 0; i < contours.size(); i++)
            {
                  minRect[i] = cv::minAreaRect(cv::Mat(contours[i]));

                  if (contours[i].size() <= 5) continue;

                  minEllipse[i] = cv::fitEllipse(cv::Mat(contours[i]));

                  if (hierarchy[i][2] != -1)
                  {
                        //ellipse(output, minEllipse[i], cv::Scalar(0, 255, 0), 1, 8);
                  }
                  else
                  {
                        float w = minEllipse[i].size.width;
                        float h = minEllipse[i].size.height;

                        if (w > 50 || w < 7) continue;
                        if (h > 50 || h < 7) continue;

                        float temp_ratio = w / h;
                        if (temp_ratio > 2 || temp_ratio < 0.5) continue; // 좌우로 긴 모양;

                        //ellipse(output, minEllipse[i], cv::Scalar(0, 0, 255), 1, 8);

                        Target t;
                        t.ellipse = minEllipse[i];
                        candiatesTargets.push_back(t);
                  }
            }

      }



      // clustering - random partition
      const int numCandiates = candiatesTargets.size();
      const int K = 5;
      const int maxIter = 20;

      cv::Vec2f k_ellipseSize[K];
      cv::Vec2f k_ellipseSize_backup[K];
      int cntK[K] = { 0, };

      // 초기화, 랜덤 배분;
      for (int i = 0; i < numCandiates; i++)
      {
            candiatesTargets[i].k = -1;

            cv::Vec2i dxy(candiatesTargets[i].ellipse.size.width, candiatesTargets[i].ellipse.size.height);
            k_ellipseSize[i % K][0] += (float)dxy[0]; // 원이 발견된 순서대로 번갈아가며 K 번째에 넣는다;
            k_ellipseSize[i % K][1] += (float)dxy[1];
            cntK[i % K]++;
      }

      for (int i = 0; i < K; i++)
      {
            if (cntK[i] == 0) continue; // 원이 하나도 없을때;
            k_ellipseSize[i] /= (float)cntK[i];
      }

      int turn = 0;
      while (1)
      {
            for (int i = 0; i < numCandiates; i++)
            {
                  cv::Vec2i dxy(candiatesTargets[i].ellipse.size.width, candiatesTargets[i].ellipse.size.height);

                  // 각 집합의 평균 크기와 자신의 크기가 가장 비슷한 집합으로 옮긴다;
                  float min = FLT_MAX;
                  for (int _k = 0; _k < K; _k++)
                  {
                        cv::Vec2f _dxy;
                        _dxy[0] = (float)dxy[0] - k_ellipseSize[_k][0];
                        _dxy[1] = (float)dxy[1] - k_ellipseSize[_k][1];

                        float d = (_dxy[0]) * (_dxy[0]) + (_dxy[1]) * (_dxy[1]);

                        if (min > d)
                        {
                              min = d;
                              candiatesTargets[i].k = _k;
                        }
                  }
            }

            // 초기화;
            for (int i = 0; i < K; i++)
            {
                  k_ellipseSize[i] = cv::Vec2f(0.f, 0.f);
                  cntK[i] = 0;
            }

            // 집합의 평균값을 다시 계산;
            for (int i = 0; i < numCandiates; i++)
            {
                  if (candiatesTargets[i].k < 0) continue;

                  cv::Vec2i dxy(candiatesTargets[i].ellipse.size.width, candiatesTargets[i].ellipse.size.height);
                  k_ellipseSize[candiatesTargets[i].k][0] += (float)dxy[0];
                  k_ellipseSize[candiatesTargets[i].k][1] += (float)dxy[1];

                  cntK[candiatesTargets[i].k] += 1;
            }

            bool re = true;
            for (int i = 0; i < K; i++)
            {
                  if (cntK[i] == 0) continue;
                  k_ellipseSize[i] /= (float)cntK[i];

                  // 각 집합의 평균값이 거의 변동이 없으면 re = true로 셋하고 while문을 빠져나간다;
                  if (fabs(k_ellipseSize_backup[i][0] - k_ellipseSize[i][0]) > 0.001 && fabs(k_ellipseSize_backup[i][1] - k_ellipseSize[i][1]) > 0.001) re = false;
            }

            if (re) break;
            if (turn > maxIter) break;

            for (int i = 0; i < K; i++)
            {
                  k_ellipseSize_backup[i] = k_ellipseSize[i];
            }
            turn++;
      }

      // 크기 순서대로 k를 정렬;
      {
            std::vector<std::pair<int, float>> kList;

            for (int i = 0; i < K; i++)
            {
                  kList.push_back(std::pair<int, float>(i, k_ellipseSize[i][0] * k_ellipseSize[i][0] + k_ellipseSize[i][1] * k_ellipseSize[i][1]));
            }

            std::sort(kList.begin(), kList.end(), [](std::pair<int, float>& a, std::pair<int, float>& b){ return a.second > b.second; });

            for (int i = 0; i < numCandiates; i++)
            {
                  if (candiatesTargets[i].k < 0) continue;

                  for (int _k = 0; _k < K; _k++)
                  {
                        if (candiatesTargets[i].k == kList[_k].first)
                        {
                              candiatesTargets[i].k = _k;
                              break;
                        }
                  }
            }
      }

      cv::Scalar cols[7];
      cols[0] = cv::Scalar(0, 0, 255);
      cols[1] = cv::Scalar(0, 255, 255);
      cols[2] = cv::Scalar(0, 255, 0);
      cols[3] = cv::Scalar(255, 255, 0);
      cols[4] = cv::Scalar(255, 0, 0);
      cols[5] = cv::Scalar(255, 0, 255);
      cols[6] = cv::Scalar(255, 255, 255);

      for (int i = 0; i < numCandiates; i++)
      {
            ellipse(output, candiatesTargets[i].ellipse, cols[candiatesTargets[i].k], 2, 8);
      }





끝.

OpenCV 그리기 함수

직선 그리기
/* Draws 4-connected, 8-connected or antialiased line segment connecting two points */
CVAPI(void)  cvLine( CvArr* img, CvPoint pt1, CvPoint pt2,
                     CvScalar color, int thickness CV_DEFAULT(1),
                     int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0) );
통상적인 image 의 입력, CvPoint 타입의 점 두개를 입력받는다. 다음으로 색상값을 CvScalar로 입력받는다.
두께, anti-aliasing 방법 등을 추가로 선택 할 수 있다.

사각형 그리기
/* Draws a rectangle given two opposite corners of the rectangle (pt1 & pt2), if thickness<0 (e.g. thickness == CV_FILLED), the filled box is drawn */
CVAPI(void)  cvRectangle( CvArr* img, CvPoint pt1, CvPoint pt2,
                          CvScalar color, int thickness CV_DEFAULT(1),
                          int line_type CV_DEFAULT(8),
                          int shift CV_DEFAULT(0));
위의 cvLine과 유사하다. 두 점을 대각 방향의 모서리점으로 간주하여 사각형을 그린다.

/* Draws a rectangle specified by a CvRect structure */
CVAPI(void)  cvRectangleR( CvArr* img, CvRect r,
                           CvScalar color, int thickness CV_DEFAULT(1),
                           int line_type CV_DEFAULT(8),
                           int shift CV_DEFAULT(0));

원 그리기
/* Draws a circle with specified center and radius. Thickness works in the same way as with cvRectangle */
CVAPI(void)  cvCircle( CvArr* img, CvPoint center, int radius,
                       CvScalar color, int thickness CV_DEFAULT(1),
                       int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0));

타원 그리기
/* Draws ellipse outline, filled ellipse, elliptic arc or filled elliptic sector,
   depending on <thickness>, <start_angle> and <end_angle> parameters. The resultant figure
   is rotated by <angle>. All the angles are in degrees */
CVAPI(void)  cvEllipse( CvArr* img, CvPoint center, CvSize axes,
                        double angle, double start_angle, double end_angle,
                        CvScalar color, int thickness CV_DEFAULT(1),
                        int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0));
angle을 지정하여 호를 그릴 수 있다.

다각형 그리기
/* Fills convex or monotonous polygon. */
CVAPI(void)  cvFillConvexPoly( CvArr* img, const CvPoint* pts, int npts, CvScalar color,
                               int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0));

/* Fills an area bounded by one or more arbitrary polygons */
CVAPI(void)  cvFillPoly( CvArr* img, CvPoint** pts, const int* npts,
                         int contours, CvScalar color,
                         int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0) );

/* Draws one or more polygonal curves */
CVAPI(void)  cvPolyLine( CvArr* img, CvPoint** pts, const int* npts, int contours,
                         int is_closed, CvScalar color, int thickness CV_DEFAULT(1),
                         int line_type CV_DEFAULT(8), int shift CV_DEFAULT(0) );
CvPoint 구조체의 배열로 전달이 된다.

글자 그리기
/* Renders text stroke with specified font and color at specified location.
   CvFont should be initialized with cvInitFont */
CVAPI(void)  cvPutText( CvArr* img, const char* text, CvPoint org,
                        const CvFont* font, CvScalar color );
org을 좌하단으로 두는 사각의 영역에 color 색상과 font의 글꼴로 문자열이 출력된다.

다음 cvInitFont 를 사용하여 font를 생성한다.
/* Initializes font structure used further in cvPutText */
CVAPI(void)  cvInitFont( CvFont* font, int font_face,
                         double hscale, double vscale,
                         double shear CV_DEFAULT(0),
                         int thickness CV_DEFAULT(1),
                         int line_type CV_DEFAULT(8));



OpenCV 행렬과 영상에 관련된 기본 연산

 cvAbs : 행렬의 모든 원소들의 절대값을 구한다.
 cvAbsDiff : 두 행렬의 차를 구하고, 결과행렬의 모든 원소들의 절대값을 구한다.
 cvAbsDiffS : 행렬에서 원소 단위로 스칼라값을 빼고, 그 절대값을 구한다.
 cvAcc : 행렬의 값을 더해서 누적한다.
 cvAdd : 두 행렬의 원소끼리 덧셈연산을 수행한다.
 cvAddS : 하나의 행렬과 스칼라값을 원소 단위로 더한다.
 cvAddWeighted : 두 행렬에 가중치를 부여한 후 더한다.
 cvAnd : 두 입력 영상에서 동일한 위치의 픽셀값에 대해 AND연산 수행
 cvAndS : 하나의 행렬과 상수값에 대하여 AND연산을 수행한다.
 cvAvg : 행렬의 모든 원소값의 평균을 구한다.
 cvArgSdv : 행렬의 모든 원소값의 평균과 표준편차를 구한다.
 cvCalcCovarMatrix : n차원 백터들의 집합으로부터 공분산행렬을 계산한다.
 cvCmp : 두 행렬의 모든 원소에 대하여 선택된 비교 연산을 수행한다.
 cvCmpS : 행렬과 스칼라값 사이에 선택된 비교 연산을 수행한다.
 cvConvertScale : 선택적으로 값을 증감하면서 행렬타입을 변경한다.
 cvConvertScaleAbs : 선택적으로 값을 증감하고, 절대값으로 바꾼후 행렬의 타입을 변경한다.
 cvCopy : 행렬의 원소들을 다른 행렬로 복사한다.
 cvCountNonZero : 행렬에서 0이 아닌 원소의 개수를 반환한다.
 cvCrossProduct : 두개의 3차원 백터에 대한 외적을 계산한다.
 cvCvtColor : 행렬의 채널을 하나의 색공간에서 다른 색 공간으로 변환한다.
 cvDet : 정방행렬의 행렬식의 계산한다.
 cvDiv : 두행렬의 원소끼리 나눗셈 연산을 수행한다.
 cvDotProduct : 두 벡터의 내적을 계산한다
 cvEigenVV : 정방행렬의 고유백터와 고유값을 계산한다.
 cvFlip : 선택한 축을 기준으로 행렬을 뒤집는다.
 cvGEMM : 범용적인 행렬의 곱셈연산을 수행한다.
 cvGetCol : 행렬에서 하나의 열을 추출하여 백터형태로 반환한다.
 cvGetDiag : 행렬에서 대각 성분을 백터 형태로 반환한다.
 cvGetDims : 행렬의 차원 수를 반환하고 선택적으로 각 차원의 크기를 알려준다.
 cvGetDimSize : 행렬에서 지정된 차원의 크기를 반환한다.
 cvGetRow : 행렬에서 하나의 행을 추출하여 백터 형태로 반환한다.
 cvGetRows : 행렬에서 여러 개의 인접한 행을 추출하여 행렬 형태로 반환한다.
 cvGetSize : 2차원 행렬의 크기를 CvSize타입으로 반환한다.
 cvGetSubRect : 행렬에서 일부 사각형 영역을 추출한다.
 cvInRange : 행렬의 원소값이 두 행렬로 지정된 범위 안에 존재하는 지를 검사한다.
 cvInRangeS : 행렬의 원소값이 두 스칼라값으로 지정된 범위안에 존재하는 지를 검사한다.
 cvInvert : 정방행렬의 역행렬을 구한다.
 cvMahalonobis : 두 벡터 사이의 마할노비스 거리를 계산한다.
 cvMax : 두 행렬에서 큰 값을 골라서 새 행렬을 만든다.
 cvMaxS : 행렬의 원소값과 스칼라값 중에서 큰값을 골라서 새 행렬을 만든다.
 cvMerge : 여러개의 단일 채널 영상을 모아서 다중 채널 영상을 생성한다.
 cvMin : 두 행렬에서 작은 값을 골라서 새 행렬을 만든다.
 cvMins : 행렬의 원소값과 스칼라값중에서 작은값을 골라서 새 행렬을 만든다.
 cvMinMaxLoc : 행렬 내부에서 최대값과 최소값을 구한다.
 cvMul : 두 행렬의 원소끼리 곱셈 연산을 수행한다.
 cvNot : 행렬의 모든 원소값의 비트를 반전한다.
 cvNorm : 단일 행렬의 전체 놈 또는 두 행렬사이의 거리 놈을 계산한다.
 cvNormalize : 행렬을 정규화하거나 특정 범위로 원소값을 매핑한다.
 cvOr : 두 행렬의 비트 단위 OR연산을 수행한다.
 cvOrS : 한 행렬과 스칼라값 사이의 비트 단위 OR연산을 수행한다.
 cvReduce : 2차원 행렬을 지정한 방법에 의해 백터로 변환한다.
 cvRepeat : 행렬을 타일 형태로 깔아서 새 행렬을 생성한다.
 cvSet : 행렬의 모든 원소를 주어진 값으로 설정한다.
 cvSetZero : 행렬의 모든 원소를 0으로 설정한다.
 cvSetIdentity : 행렬의 대각 성분을 1로, 나머지 원소들은 0으로 설정한다.
 cvSolve : 선형 방적식의 해를 구한다.
 cvSplit : 다중 채널행렬을 여러개의 단일 채널 행렬로 분활한다.
 cvSub : 두 행렬의 원소끼리 뺄셈연산을 수행한다.
 cvSVD : 2차원 행렬의 특이값 분해(SVD)를 수행한다.
 cvSVBkSb : 특이값 역치환을 계산한다.
 cvTrace : 행렬의 대각합을 구한다.
 cvTranspose : 행렬의 행과 열을 서로 교환한 전치행렬을 구한다.
 cvXor : 두 행렬의 원소 사이의 비트단위 XOR연산을 수행한다.
 cvXorS : 행렬과 스칼라 사이의 비트 단위 XOR 연산을 수행한다.
 cvZero : 행렬의 모든 원소를 0으로 설정한다. cvSetZero()와 동일하다.

OpenCV 데이터 타입

점, 좌표
CvPoint : 두 개의 정수형 멤버 x, y
CvPoint2D32f : 두 개의 실수형 멤버 x, y
CvPoint3D32f : 세 개의 실수형 멤버 x, y, z

크기
CvSize : CvPoint 의 사촌격. 정수형 멤버 width, height
CvSize2D32f : 실수형 width, height

사각형, 영역
CvRect : 네 개의 정수형 멤버, x, y, width, height

RGB값
CvScalar : 네개의 double형 실수값을 배열 형태로 가지고 있다. double형 배열 val을 멤버로 가지고 있다.

영상
IplImage : 그레이스케일, 컬러, 4채널 영상 표현 가능. 정수형 또는 실수형 데이터를 저장할 수 있다.


CvMat 행렬의 구조
OpenCV 에는 Vector를 표현하는 데이터 타입이 없다. 그러므로 벡터를 사용하고 싶다면 한 열짜리 행렬을 사용하면 된다.
OpenCV의 행렬의 원소는 단순히 숫자만 되는 것이 아니다.

2차원 행렬을 만드는 함수의 원형은 다음과 같다.
CvMat*  cvCreateMat( int rows, int cols, int type );
// type : CV_<비트수>(S|U|F)C<채널 개수>

OpenCV 시작

Open CV (Open Source Computer Vision) : http://opencv.org/


2017년 2월 현재 OpenCV 3.2 까지 나와있다.

OpenCV API

OpenCV 는 크게 다음과 같이 나눌수 있다.
  1. CV : 기본적인 영상 처리와 고수준의 컴퓨터 비전 알고리즘
  2. MLL : 다양한 통계 분류와 군집화 도구 등의 기계 학습 라이브러리
  3. HighGUI : GUI, 영상과 비디오에 대한 입출력에 관련된 함수들
  4. Core : 기본적인 자료 구조와 알고리즘
 
OpenCV 시작

정지영상 출력
#include "highgui.h"
// OpenCV는 비디오와 카메라 입력 뿐 아니라 다양한 포멧의 영상 파일을 불러올수 있다. 이러한 기능은 HighGUI 라이브러리에 정의가 되어있다. 

int main(int argc, char **argv)
{
      IplImage* img = cvLoadImage("sample.jpg");
      // cvLoadImage()는 파일의 형식을 자동으로 파악하여 영상을 불러오며 메모리공간도 자동으로 할당한다.
      // bmp,dib,jpeg,jpe,png,pbm,pgm,ppm,sr,ras,tiff 등의 영상포멧을 읽을 수 있다.
      // IplImage 타입은 모든 형태의 영상데이터를 표현할 수 있으며 영상 데이터를 조작할 때 주로 사용한다.

      cvNamedWindow("Example1", CV_WINDOW_AUTOSIZE);
      // HighGUI 라이브러리 함수들은 위도우 이름을 이용하여 윈도우를 구분하고 제어한다.

      cvShowImage("Example1", img);

      cvWaitKey(0);

      cvReleaseImage(&img);
      // img에 할당된 메모리 해제.

      cvDestroyWindow("Example1");

      return 0;
}



끝.

OpenCV IplImage

OpenCV CvMat 행렬과 함께 많이 사용하는 IplImage의 구조와 접근방법에 대해 정리한다.


인텔 영상 처리 라이브러리(IPL : Image Processing Library)

데이터 타입(depth)와 채널의 개수(nChannels)의 차이점에 유의한다.
IplImage에서는 두가지를 서로 개별적으로 처리한다. (Mat에서는 두가지 속성을 한꺼번에 다룬다.)

nChannels의 값은 1,2,3,4 가능

widthStep : 한 행이 실제로 차지하고 있는 바이트 수
imageDataOrigin : IPL_ORIGIN_TL(값) --> 좌상단(0), 좌하단(1)

 
IplImage 구조체

typedef struct _IplImage {
  int                  nSize;
  int                  ID;
  int                  nChannels;
  int                  alphaChannel;
  int                  depth;
  char                 colorModel[4];
  char                 channelSeq[4];
  int                  dataOrder;
  int                  origin;
  int                  align;
  int                  width;
  int                  height;
  struct _IplROI*      roi;
  struct _IplImage*    maskROI;
  void*                imageId;
  struct _IplTileInfo* tileInfo;
  int                  imageSize;
  char*                imageData;
  int                  widthStep;
  int                  BorderMode[4];
  int                  BorderConst[4];
  char*                imageDataOrigin;
} IplImage;


void saturate_sv( IplImage* img ) {

  for( int y = 0; y < img->height; y++ )
  {
    uchar* ptr = (uchar*)(img->imageData + y * img->widthStep);
    for( int x=0; x < img->width; x++ ) {
      ptr[3 * x + 1] = 255;
      ptr[3 * x + 2] = 255;
    }
  }
}

int main( int argc, char** argv )
{
  IplImage* img = cvLoadImage( argv[1] ); // 3채널 이미지
  cvNamedWindow("Example1", CV_WINDOW_AUTOSIZE );
  saturate_sv(img);
  cvShowImage("Example1", img );
  cvWaitKey(0);
  cvReleaseImage( &img );
  cvDestroyWindow("Example1");
}

IplImage 구조체의 imageData와 CvMat 구조체의 data 멤버 변수를 이용하여 실제 데이터에 접근하는 방법에는 큰 차이점이 있다.
CvMat 구조체의 data 원소는 어떤 타입의 포인터로 접근할것인지 명시가 필요하다.
imageData는 바이트 타입의 포인터(uchar*)이다.

  IplImage* img_rgb  = cvLoadImage( argv[1] );
  IplImage* img_gry  = cvCreateImage( cvSize( img_rgb->width,img_rgb->height ), img_rgb->depth, 1);

  cvNamedWindow("Example Gray", CV_WINDOW_AUTOSIZE );

  cvShowImage("Example Gray", img_gry );

  cvDestroyWindow("Example Gray");

  cvReleaseImage( &img_rgb);
  cvReleaseImage( &img_gry);














OpenCV CvMat

OpenCV CvMat행렬의 구조와 데이터 접근 방법에 대해 정리한다.

CvMat 행렬의 구조는 다음과 같다.
 
typedef struct CvMat {
    int type;
    int step;
    int* refcount;     // for internal use only
    union {
         uchar* ptr;
         short* s;
         int*    i;
         float* fl;
         double* db;
    } data;
    union {
         int rows;
         int height;
    };
    union {
         int cols;
         int width;
    };
} CvMat;

매트릭스의 행렬의 생성
 
CvMat* cvCreateMat( int rows, int cols, int type );

// Create only matrix header without allocating data
//
CvMat* cvCreateMatHeader( int rows, int cols, int type );

// Initialize header on existing CvMat structure
//
CvMat* cvInitMatHeader(
   CvMat* mat,
   int   rows,
   int   cols,
   int   type,
   void* data = NULL,
   int   step = CV_AUTOSTEP
);

// Like cvInitMatHeader() but allocates CvMat as well.
//
CvMat cvMat(
   int   rows,
   int   cols,
   int   type,
   void* data = NULL
);

// Allocate a new matrix just like the matrix 'mat'.
//
CvMat* cvCloneMat( const cvMat* mat );

// Free the matrix 'mat', both header and data.
//
void cvReleaseMat( CvMat** mat );



행렬 데이터에 접근하기

- 간편한 방법

CV_MAT_ELEM() 매크로를 사용하여 행렬의 원소값 받아오기
  CvMat* mat = cvCreateMat( 5, 5, CV_32FC1 );
  float element_3_2 = CV_MAT_ELEM( *mat, float, 3, 2 );
  printf("Exercise 3_4, matrix created and accessed [3,2]=%f\n",element_3_2);

CV_MAT_ELEM_PTR() 매크로를 사용하여 행렬의 원소값 설정하기
  CvMat* mat = cvCreateMat( 5, 5, CV_32FC1 );
  float element_3_2 = 7.7;
  *( (float*)CV_MAT_ELEM_PTR( *mat, 3, 2 ) ) = element_3_2;

  // below from example ch3_ex3_8.txt
  cvmSet( mat, 2, 2, 0.5000 );
  cvSetReal2D( mat, 3, 3, 0.3300 );

  printf("Exercise 3_5, matrix created and accessed [3,2]=%f, [2,2]=%f, [3,3]=%f\n",CV_MAT_ELEM( *mat, float, 3, 2 ),CV_MAT_ELEM( *mat, float, 2, 2 ),CV_MAT_ELEM( *mat, float, 3, 3 ));

CV_MAT_ELEM() 매크로와 CV_MAT_ELEM_PTR() 매크로는 호출할 때마다 주소값 계산을 반복한다.
매번 행렬의 데이터 영역 시작 주소를 찾고, 알고싶은 원소의 주소까지 오프셋을 계산한 후 시작 주소에 더해서 해당 원소에 접근하는 작업을 반복한다.



- 엄격한 방법

Example 3-6. Pointer access to matrix structures 
  uchar* cvPtr1D(
      const CvArr* arr,
      int          idx0,
      int*         type = NULL
   );
   uchar* cvPtr2D(
      const CvArr* arr,
      int          idx0,
      int          idx1,
      int*         type = NULL
   );
   uchar* cvPtr3D(
      const CvArr* arr,
      int          idx0,
        int          idx1,
        int          idx2,
        int*         type = NULL
    );
    uchar* cvPtrND(
        const CvArr* arr,
        int*         idx,
        int*         type            = NULL,
        int          create_node     = 1,
        unsigned*    precalc_hashval = NULL
    );

Example 3-7. CvMat and IplImage element functions
double cvGetReal1D( const  CvArr* arr, int idx0 );
double cvGetReal2D( const  CvArr* arr, int idx0, int idx1 );
double cvGetReal3D( const  CvArr* arr, int idx0, int idx1, int idx2 );
double cvGetRealND( const  CvArr* arr, int* idx );
CvScalar cvGet1D( const CvArr*  arr, int idx0 );
CvScalar cvGet2D( const CvArr*  arr, int idx0, int idx1 );
CvScalar cvGet3D( const CvArr*  arr, int idx0, int idx1, int idx2 );
CvScalar cvGetND( const  CvArr* arr, int* idx );

Example 3-8. Set element functions for CvMat or IplImage.  
// Also see usage example in ch3_ex5.cpp
void cvSetReal1D( CvArr* arr, int idx0, double value );
void cvSetReal2D( CvArr* arr, int idx0, int idx1, double value );
void cvSetReal3D(
   CvArr* arr,
   int idx0,
   int idx1,
   int idx2,
   double value
);
void cvSetRealND( CvArr* arr, int* idx, double value );
void cvSet1D( CvArr* arr, int idx0, CvScalar value );
void cvSet2D( CvArr* arr, int idx0, int idx1, CvScalar value );
void cvSet3D(
   CvArr* arr,
   int idx0,
   int idx1,
   int idx2,
   CvScalar value
);
void cvSetND( CvArr* arr, int* idx, CvScalar value );

- 적절한 방법
float sum( CvMat* mat ) {
  float s = 0.0f;
  for( int row = 0; row < mat->height; row++ )
  {
    float* ptr = mat->data.fl + row * mat->step / 4; // Float형 포인터인 경우
    // float* ptr = (float*)(mat->data.ptr + row * mat->step); // 그냥 포인터인 경우
    for( int col = 0; col < mat->width; col++ )
    {
      s += *ptr++;
    }
  }
  return( s );
};
OpenCV에서 step은 행렬의 행 크기를 바이트 단위로 나타낸다.
그러므로 다음행으로 넘어가기 위해서는 해당 포인터에 step을 더해줘야한다.




2017년 2월 13일 월요일

OpenCV Test 프로젝트 (Visual Studio 2013, x64)

OpenCV 프로젝트 (Visual Studio 2013, x64)
- /Images 경로의 이미지 자동 링크
- GLM 링크
- 설명서 Readme 첨부

링크된 OpenCV 모듈
- opencv_calib3d2412d.dll
- opencv_core2412d.dll
- opencv_features2d2412d.dll
- opencv_highgui2412d.dll
- opencv_imgproc2412d.dll
- opencv_ml2412d.dll

파일

Generate_Project_x64.bat 실행

시작프로젝트 설정

실행



2016년 12월 30일 금요일

OpenCV 정리

CvPoint : 두 개의 정수형 멤버 x, y
CvPoint2D32f : 두 개의 실수형 멤버 x, y
CvPoint3D32f : 세 개의 실수형 멤버 x, y, z

CvSize : CvPoint 의 사촌격. 정수형 멤버 width, height
CvSize2D32f : 실수형 width, height

CvRect : 네 개의 정수형 멤버, x, y, width, height

CvScalar : 네개의 double형 실수값을 배열 형태로 가지고 있다. double형 배열 val을 멤버로 가지고 있다.

CvArr --> CvMat --> IplImage

CvMat*  cvCreateMat( int rows, int cols, int type );
type : CV_<비트수>(S|U|F)C<채널 개수>

* IplImage 의 데이터 접근
IplImage 구조체는 ImageData라는 char**형 이미지 저장 장소를 지원합니다.

위의 예를 따르자면 image->ImageData 와 같이 접근 가능합니다.

1)컬러 영상 : 일반적으로 32bit 이미지로, 각 Blue, Green, Red 영역 순으로 기록
for(int i = 0; i < image->widthstep; i += image->nChannels)
     for(int j = 0; j < image->height; j++)
     {
          image->ImageData[i + j * image->widthstep) = xxxx; // blue 영역
          image->ImageData[i + j * image->widthstep + 1) = xxxx; // green 영역
          image->ImageData[i + j * image->widthstep + 2) = xxxx; // red 영역
     }

2)1채널 영상
for(j = 0; j < image->height; j++)
     for(i = 0; i < image->width; i++)
          tmpBuf[j * image->width + i] = (unsigned char) image->imageData[j * image->widthStep + i];

*tmpBuf( unsigned char* tmpBuf = new unsigned char [nWidth * nHeight];)는 영상 값

lPlImage *image = cvLoadImage(filename) ;

1 channel gray 영상의 경우:
int index = x + y*image->widthStep ;
unsigned char value = image->imageData[index] ;

3 channel RGB 영상의 경우:
int index = 3*x + y*widthStep ;
unsigned char B = image->imageData[index] ;
unsigned char G = image->imageData[index+1] ;
unsigned char R = image->imageData[index+2] ;

또 다른 방법은 OpenCV가 제공하는 cvGet 함수를 이용하는 것이다. 1채널 Gray 영상에는 cvGetReal2D() 함수를, 다채널 영상에는 cvGet2D()를 사용한다. cvGetReal2D 함수는 double 값을, cvGet2D는 CvScalar 구조체에 각 채널별로 값을 저장해서 리턴한다. 함수의 인자에 x와 y의 순서에 주의한다.

1 channel gray 영상의 경우
double value = cvGetReal2D(image, y, x) ;

3 channel RGB 영상의 경우
CvScalar value = cvGet2D(image, y, x) ;
double B = value.val[0];
double G = value.val[1];
double R = value.val[2];

* CvMat 의 데이터 접근
1. at 접근 방법
Mat image(ROW, COL, CV_TYPE);
image.at<DATA_TYPE>(WANT_ROW, WANT_COL);
- ROW : 행
- COL : 열
- CV_TYPE : 데이터 타입(예: CV_8UC3 = 8 bit 3 channels)
- DATA_TYPE : Mat 생성시 데이터 타입(예: float, usigned char)
- WANT_ROW : 접근하기 원하는 행
- WANT_COL : 접근하기 원하는 열

2. ptr 접근 방법
Mat image(ROW, COL, CV_TYPE);
image.ptr<DATA_TYPE>(WANT_ROW, WANT_COL); (포인터형으로 반환하기때문에 이에 맞는 포인터 변수를 사용해서 접근해야함)
- ROW : 행
- COL : 열
- CV_TYPE : 데이터 타입(예: CV_8UC3 = 8 bit 3 channels)
- DATA_TYPE : Mat 생성시 데이터 타입(예: float, usigned char)
- WANT_ROW : 접근하기 원하는 행
- WANT_COL : 접근하기 원하는 열

3. data 접근 방법
Mat image(ROW, COL, CV_TYPE);
DATA_TYPE* data = (DATA_TYPE*)image.data;
data[WANT_ROW *  image.cols + WANT_COL]
- ROW : 행
- COL : 열
- CV_TYPE : 데이터 타입(예: CV_8UC3 = 8 bit 3 channels)
- DATA_TYPE : Mat 생성시 데이터 타입(예: float, usigned char)
- WANT_ROW : 접근하기 원하는 행
- WANT_COL : 접근하기 원하는 열

* IplImage 관련,  생성과 해제
//생성
IplImage *srcimg_R  = NULL;
srcimg_R  = cvCreateImage(cvSize(m_width,m_height), 8, 3);   //cvSize(640,480) 같은 것도 됨
srcimg_R  = cvCreateImage(cvGetSize(src_color), 8,3);  //요것도 됨.  다른 IplImage 사이즈 받아와서

//요런것도 됨
CvSize img_size;
img_size.height = ImageHeight;
img_size.width  = ImageWidth;

IplImage* BGR_image = cvCreateImage(img_size, IPL_DEPTH_8U, 3);

//이미지 복사하기
src = cvCloneImage(src_img);  //src가 비어있어야 함.  아니면 메모리 계속 쌓인다
cvCopy(src_img, src_img2);

//컬러 이미지 각 소스별로 분리하거나 합치거나 할 때
cvCvtPixToPlane( src_hlsimg, Hue, Intensity, Saturation, NULL );  //HLS 이미지 각 속성별로 나눔
cvCvtPlaneToPix( Hue, Intensity, Saturation, NULL, Equ_hls );  //다시 합친다

//0으로 초기화
cvZero(src_img);

//해제
if(srcimg_R)
  cvReleaseImage(&srcimg_R);

* 이미지 불러오기, 저장하기
//불러오기
TmpLImg = cvLoadImage("img_InElevator_1_L.bmp");    //간단하게, TmpLImg는 IplImage
//복잡하게
if ((TmpLImg = cvLoadImage("img_InElevator_1_L.bmp")) == 0)  // load left image
{
   printf("%s", "left image file read has failed!! \n");
   return 0;
}

//저장하기
char file_name[20];
sprintf(file_name,"img_R.bmp");            //파일이름 맹글기
cvSaveImage(file_name,srcimg_R);   //srcimg_R 이라는 IplImage를 저장

* 창 만들고 닫기 등등
//생성
cvNamedWindow("Right Original", CV_WINDOW_AUTOSIZE);

//창 움직이기 - uv 좌표로 배치함
cvMoveWindow("source_color",610,0);

//보이기
cvShowImage( "Right Original", srcimg_R );

//창 닫기
cvDestroyAllWindows();  //모든 OpenCV 윈도우 닫기

//특정 윈도우만 닫기
cvDestroyWindow("Right Original");

* canny edge detect
IplImage *canny_R   = NULL;
canny_R    = cvCreateImage(cvSize(m_width,m_height), 8, 1);
...
cvCvtColor(srcimg_R, grayimg_R, CV_BGR2GRAY);   //원본 컬러이미지를 흑백으로 변환하고

cvCanny( grayimg_R, canny_R, 40, 130, 3 );  //그 흑백이미지를 캐니로 변환

* Harris edge detector
IplImage *harris = cvCreateImage(cvSize(WIDTH,HEIGHT), IPL_DEPTH_32F, 1);
cvCornerHarris(src_gray, harris, 3, 9, 0.07);

* Smoothing 연산
//Gaussian filtering
cvSmooth(src, Gaussed_image, CV_GAUSSIAN, 3);  //src는 gray영상, 두번째 매개변수도 IplImage임, 3은 마스크 크기

//DoG(Difference of Gaussian)
cvSmooth(src_gray, GaussImg1, CV_GAUSSIAN, 3);
cvSmooth(src_gray, GaussImg2, CV_GAUSSIAN, 27);
cvSub(GaussImg1, GaussImg2, dst);

* 키 입력
pressed_key=cvWaitKey(0) ;
  if(pressed_key=='q')    //q 키가 누르면 빠져나가기
    break;
  else if(pressed_key=='c')  //캡쳐 키 누르면 캡쳐
  {
    timer=time(NULL);  //현재시간저장
    t=localtime(&timer); //지역시간
    sprintf(file_name,"img_%4d%02d%02d%02d%02d%2d.bmp",t->tm_year + 1900, t->tm_mon +1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);

    cvSaveImage(file_name, src_color);

    //확인메시지출력
    printf("%s file saved is success!!\n",file_name);
  }

* 화면에 글자 쓰기
char s_output_result[50];
CvFont font;
...
sprintf(s_output_result,"sum vector x:%1.3f  y:%1.3f",sumvector_x,sumvector_y );    //우선 sprintf로 문자열 생성
cvInitFont(&font, CV_FONT_HERSHEY_SIMPLEX|CV_FONT_ITALIC, 0.5, 0.5, 0, 1);  //이런 저런 설정.
cvPutText(src_color, s_output_result ,cvPoint(15,20),&font,cvScalar(0,255,0));   //cvPoint로 글자 시작 위치 설정(uv)
//void cvInitFont(CvFont* font, int font_face, double hscale, double vscale, double italic_scale, int thickness)

* 트랙바 생성
int hue_threshold=139;  //Hue 값의 피부색 threshold

cvNamedWindow( "HLS_image", CV_WINDOW_AUTOSIZE );
cvCreateTrackbar("Hue","HLS_image",&hue_threshold,255, NULL );  //중요한 부분은 요거

* 마우스 입력
void on_mouse( int event, int x, int y, int flags, void* param );
......

cvSetMouseCallback( "LKTracker", on_mouse, NULL );
......

void on_mouse( int event, int x, int y, int flags, void* param )
{
    if( !image )
        return;

    if( image->origin )
        y = image->height - y;

    if( event == CV_EVENT_LBUTTONDOWN )
    {
        pt = cvPoint(x,y);
        add_remove_pt = 1;
    }
}

* IPP 검사
    const char* libraries;
    const char* modules;
    cvGetModuleInfo( 0, &libraries, &modules );
    printf("Libraries: %s\nModules: %s\n", libraries, modules );