이론
화소 점 처리(pixel point processing)이란 원 화소의 값이나 위치를 바탕으로 단일 화소 값을 변경하는 기술이다.
a 는 반전 영상, b는 이진화 영상, c는 구간별 밝기 변화를 다르게 한 영상이다.
a의 x절편과 y절편을 255(픽셀 최댓값)이라고 할 때, a는 y = -x + 255 라는 직선의 방정식으로 표현할 수 있다. 255에서 원래 화소가 가지는 값(x)를 빼는 것이므로 a는 반전 영상을 나타내기 위한 수식으로 해석할 수 있다.
b는 어떤 지점을 기준으로 출력 명암도가 최소(0)에서 최대(255)로 뛴다. 이는 어떤 기준 이하의 색은 검정색으로, 이상의 색은 흰색으로 이진화를 하겠다는 의미이다.
c는 p1과 p2를 기준으로 세 개의 직선의 방정식이 연결되어 있다. 기울기가 1보다 작은 구간 [0:p1) 에서는 출력 영역이 좁기 때문에 밝기가 압축되고 기울기가 1보다 큰 구간 [p1:p2) 는 출력 영역이 넓기 때문에 영상이 선명하다. 따라서 c 의 의미는 [0:p1) 에 포함되는 색상은 희미하게 표현하고, [p1 : p2] 에 해당하는 색상은 선명하게 표현하겠다는 것이다.
실습
0. 이미지 가져오기 : 앞으로 나오는 실습을 위해 이미지를 가져오는 방법과 자주 사용하는 class에 대해 알아보자.
#include <opencv2/core.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>
using namespace cv;
using namespace std;
int main(int argc, char** argv) {
Mat image;
char* imageName = argv[1];
// image 정보 가져오기
image = imread(imageName, IMREAD_COLOR);
// original이라는 이름의 버퍼 만들기
namedWindow("original");
//가져온 image를 original 버퍼에 보여주기
imshow("original", image);
// 다른 키 누를때까지 무한대기
waitKey(0);
return 0;
}
- 헤더파일은 어떤 파일이 뭐를 위해 사용되는지 일일이 모르겠다. 그냥 항상 기본으로 복붙해놓고 사용한다.
- opencv를 사용할 때는 imshow, namedWindow, imread 등 cv에 포함된 명칭들이 많으므로 using namespace cv를 해주면 편하다.
- Mat은 opencv에서 항상 사용되는 이미지 픽셀을 저장할 행렬이다. mat에 이미지를 저장하면 이미지의 각 픽셀에 접근할 수 있다.
- imread는 영상 정보를 가져오는 함수다. Mat img = imread("입력파일 이름", color flag) 로 사용된다. 위에서는 flag를 IMREAD_COLOR로 사용했는데, 이는 1로 대체될 수도 있고, 이미지를 컬러로 가져오겠다는 의미이다. 이 외에도 IMREAD_GRAYSCALE(=0)이 많이 사용된다.
- namedWindow는 생성할 윈도우 창의 정보를 지정하는 것이다. namedWindow("윈도우 이름", size flag) 로 사용되며, "윈도우 이름"으로 새로운 창이 생성되고, size flag는 생략하면 WINDOW_AUTOSIZE가 디폴트로 들어간다.
- imshow는 이미지를 윈도우창에 보여주는 함수다. imshow("윈도우이름", 영상데이터 버퍼)로 사용되고, 위에는 사용되지 않았지만 destroyAllWindows는 모든 HighGUI windows를 없애준다.
- waitkey(n)는 n msec. 만큼 임의의 키 입력을 대기하는 함수이다. waitKey(0)는 키입력까지 무한 대기를 의미한다. 또는 char ch = waitKey(10); if(ch==27) break; 로 사용하기도 하는데, 이는 ascii코드가 27인 키(ESC)가 눌릴 때까지 대기하라는 의미이다.
- 위 코드는 실행하기 위해 cmd창에서 코드의 실행 파일이 있는 위치에서 "실행파일명 이미지명"을 입력한다.
1. 반전 영상, 반전 화소값 : 이론에서 a에 해당하는 그래프, y = 255 - x
#include <opencv2/core.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>
using namespace cv;
using namespace std;
int main(int argc, char** argv) {
Mat image, result;
// image 정보 가져오기
char* imageName = argv[1];
image = imread(imageName, IMREAD_COLOR);
result = image.clone();
if (image.empty())
{
cout << "Could not open or fine the image" << endl;
return -1;
}
// 버퍼 만들기
namedWindow("Original image", WINDOW_AUTOSIZE);
//가져온 image를 버퍼에 보여주기
imshow("Original image", image);
// 반전 영상 생성
for (int i = 0; i < image.rows; i++)
{
for (int j = 0; j < image.cols; j++)
{
result.at<Vec3b>(i, j)[0] = 255 - image.at<Vec3b>(i, j)[0];
result.at<Vec3b>(i, j)[1] = 255 - image.at<Vec3b>(i, j)[1];
result.at<Vec3b>(i, j)[2] = 255 - image.at<Vec3b>(i, j)[2];
}
}
namedWindow("Processed image");
imshow("Processed image", result);
// 다른 키 누를때까지 무한대기
waitKey(0);
destroyAllWindows();
return 0;
}
- clone : result = image.clone(); 에서 clone는 복제를 의미한다. mat의 함수 중에 하나로 result에 image를 copy하겠다는 의미이다.
- <Vec3b>(x, y) : 데이터 타입 중 하나로, <cv::Vec3bb/s/i/f/d> 로 쓸 수 있다. 픽셀 (x,y)에 대한 3차원 벡터를 의미하고, b = byte, s = short, i = integer, f = float, d = double types를 의미한다. RGB 컬러 영상의 경우 3개의 채널이 존재한다. 어떤 색상의 RGB 값이 (100, 50, 255)라고 해보자. 그렇다면 이 픽셀에는 Red 성분, Green 성분, Blue 성분이 각각 저장되어야 한다. 따라서 Red 값을 저장하는 벡터(채널) 한 개, Blue 값을 저장하는 채널 한 개, Green 값을 저장하는 채널 한 개 총 3개의 채널이 필요한 것이다. 또한 화소값의 범위가 0부터 255로 총 256개이므로 byte를 사용한다. 따라서 <Vec3b>가 되는 것!
- 행렬 Mat에 저장된 image 픽셀 각각에 접근하는 방법은 "at"이다. 사용법은 cv::Mat::at<data type>(pointer of location) 이다. 위에서는 result.at<Vec3b>(i, j)[0] = 255 - image.at<Vec3b>(i, j)[0]; 으로 사용되었다. opencv에서 rgb 순서는 blue green red 순이다. 따라서 0번째 채널은 blue 채널을 의미한다. 위의 식은 y = 255 - x 에서 y에 결과픽셀을 x에 현재 현재픽셀을 대입한 것이다. 직역하면 result 행렬의 0번째 채널의 (i, j) 번째 픽셀에 255에서 image의 0번째 채널의 (i, j) 번째 픽셀의 화소값을 뺀 값을 저장하라는 의미다.
- 실행 결과
2. 이진화 영상 : 이론에서 b에 해당한다. 특정값을 기준으로 0과 255 두 개의 화소값만 벡터에 저장된다.
#include <opencv2/core.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>
using namespace cv;
using namespace std;
int main(int argc, char** argv) {
//cmd창에서 1번째로 입력되는 값을 이미지로 받는다 0번째는 Project1.exe
Mat image = imread(argv[1]);
Mat result = image.clone();
int Thres;
cout << "*Enter threshould value [0~255] : ";
cin >> Thres;
for (int i = 0; i < image.rows; i++)
{
for (int j = 0; j < image.cols; j++)
{
if (image.at<Vec3b>(i, j)[0] > Thres)
{
result.at<Vec3b>(i, j)[0] = 255;
}
else
{
result.at<Vec3b>(i, j)[0] = 0;
}
if (image.at<Vec3b>(i, j)[1] > Thres)
{
result.at<Vec3b>(i, j)[1] = 255;
}
else
{
result.at<Vec3b>(i, j)[1] = 0;
}
if (image.at<Vec3b>(i, j)[2] > Thres)
{
result.at<Vec3b>(i, j)[2] = 255;
}
else
{
result.at<Vec3b>(i, j)[2] = 0;
}
}
}
// 버퍼 만들기
namedWindow("Original image");
namedWindow("Processed image");
//가져온 image를 버퍼에 보여주기
imshow("Original image", image);
imshow("Processed image", result);
// 다른 키 누를때까지 무한대기
waitKey(0);
return 0;
}
- 조건문으로 입력받은 Thres 값 초과인 화소는 255로 바꾸고, Thres 값 이하인 화소값은 0으로 바꿔서 모든 화소값이 0 또는 255가 되도록 바꾸는 것이다. (blue, green, red)에 각각 0 또는 255의 값이 들어가므로 이진화 결과 표현될 수 있는 색상은 2*2*2 = 8 가지이다.
- 다음은 Thres 값을 150으로 입력한 수행결과이다.
(+c의 그래프는 2(b그래프)에서 구간 값을 다르게 받거나 변수를 하나 추가해서 조건문을 수정해주면 같기 때문에 생략)
'개인 공부 > 영상처리 OpenCV' 카테고리의 다른 글
[영상처리][C++][opencv] 2. 화소 점 처리 실습 :: seoftware (0) | 2020.03.08 |
---|---|
[영상처리][OpenCV] 영상처리 image processing 이란? :: seoftware (0) | 2020.03.08 |
댓글