ASCII (American Standard Code for Information Interchange)
ASCII는 최초의 문자열 인코딩이다. 7비트로 구성이 되어있으며 영어 문자, 숫자, 특수문자, 기호 등 128개의 문자를 표현할 수 있다.
영어만을 고려하여 만들어졌기 대문에 다른 언어의 표현이 불가능하다.
여기서 다른 언어를 지원하기위해 만들어진 인코딩이 ANSI이다.
ANSI (American National Standard Institute)
ANSI는 8비트로 구성되어 있으며 256개의 문자를 표현할 수 있다.
ANSI는 ASCII의 확장판으로 이해하면 된다.
앞 7비트는 ASCII와 동일하고 맨뒤의 1비트를 이용하여 다른 언어의 문자를 표현한다.
그런데 새로 추가된 128개 문자로는 모든 언어의 문자를 표현할 수 없다.
그래서 생긴 개념이 CodePage 이다.
각 언어별로 Code 값을 주고 Code 마다 다른 문자열 표를 의미하도록 약속을 했다.
쉽게 생각하면 아래와 같다.
ANSI(8bit) = ASCII(7bit) + CodePage(1bit)
영어 외의 다른 언어를 사용할 경우 ANSI는 CodePage를 동일하게 맞춰주면 된다.
CodePage가 다를 경우 의도와 다른 결과가 나올 수 있다.
EUC-KR
EUC-KR은 한글 지원을 위하여 유닉스 계열에서 나온 완성형 코드 조합이다.
완성형 코드란 완성된 문자 하나하나마다 코드 번호를 부여한 것으로 반대되는 개념으로는 조합형 코드가 있다. 예를들면 한글의 자음과 모음 각각에 코드 변호를 부여한 후 초성, 중성, 종성을 조합하여 하나의 문자를 나타내는 방식이다.
EUC-KR은 ANSI를 한국에서 확장한 것으로 외국에서는 지원이 안될 가능성이 높다.
CP949
CP949는 한글 지원을 위해 윈도우즈 계열에서 나온 확장 완성형 코드 조합이다.
EUC-KR은 2byte의 완성형 코드로 표현할 수 있는 완성된 문자의 수의 한계가 있었다.
그래서 MS에서 EUC-KR을 개선, 확장하여 만든것이 CP949이다. 기본적으로 EUC-KR과 호환이 되며, EUC-KR에서 표현이 되지 않는 문자는 조합을 하여 표현한다.
MS에서 만들었다고 하여 MS949라고 부르기도 한다.
UTF-8 (Universal coded character set + Transformation Format 8 bit)
UTF-8은 유니코드를 위한 가변 길이 문자 인코딩, 즉 멀티바이트 방식 중 하나로 ANSI의 단점을 보완하기 위해 만들었다. ANSI는 다국어를 지원하기 위하여 CodePage 정보를 미리 알고 있어야 한다는 단점이 있는데 UTF-8은 멀티바이트의 개념을 사용하여 하나의 Character set에 거의 모든 문자를 담았다.
멀티바이트란 표현해야 하는 문자에 따라 글자 크기를 가변으로 변경하여 사용하는 것을 말한다.
ANSI와 비교를 하면 ANSI는 1byte 고정바이트를 사용하여 표현을 하지만 UTF-8은 멀티바이트로 1~4byte를 가변적으로 사용하도록 되어있다.
첫 128자는 ASCII 코드 값으로 ANSI와 UTF-8이 동일하다. 그래서 영어를 사용할 경우 1byte만 사용한다.
중동지역 언어 또는 많은 유럽 언어는 2byte를 사용하며, 한국, 중국, 일본 등 동아시아권 언어는 3byte이상을 사용한다. UTF-8은 매우 일반적인 인코딩 방식이지만 3byte 이상의 문자를 사용할 경우에는 비효율적일 수 있다.
UTF-16
16bit 기반으로 저장하는 UTF-8의 변형이라고 볼 수 있다.
한글의 경우 UTF-8로 저장을 할 경우 3byte가 필요한데 UTF-16으로 저장을 하면 2byte면 되기 때문에 용량의 이점이 있다고 한다.
그러나 경우에 따라 2byte 이상을 사용하는 경우도 있고, 엔디안 처리를 고려함에 따른 복잡성 증대나, ANSI와 호환이 되지 않는다는 단점이 있다.
UNICODE
유니코드는 전세계의 모든 문자를 컴퓨터에서 일관되게 표현할 수 있도록 고안된 코드 조합이다.
여기서 주의할 점은 유니코드는 '인코딩'이 아니라는 것이다. 유니코드는 전세계 거의 모든 문자를 2byte 숫자로 1대1 매핑시키는 '방식'을 말하는 것이며 유니코드를 표현하는 여러가지 '인코딩' 방식들이 존재하는 것이다. UTF-8, UTF-16 등이 그렇다.
유니코드는 목록을 통해 매핑 테이블을 확인할 수 있다.
BOM (Byte Order Mark)
빅엔디안과 리틀엔디안은 메모리 저장방식의 차이.
AC 00 이라는 값을 저장할 때 빅 엔디안은 AC 00 리틀엔디안은 00 AC 으로 저장한다.
이러한 저장방식의 차이를 다른 프로그램에게 설명하기 위해서 특정한 값이 필요하고, 유니코드 문서의 첫 번째에 특정한 값을 삽입하여주는것을 BOM이라 한다.
- UTF-8 BOM : EF BB BF
- UTF-16 BE BOM : FE FF
- UTF-16 LE BOM : FF FE
BOM으로 인한 문제
UTF-8 은 저장 방식의 차이가 없기 때문에 BOM이 없어도 되지만, 몇몇 프로그램에서는 BOM을 삽입하고 있다. 이로 인하여 파일을 읽을 때 첫번째 문자가 깨지는 현상이 발생하게 된다.Notepad++ 같은 프로그램을 이용할 때에는 저장할 때 BOM 없이 저장을 하도록 설정하여 주는것이 좋다.
Hello World
2020년 6월 8일 월요일
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);
나만의 3차원 스캐너 만들기 (2)
- 주제 : 카메라 2개와 Line 레이저를 사용한 광학식 3차원 스캐너
- 개발 범위 : 카메라로 데이터 획득 --> Reconstruction (3D point cloud)
앞에서 레이저 포인터를 이용하여 3차원 데이터를 획득 하였을 때의 단점을 계선하기 위해
레이저 포인터를 Line 레이저로 바꿔본다.
레이저 포인터를 사용하면 대응점 검출을 매우 간단한 방법으로 할 수 있었다.
Line 레이저를 사용하면 왼쪽 카메라에서 대응되는 점을 오른쪽에서 어떻게 찾을 수 있을까?
두 카메라의 위치를 알고있다면 왼쪽 카메라에서의 한 점은 두 카메라의 위치에 따른 제약에 따라 오른쪽 카메라에서 선으로 투영이 된다.
그 내용은 Epipolar Geometry에 대하여 검색을 해보면 알 수 있다.
Epipolar geometry is the geometry of stereo vision. When two cameras view a 3D scene from two distinct positions, there are a number of geometric relations between the 3D points and their projections onto the 2D images that lead to constraints between the image points. These relations are derived based on the assumption that the cameras can be approximated by the pinhole camera model.
출처 : Wikipedia https://en.wikipedia.org/wiki/Epipolar_geometry
위키피디아에서 에피폴라기하에 대하여 검색을 해보면 위와같이 그림이 잘 나와있다.
왼쪽 뷰에서 3차원 객체 X를 바라볼때 X가 OL로 이어지는 선으로 객체가 투영이 되는데 이 선은 오른쪽 뷰에서 빨간선으로 보이게된다.
왼쪽 뷰의 한 픽셀에 대한 오른쪽 뷰에서의 선을 계산하기 위해서는 Fundamental matrix를 사용하는 것이 편하다.
F는 기존에 수행했던 cv::stereoCalibration에서 결과 값으로 계산을 해준다.
C++: double stereoCalibrate(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints1, InputArrayOfArrays imagePoints2, InputOutputArray cameraMatrix1, InputOutputArray distCoeffs1, InputOutputArray cameraMatrix2, InputOutputArray distCoeffs2, Size imageSize, OutputArray R, OutputArray T, OutputArray E, OutputArray F, TermCriteria criteria=TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 1e-6), int flags=CALIB_FIX_INTRINSIC )
위의 이미지에서. 왼쪽 뷰에서 Line 레이저에 의해 인식된(빨간색) 점 중 하나를 고르면 오른쪽 뷰에서 주황색 선을 따라 투영이 된다.
이때 Line 레이저를 통해 인식된 빨간 선과 교차하는 부분을 계산하면 대응점을 쉽게 구할 수 있다.
다시 Line 스캔의 순서를 정리해보면
- Line을 인식한다.(빨간색)
- Epipolar 제약에 따른 선을 계산하여 교점을 계산한다. (=대응점 계산)
- 대응점에 대한 Ray를 계산한다.
- 두 Ray의 교점을 계산한다.(triangulation)
- 위 [2~5]과정을 모든 Pixel을 반복한다.
- 결과
레이저 Line에 대한 3차원 데이터를 매우 빠르게 얻을 수 있다.
나만의 3차원 스캐너 만들기 (1)
- 주제 : 카메라 2개와 레이저 포인터를 사용한 광학식 3차원 스캐너
- 개발 범위 : 카메라로 데이터 획득 --> Reconstruction (3D point cloud)
- 구성품 : 카메라와 렌즈 2 set, 레이저 포인터, 카메라를 고정할 지지대
- Calibration (cv::stereoCalibrate )
두 개의 카메라에서 활영한 영상에서 각각의 대응점을 찾고, 두 카메라 사이의 정보가 있으면 객체의 실제 3차원 좌표를 계산 할 수 있다.
일단 카메라 사이의 정보가 필요하다. OpenCV의 Streo Calibration을 수행하여 두 카메라 간의 기하학적 정보 R, T를 계산한다.
- 대응점(corresponding points) 검출
왼쪽 카메라에서 보이는 한 점이 오른쪽 카메라에서 어떤 점일까?
사람은 매우 똑똑하기 때문에 쉽게 대응점을 찾지만 컴퓨터가 카메라 영상에서 일치하는 점을 찾기는 어렵다.
그래서 3차원 스캐너에서는 보통 여러종류의 패턴을 사용한다.
일단 주위에서 쉽게 구할 수 있는 레이저 포인터를 사용해보자.
레이저 포인터를 사용하여 손쉽게 대응점을 찾을 수 있었다.
각 카메라의 영상에서 레이저 포인터의 중심점(x, y)를 얻는다.
여러가지 방법이 있을 수 있겠지만.. 나는 cv::Canny --> cv::findContours --> cv::fitEllipse 를 사용하여 중점을 계산하였다.
- 대응점에 대한 Ray 계산
초점거리(f)와 주점(cx, cy) 그리고 전 단계에서 알아낸 대응점 좌표를 사용하여 카메라의 중심에서부터의 Ray를 계산한다.
초점거리와 주점은 카메라 calibration을 수행하면 얻을 수 있다. (cv::calibrateCamera 또는 cv::stereoCalibrate)
calibration을 하지 않는다면 렌즈의 초점거리를 초점거리(f)로, 이미지 센서의 중점을 주점(cx, cy)로 사용 할 수 있다.
정밀도는 매우 떨어진다.
정밀도는 매우 떨어진다.
- 두 Ray의 교점 계산 (triangulation)
우리는 한 변과 양 끝의 각을 알면 유일한 삼각형을 만들 수 있다는것을 알고있다.
두 카메라의 기하학적인 관계(R|t)를 알고 있으며 양 끝에서의 Ray(크기와 방향을 가지는 vector)를 알고 있으므로 유일한 삼각형을 만들어 낼 수 있다.
두 벡터의 교점을 구하면 레이저 포인터가 가리키는 한 점에 대한 3차원 좌표를 계산 할 수 있다.
- 결과
레이저 포인터가 가리키는 한 점에 대한 3차원 데이터를 매우 빠르게 얻을 수 있다.
하지만 넓은 영역을 측정하기 힘들다.
피드 구독하기:
글 (Atom)