반응형

 

 

지식 없이 완전 처음부터 하니.. 틀린정보가 많습니다..

 

https://cheongpark.tistory.com/54

 

SCARA 로봇 제작하기 Pt. 6 (제작 - 끝)

이제.. 다 조립했으니.. 보드에 선을 연결해야하는데.. 제일 헷갈린 부분이 여기다.. 최대한 찾아서 끼우긴 했지만.. 계속 잘 못 끼웠었었다.. 그러다가 결국엔 끼웠지만.. 저 사진에 보이는 것 처

cheongpark.tistory.com

여기에서 로봇을 모두 제작했었다.

그런데 한가지 문제가 있다.. 로봇을 제어할 프로그램이 필요한데.. 예제에서 제공하는 프로그램은 도무지 알 수도 없고.. 여러 버그도 있고 잘 되지 않는 문제들이 있다.. 

그래서 그냥 처음부터 프로그램을 제작하는 것을 목표로 잡았다..

 

구상

일단 GUI 프로그램으로 만들 예정인데..

나에게는 웹페이지로 GUI를 구현하는 방법, 프로그램으로 GUI를 구현하는 방법 2가지가 있었는데..

난 여기에서 프로그램으로 GUI를 만드는걸로 선택했다.

이유는 프로그램으로 GUI를 만들어 본적이.. 예전에 OpenCV로 비슷하게나마 만들어본게 끝이고.. 제대로 만들어본 적은 없고.. C++를 연습하기 위해서이다.

일단.. C++로 프로그램을 제작한다면.. MFC나.. WinRT 이런게 있는데.. 나는 나중을 생각해서 크로스 플랫폼이 지원되는 WinRT로 정했다.

 

그래서 Visual Studio에 WinRT 프로그램을 제작하기 위한 설치 파일을 설치한 후 프로젝트를 만들었다.

 

그 후 일단 구상을 했는데

대충 이런 느낌으로 제작하는 것이다. 메인 그리드 부분에선 Inverse Kinematics로 로봇의 암을 제어하고 오른쪽 설정 창에선 집게를 펼지 말지 이런거를 세팅하고, 왼쪽에선 높이로 로봇의 Z축을 제어하는 곳으로 정했다.

또한 아래쪽의 검은색 저부분은 각각의 세팅 값이다. 설정 창에서 저장버튼을 눌르면 아래에 스택이 쌓이고 각 스택을 눌르면 그 때 저장했던 값들이 다시 나오게 되는 프로그램을 제작하기로 했다.

 

현재도 제작중이기 때문에.. 저기에서 어떤 점이 바뀔지는 모르겠지만.. 이 상태로 하기로 정했다.

 

GUI Xaml 제작 및 프로그래밍

일단 여기에서 제일 중요한 부분은 Inverse Kinematics 부분이다. 물론 각도로 일일이 다 지정을 할 순 있겠지만.. 그렇게 하면.. GUI 프로그램을 만드는 의미가 사라진다..

그래서 일단 저기를 구현해야하는데.. 구현하기 전에 사전에 세팅을 했다.

 

그리드 그리기

먼저 그리드를 표시하는 것!

일단 Xaml로 저것이 표시될 구간을 정한다!

현재 나의 경우엔.. 이걸 처음하다보니.. 아는게 없었다.. 그래서 어느정도 전문가가 보기엔 불편한 것들이 많겠지만.. 일단 이대로 진행하기로 했다. 아래 코드 구조에서 Canvas가 2개가 있는데.. 하나는 그리드를 표시하는 용이고 하나는 Inverse Kinematics로 팔 부분을 표시하는 것이다.

<Border
    Width="506"
    Height="506"
    BorderBrush="#f8f8f2"
    BorderThickness="3"
    CornerRadius="15,15,15,15"
    RelativePanel.AlignVerticalCenterWithPanel="True"
    Margin="200, 0, 0, 0">
    <!-- 그리드 위에 컨트롤를 넣는 느낌으로 할려 했지만 그리드가 나중에 렌더링 되어서 Canvas.ZIndex로 직접 바꿈 -->
    <Canvas
        x:Name="DrawGrid"
        Width="500"
        Height="500"
        Canvas.ZIndex="0"
        Background="Transparent"> 
        <Canvas
            x:Name="ControlJoint"
            Width="500"
            Height="500"
            Canvas.ZIndex="1"
            Background="Transparent"> <!-- 배경을 투명하게라도 주는 이유는 안하면 ControlJoint로 마우스 위치를 얻을려고 하면 Shape 부분만 얻을 수 있어서.. 전부 얻기 위해서 하는거 -->
        </Canvas>
    </Canvas>
</Border>

주석에 달린 것 처럼.. ZIndex를 넣은 이유가.. 그리드 배경 위에 팔 부분이 나오면 좋겠지만.. 렌더링 되는 것이.. 그리드가 먼저 되다보니.. ZIndex로 직접 바꾼거다.. 그리고 움직일 부분에서 마우스로 제어할 것인데.. 거기에서 마우스로 제어하는 경우 그리드에선 전부 추적이 가능하지만.. ControlJoint에선 팔 부분만 마우스의 좌표를 알 수 있다.. 그래서 그냥 배경을 추가하는 방식으로 진행했다.

 

이제 저렇게 하면 프로그램 안에 500 * 500 의 사각 박스가 나오게 된다.

이 사각 박스 안에 격자를 그려야하는데 이걸 위해 함수를 만들었다.

일단 line의 경우 좌표에서 필요한 속성은 X1, Y1, X2, Y2가 있다.

그래서 내가 생각한 방법으론 500을 내가 필요한 만큼의 라인 갯수로 나눠서 각 지점에 직접 배치하는 방식으로 했다.

 

그렇게 해서 C++ 코드를 작성했다.

void MainPage::DrawLine(Xaml::Shapes::Line& line, Xaml::Controls::Canvas& canvas, double thickness, float2 pos_1, float2 pos_2, Xaml::Media::Brush color) {
    // 위치나 색 설정
    line.X1(pos_1.x);
    line.Y1(pos_1.y);
    line.X2(pos_2.x);
    line.Y2(pos_2.y);

    line.StrokeThickness(thickness);
    line.Stroke(color);

    canvas.Children().Append(line);

    Xaml::Controls::Canvas::SetZIndex(line, 0);
}

void MainPage::DrawGridLines(int grid_x_count, int grid_y_count) {
    double grid_width = DrawGrid().ActualWidth();
    double grid_height = DrawGrid().ActualHeight();
    double line_thickness = 1;

    // 가로 줄 세로로 여러개 그리는거
    for (double i = grid_height / grid_y_count; i < grid_height; i += grid_height / grid_y_count) {
        Xaml::Shapes::Line line = Xaml::Shapes::Line();
        DrawLine(line, DrawGrid(), line_thickness, float2(0, i), float2(grid_width, i));
    }

    // 세로 줄 가로로 여러개 그리는거
    for (double i = grid_width / grid_x_count; i < grid_width; i += grid_width / grid_x_count) {
        Xaml::Shapes::Line line = Xaml::Shapes::Line();
        DrawLine(line, DrawGrid(), line_thickness, float2(i, 0), float2(i, grid_height));
    }
}

이 코드로 DrawGridLines를 처음에 프로그램을 시작할 때 실행해서 각 x축으로 몇개 y축으로 몇개 넣을지 선택해서 하면 

 

결과적으로 이렇게 표시가 된다..!

라인을 각각 10개씩 넣어서 했다.

 

마우스 위치 알아내고 표시하기

그리고 이후에 팔을 제어하기 위해 마우스를 사용해야해서 마우스 포인터의 위치를 알아야 한다..!

근데 일단 보통은 콘솔창에서 확인을 하는데.. 콘솔창은 어떻게 띄우는지도 모르겠고.. 그냥 GUI 화면에서 확인하는게 빠를 것 같아서 GUI 화면에서 표시되도록 했다..!

 

일단 Xaml 코드에선

<StackPanel
    BorderBrush="White"
    BorderThickness="2" RelativePanel.AlignBottomWithPanel="True" RelativePanel.AlignLeftWithPanel="True">
    <StackPanel
        Orientation="Horizontal">
        <TextBlock
            Width="50"
            TextAlignment="Center"
            Text="X"
            FontSize="35"/>
        <TextBlock
            x:Name="Point_X"
            Width="100"
            TextAlignment="Right"
            Text="0"
            FontSize="35"/>
    </StackPanel>
    <StackPanel
        Orientation="Horizontal">
        <TextBlock
            Width="50"
            TextAlignment="Center"
            Text="Y"
            FontSize="35"/>
        <TextBlock
            x:Name="Point_Y"
            Width="100"
            TextAlignment="Right"
            Text="0"
            FontSize="35"/>
    </StackPanel>
</StackPanel>

이런식으로 했다. X인지 Y인지 알 수 있게 하고 그 바로 옆에 숫자를 표시하도록 했다.

그래서 이걸로 해서 실행하면 저런식으로 나온다.

 

그리고 각 X, Y 좌표를 얻어야 한다.

이 경우엔 좀 간단한데.. 보통은 Xaml 코드에서 X, Y 값을 가져올 함수를 지정할 순 있지만.. 나는 나중에 함수 이름을 쉽게 변경하기 위해 모두 C++에서 진행했다.

일단 X, Y 좌표를 가져올 캔버스는 ControlJoint라는 것이기 때문에 여기의 PointerMoved로 접근해서 함수로 넘겨주는 방식으로 했다.

void MainPage::Init() {
	ControlJoint().PointerMoved({ this, &MainPage::JointControlCanvasPointerMoved });
}

void MainPage::JointControlCanvasPointerMoved(IInspectable const& sender, Xaml::Input::PointerRoutedEventArgs const& e) {
    Input::PointerPoint point = e.GetCurrentPoint(ControlJoint());
    float2 point_pos(point.Position().X, point.Position().Y);

    Point_X().Text(to_hstring(point_pos.x));
    Point_Y().Text(to_hstring(point_pos.y));
}

이런식으로 하면 마우스 포인터의 값을 가져와서 아까 만들었던 Text로 집어넣게 된다.

Init 함수는 그냥 임의로 만든거고.. 프로그램이 시작할 때 실행되도록 하면 된다. 그렇게 해서 마우스 값을 넘겨주고 거기에서 X, Y 값을 뽑아서 Text를 지정하는 방식이다.

 

결과물!!

그래서 이것으로 일단 프로젝트 준비는 끝났다..!!

이걸로 지금까지 한 것의 결과물을 보면 아래 처럼된다..!

동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.

그렇게 해서 일단 Pt. 1은 끝났다..! 

이 모든 것이.. 3일 정도 걸려서 알아내고 만든 결과다..! 물론 GPT의 도움도 받긴 했지만.. 암튼.. 다음에선 IK를 구현하는 것이다..!

반응형
반응형

원인

필요한 헤더파일이 안불러와진 것..

 

문제

WinRT로 프로그램을 만드는 중에.. 어떤 오류가 발생했다.. 

심각도 코드 설명 프로젝트 파일 줄 비표시 오류(Suppression) 상태
오류 C3779 'winrt::impl::consume_Windows_UI_Input_IPointerPoint<winrt::Windows::UI::Input::IPointerPoint>::Position': 'auto'을(를) 반환하는 함수를 정의되기 전에 사용할 수 없습니다.	ScaraGUIController	C:\Users\PKDPMS0328\source\repos\ScaraGUIController\ScaraGUIController\MainPage.cpp 22

이 오류는 WinRT 부분에서만 일어나는 오류 같은데.. 블로그나 해결 방법은 잘 안나와 있어서.. 일단 마이크로소프트 문서에 검색해보니..

https://learn.microsoft.com/ko-kr/cpp/error-messages/compiler-errors-2/compiler-error-c3779

 

컴파일러 오류 C3779

자세한 정보: 컴파일러 오류 C3779

learn.microsoft.com

해결 방법은 다행히 있었다..!

 

해결방법

어쨋든 내 코드의 경우 

#include <winrt/Windows.UI.Xaml.Input.h>

void function(winrt::Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e) {
	winrt::Windows::UI::Input::PointerPoint point = e.GetCurrentPoint(ControlJoint());
    
    	Point_X().Text(winrt::to_hstring(point.Position().X));
    	Point_Y().Text(winrt::to_hstring(point.Position().Y));
}

대충 이런식의 코드인데.. 여기에서 오류가 난 부분은 point.Position().X 저쪽 부분이다..

여기에서 문제의 저 오류에 winrt::Windows::UI::Input::IPointerPoint 이런게 뭐가 문제가 있다면서 알려주는데

이것을 헤더파일에 추가하면 문제가 해결되는 것이였다..!

 

그래서 나의 경우에는 IPointerPoint는 Input 헤더 안에 있는 것이기 때문에 

#include <winrt/Windows.UI.Input.h> 이 코드로 헤더파일을 추가해서 해결했다..!

#include <winrt/Windows.UI.Xaml.Input.h>
#include <winrt/Windows.UI.Input.h> // 오류 해결에 필요한 헤더파일

void function(winrt::Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e) {
	winrt::Windows::UI::Input::PointerPoint point = e.GetCurrentPoint(ControlJoint());
    
    	Point_X().Text(winrt::to_hstring(point.Position().X));
    	Point_Y().Text(winrt::to_hstring(point.Position().Y));
}

 

결론

'winrt::impl::consume_Windows_UI_Input_IPointerPoint<winrt::Windows::UI::Input::IPointerPoint>::Position': 'auto'을(를) 반환하는 함수를 정의되기 전에 사용할 수 없습니다.

 

이런식으로 오류가 뜰 경우

1.  <winrt::Windows::UI::Input::IPointerPoint> 부분이 자신의 오류에서 뭐인지 찾는다.

2. winrt::Windows::UI::Input::IPointerPoint 이것에 해당하는 헤더파일을 찾는다. 보통은 맨 뒤에꺼 지운게 아마 헤더파일일 꺼다.. winrt::Windows::UI::Input 여기까지, 그리고 헤더파일로 할 경우 winrt/Windows.UI.Input.h

3. 찾은 헤더파일을 추가해서 문제를 해결한다!!!!

 

근데.. 애초에 Input::PointerPoint가 필요한 헤더파일이 winrt/Windows.UI.Input.h였다.. winrt/Windows.UI.Xaml.Input.h가 아닌... 그냥.. 필요한 헤더파일을 안불러왔었던 것.. 

반응형
반응형

오늘 학교에서 머신러닝 과목으로 첫 인공지능을 배웠습니다!

K-최근접 이웃(K-Neighbors Classifier) 알고리즘은 여러 데이터에서 가장 가까운 k개의 이웃을 찾아 다수결로 분류하는 방식의 알고리즘입니다.(아마)

 

어쨋든!

학교에서 수업한 내용은 파이썬으로 되어있지만.. 파이썬은 여러 라이브러리를 통해 쉽게 할 수 있기 때문에.. 이해가 잘 안되는 것 들도 많았습니다..

그래서 최대한 이해하고자 라이브러리가 별로 없는 C++로 진행하고 싶어 만들어봤습니다!

 

Matplotlib을 통해 표시한 그래프
C++ 콘솔 창으로 만든 그래프

일단 하기전에.. 원래는 그래프로 직접 보기 위해 Matplotlib C++를 가져올려 했으나.. 버전이 너무 오래 되어 불가능 했습니다.. 그래서 직접 만들고자 했지만.. 함수화 하는 과정에서 너무 어려워져 제쳐두었습니다. 위 사진은 시도한 흔적입니다.

 

아래 데이터셋을 이용!

bream_length = [25.4, 26.3, 26.5, 29.0, 29.0, 29.7, 29.7, 30.0, 30.0, 30.7, 31.0, 31.0, 
                31.5, 32.0, 32.0, 32.0, 33.0, 33.0, 33.5, 33.5, 34.0, 34.0, 34.5, 35.0, 
                35.0, 35.0, 35.0, 36.0, 36.0, 37.0, 38.5, 38.5, 39.5, 41.0, 41.0]
bream_weight = [242.0, 290.0, 340.0, 363.0, 430.0, 450.0, 500.0, 390.0, 450.0, 500.0, 475.0, 500.0, 
                500.0, 340.0, 600.0, 600.0, 700.0, 700.0, 610.0, 650.0, 575.0, 685.0, 620.0, 680.0, 
                700.0, 725.0, 720.0, 714.0, 850.0, 1000.0, 920.0, 955.0, 925.0, 975.0, 950.0]
smelt_length = [9.8, 10.5, 10.6, 11.0, 11.2, 11.3, 11.8, 11.8, 12.0, 12.2, 12.4, 13.0, 14.3, 15.0] 
smelt_weight = [6.7, 7.5, 7.0, 9.7, 9.8, 8.7, 10.0, 9.9, 9.8, 12.2, 13.4, 12.2, 19.7, 19.9]

 

설명을 딱히 하긴 어렵지만.. 일단 구현이 안된건 score()입니다.. 어떤 식으로 구현해야할지 감이 안잡혀서.. 하하;;

 

어쨌든 아래 코드를 오늘 수업한 내용 과정 그대로 C++로 옮겨보았습니다! 2개의 데이터와 1개의 정답으로 벡터를 만들어 2개의 데이터와 예측 데이터로 유클리드 거리를 사용해서 거리 값을 구해 k개의 가까운 거리 값을 뽑아 어떤 것이 더 큰지 찾는 코드입니다!

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

//거리 기준으로 오름차순 정렬을 하기 위한거
bool cmp(vector<double> x, vector<double> y) {
	return x[3] < y[3];
}

int main() {
	//데이터 정의
	vector<double> bream_length = { 
		25.4, 26.3, 26.5, 29.0, 29.0, 29.7, 29.7, 30.0, 30.0, 30.7, 31.0, 31.0,
		31.5, 32.0, 32.0, 32.0, 33.0, 33.0, 33.5, 33.5, 34.0, 34.0, 34.5, 35.0,
		35.0, 35.0, 35.0, 36.0, 36.0, 37.0, 38.5, 38.5, 39.5, 41.0, 41.0 
	};
	vector<double> bream_weight = {
		242.0, 290.0, 340.0, 363.0, 430.0, 450.0, 500.0, 390.0, 450.0, 500.0, 475.0, 500.0,
		500.0, 340.0, 600.0, 600.0, 700.0, 700.0, 610.0, 650.0, 575.0, 685.0, 620.0, 680.0,
		700.0, 725.0, 720.0, 714.0, 850.0, 1000.0, 920.0, 955.0, 925.0, 975.0, 950.0 
	};
	
	vector<double> smelt_length = {
		9.8, 10.5, 10.6, 11.0, 11.2, 11.3, 11.8, 11.8, 12.0, 12.2, 12.4, 13.0, 14.3, 15.0
	};
	vector<double> smelt_weight = {
		6.7, 7.5, 7.0, 9.7, 9.8, 8.7, 10.0, 9.9, 9.8, 12.2, 13.4, 12.2, 19.7, 19.9
	};

	vector<double> length, weight;

	//bream과 smelt 데이터를 나란히 이어 붙이는 코드
	length.insert(length.end(), bream_length.begin(), bream_length.end());
	length.insert(length.end(), smelt_length.begin(), smelt_length.end());

	weight.insert(weight.end(), bream_weight.begin(), bream_weight.end());
	weight.insert(weight.end(), smelt_weight.begin(), smelt_weight.end());

	//length와 weight 데이터를 하나의 자료형에 때려 박는거
	vector<vector<double>> fish_data;
	
	for (int i = 0; i < length.size(); i++)
		fish_data.push_back({ length[i], weight[i] });

	//확인용
	if(0) 
		for (int i = 0; i < length.size(); i++)
			cout << fish_data[i][0] << "\t" << fish_data[i][1] << endl;

	//정답 데이터 생성
	vector<int> fish_target;
	for (int i = 0; i < bream_length.size(); i++) fish_target.push_back(1);
	for (int i = 0; i < smelt_length.size(); i++) fish_target.push_back(0);

	//확인용
	if (0)
		for (int i = 0; i < fish_target.size(); i++)
			cout << fish_target[i] << " ";

	//kn 모델 정의(그냥 데이터와 정답데이터 붙이는거임)
	int k = 5;
	vector<vector<double>> kn;
	for (int i = 0; i < fish_data.size(); i++)
		kn.push_back({ fish_data[i][0], fish_data[i][1], double(fish_target[i]) });

	//확인용
	if (0)
		for (int i = 0; i < length.size(); i++)
			cout << kn[i][0] << "\t" << kn[i][1] << "\t" << kn[i][2] << endl;

	//예측
	//30, 600으로
	double p1 = 30, p2 = 600;
	
	//유클리드 거리 공식을 이용해서 거리를 구하고 kn벡터에 삽입하고 거리 기준으로 정렬하여 상위 k개를 뽑아서 누가 더 큰지 비교한다.
	for (int i = 0; i < kn.size(); i++)
		kn[i].push_back(sqrt(pow((kn[i][0] - p1), 2) + pow((kn[i][1] - p2),2)));
	
	sort(kn.begin(), kn.end(), cmp);

	//확인용
	if (true)
		for (int i = 0; i < length.size(); i++)
			cout << kn[i][0] << "\t" << kn[i][1] << "\t" << kn[i][2] << "\t" << kn[i][3] << endl;

	int bream_c = 0, smelt_c = 0;
	for (int i = 0; i < k; i++) {
		(kn[i][2] == 1 ? bream_c++ : smelt_c++);
	}

	cout << (bream_c > smelt_c ? "이건 bream임" : "이건 smelt임") << endl;
}

위 코드를 실행한 결과

암튼 끝!

반응형
반응형

일단 바로 제작된 결과!

음 얼굴을 찾아서 뭔가 하는 프로젝트를 하는데 문제가 생겼다. 얼굴 이미지를 벡터로 변환하는 과정에서 원본 이미지와 얼굴 이미지를 보여줄려 하는데 여기에서 원본 이미지를 보여줄 때 보여주는 곳이 정사각형이다..

근데 OpenCV에서 이미지를 바로 넣을려니깐 이미지를 리사이즈 해서 바로 넣는 것은 좀 불편하고.. 이미지를 정사각형에 맞춰 그냥 리사이즈 하면 이미지가 뭉개지고..

 

그래서 생각한건 흰 배경 중심에 이미지를 넣는 것인데

이걸 해결하기 위해 1시간 반이 걸렸다..

대충 생각을 해본건 

  • 원본 이미지 보여주는 곳과 같은 크기의 배경을 만들어 중앙에 이미지를 배치
  • 원본 이미지의 가장 긴 변의 길이 대로 정사각형 배경을 만들어 중앙에 이미지를 배치

이런거고 중앙에다 배치할땐

  • 비율에 맞춰 원본 이미지를 축소해서 정사각형 배경의 반에 원본 이미지의 짧은 변의 반을 빼서 위치 구하기
  • 정사각형 배경의 반에 원본 이미지의 짧은 변의 반을 빼서 위치 구하기

등을 생각했는데.. 난 여러가지 방법을 생각해보고 여러가지 방법을 해보다가 헷갈리기도 하고 수정하기도 하며 결국엔

원본 이미지의 가장 긴 변의 길이 대로 정사각형 배경을 만들어 중앙에 이미지를 배치할 때 정사각형 배경의 반에 원본 이미지의 짧은 변의 반을 빼서 위치를 구해서 바로 넣는 방법을 하기로 결정했다..

 

그래서 이걸 대충 이미지로 표시하면!

 

이런식으로 

일단 정사각형이면 그냥 패스

가로가 더 길 경우 가로 길이에 대한 정사각형을 만들고 그게 검은색 사각형!

거기에 빨간색이 원본 이미지꺼..

그래서 파란색 동그라미 부분의 위치를 구하는건데

 

검은색 사각형에서 반을 나누고 거기에서 빨간색 사각형의 반을 빼면 저 위치가 나올 것 같다 라는 생각이 들어서 한 것!

파란색은 반대로 생각하면 되는거!

 

대충 식은

(Square / 2) - (OriginalImg / 2)

뭐 어쨌든 이런 방법으로 코드를 구현한 결과!

 

if (I_celeb_img.nc() == I_celeb_img.nr())
    GUICon::putWebcamView(I_celeb_img, preSetImage);
else {
    int squareLen = (I_celeb_img.nc() < I_celeb_img.nr() ? I_celeb_img.nr() : I_celeb_img.nc());

    cv::Mat originalViewImage(squareLen, squareLen, CV_8UC3);
    originalViewImage = cv::Scalar(0xFF, 0xFF, 0xFF);

    //배경 이미지 중앙에 사진 넣기
    //사진의 세로가 가로보다 짧을 경우 squareLen의 길이에서 반을 나누고 이미지 세로의 반 만큼 빼서 좌표 구하기						
    if (I_celeb_img.nr() < I_celeb_img.nc()) { //가로가 더 클 경우 같을 경우 비교 안하는건 위에서 이미 해서
        int ypos = (squareLen / 2) - (I_celeb_img.nr() / 2); //세로가 짧을 경우 정사각형의 한 변의 길에서 이미지 세로 반을 빼서 위치 구하는 것
        CPputImage(I_celeb_img, originalViewImage, cv::Rect(0, ypos, I_celeb_img.nc(), I_celeb_img.nr()));
    }
    else {
        int xpos = (squareLen / 2) - (I_celeb_img.nc() / 2); //위와 반대
        CPputImage(I_celeb_img, originalViewImage, cv::Rect(xpos, 0, I_celeb_img.nc(), I_celeb_img.nr()));
    }
    cv::cvtColor(originalViewImage, originalViewImage, cv::COLOR_RGB2BGR);
    GUICon::putWebcamView(originalViewImage, preSetImage);
}

코드는 이렇다!

여기에서 CPputImage는 그냥 OpenCV 이미지의 특정 위치에 이미지를 넣는 함수고

GUICon::putWebCamView는 원본 이미지 표시하는 쪽에 이미지 넣는 함수다.. 암튼 그럼!

 

그럼 끝!

반응형
반응형

아마 글꼴을 찾는데 문제가 있을 수 있습니다 "맑은 고딕"은 정상적으로 작동되지만 컴퓨터마다 다를 수 있을 것 같습니다. 될 수도 있고 안 될 수도 있다는 점.. 안된다면.. 폰트 쪽 코드를 수정하면 될지도..?

 

일단 음.. 이걸 성공하게 된건.. 한국어나 영어로 찾아볼 땐 아무리 찾아도 안나왔는데.. 일본에는 전문가들이 많으니깐 있지 않을까해서 일본어로 검색해보니.. 역시 있었다. 그래서 해결하게 됨

 

바로 가져갈 분은 맨 아래쪽에 있어요.

 

https://jitaku.work/it/category/image-processing/opencv/write_japanese/

 

C++のOpenCVで日本語をputTextする

Top 作成日: 2020.06.29 C++のOpenCVで日本語をputTextする Pythonの場合比較的容易にできる、日本語を画面描画する方法。 C++はそういったことができないので非常に困っていた。 で、ネットからパク

jitaku.work

일단 나는 여기 것을 사용했는데. 여기에 있는 코드를 바로 쓰면 당연히 안될꺼다.

HFONT hFont = ::CreateFontA(
        fontSize, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE,
        SHIFTJIS_CHARSET, OUT_DEFAULT_PRECIS,
        CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
        VARIABLE_PITCH | FF_ROMAN, fontname);

코드를 보면 이런데.. 저기 아래 SHIFTJIS_CHARSET 저것때문에 안된다. 아마 인코딩 문제 같은데 뭐 저거를 다른 대부분 언어도 되게 할려면 DEFAULT_CHARSET으로 하면 아마 될 것 같다. 당연 한국어도 됨!

 

일단 저걸로 하면 되고

추가적으로 수정한 것

일단 내 프로젝트에선 좀 중요하기 때문에 얘를 그냥 출력하면 안티에일리어싱이 결국엔 안된다.

위의 코드에서 안티에일리어싱을 키면 되는데 킨다 하더라도 코드 때문에 결국은 안된다.

 

일단 이 코드의 방식을 보면 먼저 MFC로 어디선가?에서 적을 OpenCV Mat에서의 가로 길이만큼의 텍스트 이미지를 생성한 후 가져와서 비트맵을 Mat 방식대로 바꿔서 적용하는 방식이다. (아마 맞을꺼임)

 

근데 저기 코드를 대충 보면

if (_tmp[0] == 0 && _tmp[1] == 0 && _tmp[2] == 0) {
                    _img[0] = (unsigned char)color.val[0];
                    _img[1] = (unsigned char)color.val[1];
                    _img[2] = (unsigned char)color.val[2];
                }

이런 코드가 있다. 그리고 위쪽에 보면 back_color로 0x99가 정의 되어 있는데.. 이걸 보면 대충 알 수 있다. 일단 MFC에서 텍스트 이미지를 생성하면 텍스트 밖은 기본적으로 흰색(255, 255, 255), 텍스트는 검은색(0, 0, 0)이다.이다. 또한 0x99는 153으로 RGB 153 153 153 뭐 이래보면 회색이다. 최대가 255이니.. 그래서 최종적으로 저 코드는 텍스트외의 배경 (153, 153, 153)과 (255, 255, 255) 부분을 제외한 검은색 부분(텍스트 부분)을 가져와 사전에 정의한 RGB색대로 다시 설정하는 방식이다.

 

이 때문에 이 코드에선 아무리 안티에일리어싱을 한들.. 안티 에일리어싱이 픽셀 사이사이에 텍스트보다 연한 색을 찍는건데 이 것들이 다 제외되면서 결국엔 안티 에일리어싱이 안찍힌다..

 

그래서 내가 생각한 방식은 안티 에일리어싱의 경우 연한 색도 처리해야하기 때문에 배경색이 바뀔때마다 당연히 글자 색도 변경될테니 배경색을 직접 지정하고 텍스트 색도 직접 지정해서 나중에 배경색만 빼는 것이다. 그러면 결국엔 적용되는건 텍스트의 진한 픽셀부터 연한 픽셀까지이다. 그래서 이 방법으로 코딩한건 뭐 아래에 넣을꺼고

 

또한 내가 지정한 것 중에 다른 것들도 있지만 ori가 있다. 내가 당시 했을때 오리엔테이션인가 뭔가 하는걸로 줄여서 적은 것 같은데.. 잘 못 지정한 것 같다.

어쨌든 저 코드를 보면

int posX = (ori == 0 ? org.x : (ori == 1 ? org.x - (size.cx / 2) : org.x - size.cx)); //기준이 왼쪽이면 그대로, 중간이면 기준점 - 넓이 / 2, 오른쪽이면 기준점 - 넓이

대충 이렇게 적어놨다. 조건연산자로 이리저리 해놨는데 주석문을 보면 대충 알 수 있다. 0이면 왼쪽 기준 1이면 중간 기준 2이면 오른쪽 기준

말 그대로다. 사전에 정의한 글자 위치대로 0이면 텍스트의 맨 왼쪽 기준으로 텍스트가 생성되고 1이면 텍스트의 중간으로 생성 2이면 텍스트의 오른쪽 끝으로 생성된다.

 

그리고 세로 축도 할려했는데.. 음..! 이 때 당시엔 필요가 없었어서.. 하실 분들은 직접 하시길! 내가 기억하기론 세로는 맨 위쪽 기준으로 되는걸로 알고 있다.

 

코드를 보면 뭐 배경색 칠하는 것도 있고 텍스트 색 지정하는 것도 있고 그렇다.. 어쨌든 그런거임!

 

이제 코드를 보여줌!

코드

void _putText(cv::Mat& img, const cv::String& text, const cv::Point& org, const int ori, const char* fontname, const int fontWeight, const double fontScale, const bool anti, const RGBScale textcolor, const RGBScale bkcolor) {
	int fontSize = (int)(10 * fontScale);
	int width = img.cols;
	int height = fontSize * 3 / 2;

	HDC hdc = CreateCompatibleDC(NULL);

	HBRUSH hBrush = CreateSolidBrush(bkcolor.rgb);

	RECT rect;
	rect.left = rect.top = 0;
	rect.right = width;
	rect.bottom = height;

	BITMAPINFOHEADER header;
	ZeroMemory(&header, sizeof(BITMAPINFOHEADER));
	header.biSize = sizeof(BITMAPINFOHEADER);
	header.biWidth = width;
	header.biHeight = height;
	header.biPlanes = 1;
	header.biBitCount = 24;
	BITMAPINFO bitmapInfo;
	bitmapInfo.bmiHeader = header;
	HBITMAP hbmp = CreateDIBSection(NULL, (LPBITMAPINFO)&bitmapInfo, DIB_RGB_COLORS, NULL, NULL, 0);
	SelectObject(hdc, hbmp);

	FillRect(hdc, &rect, hBrush);

	BITMAP  bitmap;
	GetObject(hbmp, sizeof(BITMAP), &bitmap);

	HFONT hFont = CreateFontA(
		fontSize,
		0,
		0,
		0,
		fontWeight,
		FALSE,
		FALSE,
		FALSE,
		DEFAULT_CHARSET,
		OUT_DEFAULT_PRECIS, //SHIFTJIS_CHARSET -> DEFAULT_CHARSET으로 변경
		CLIP_DEFAULT_PRECIS,
		(anti ? ANTIALIASED_QUALITY : DEFAULT_QUALITY), //안티를 허용하면 해주고 아니면 안해주고
		VARIABLE_PITCH | FF_ROMAN,
		fontname);
	SelectObject(hdc, hFont);
	SetTextColor(hdc, textcolor.rgb);
	SetBkColor(hdc, bkcolor.rgb);

	//넓이 높이 구하기
	SIZE size;
	GetTextExtentPoint32A(hdc, text.c_str(), (int)text.length(), &size);

	TextOutA(hdc, 0, height / 3 * 1, text.c_str(), (int)text.length());
	int posX = (ori == 0 ? org.x : (ori == 1 ? org.x - (size.cx / 2) : org.x - size.cx)); //기준이 왼쪽이면 그대로, 중간이면 기준점 - 넓이 / 2, 오른쪽이면 기준점 - 넓이
	int posY = org.y - (size.cy / 2 + 5);

	unsigned char* _tmp;
	unsigned char* _img;
	for (int y = 0; y < bitmap.bmHeight; y++) {
		if (posY + y >= 0 && posY + y < img.rows) {
			_img = img.data + (int)(3 * posX + (posY + y) * (((bitmap.bmBitsPixel / 8) * img.cols) & ~3));
			_tmp = (unsigned char*)(bitmap.bmBits) + (int)((bitmap.bmHeight - y - 1) * (((bitmap.bmBitsPixel / 8) * bitmap.bmWidth) & ~3));
			for (int x = 0; x < bitmap.bmWidth; x++) {
				if (x + posX >= img.cols) {
					break;
				}

				if (_tmp[0] != bkcolor.b || _tmp[1] != bkcolor.g || _tmp[2] != bkcolor.r) { //순서를 bgr를 써서 bgr로 한거, rgb 순서이면 그걸로 해야함
					_img[0] = (unsigned char)_tmp[0];
					_img[1] = (unsigned char)_tmp[1];
					_img[2] = (unsigned char)_tmp[2];
				}
				_img += 3;
				_tmp += 3;
			}
		}
	}

	DeleteObject(hFont);
	DeleteObject(hbmp);
	DeleteDC(hdc);
}

주석문 보고 뭐 대충 하면 될 것 같다. 난 OPENCV의 기본 MAT의 설정인 BGR때문에 아래가 BGR 기준이고 RGB면 순서를 바꿔서 작성해야 할 것이다.

 

cv::Mat& img, const cv::String& text, const cv::Point& org, const int ori, const char* fontname, const int fontWeight, const double fontScale, const bool anti, const RGBScale textcolor, const RGBScale bkcolor

파라미터를 대충 설명하면

  • cv::Mat& img -> 텍스트를 적용할 이미지
  • cv::String& text -> 출력할 텍스트
  • cv::Point& org -> OpenCV 이미지에서 텍스트를 지정할 위치
  • int ori -> 텍스트 위치를 지정할 때 왼쪽 중앙 오른쪽으로 어느 위치로 정렬해서 출력할지
  • char* fontname -> 폰트 이름 (예: 맑은 코딕)
  • int fontWeight -> 글자의 굵기 (예: FW_BOLD 이런거 하면 됨.. 그냥 숫자로 해도 되고)
  • double fontScale -> 글자 크기 (예: 이건 좀 보정을 안했는데 그냥 무조건 큰 숫자 적으면 진짜 커지므로 기본 생각했던 것보다 반 작게 줄여서 적으면 아마 맞을꺼다)
  • bool anti -> 안티에일리어싱을 할꺼냐 말꺼냐
  • RGBScale textcolor -> 텍스트의 컬러 지정 (예: RGBScale(255, 0, 0))
  • RGBScale bkcolor -> 배경색 지정 (예: RGBScale(255, 255, 255))

어쨌든 이정도다.. 뭐 쓰다보니 파라미터가 너무 많아지긴 했지만.. 뭐 내가 쓰는건데 뭐 어때!

_putText(outImg, "나", cvPoint(50, 50), 1, "맑은 고딕", FW_BOLD, 6, true, RGBScale(0, 0, 0), RGBScale(255, 255, 255));

사용 예시는 위와 같다.

 

어쨌든 그렇다. 이거 글 쓰는 것도 힘들 구 생각하는 것도 힘들었는데..

나중에 자기 글에다 쓰고 싶다면... 적어도 링크라도.. 부탁.. (이거 그럼 만든게 2022년 11월 쯤에 만들었으니.. 고등학교 2학년 때 만든거군.. 아 여기 학교 C/C++과목 전혀 없음)

 

뭐 어쨌든 끄읕!

 

얼마나 사용해줄진 모르겠지만.. C++은 겁나 복잡해!!

 

(파이썬은 짧고 쉽던데.. 방식은 이거와 비슷)

 

2023-03-01 추가

코드에서 RGBScale에 대한 구조체 내용이 없어서 다시 정리하고 구조체에 대한 것도 새로 적습니다.

struct RGBScale {
    int r = 0;
    int g = 0;
    int b = 0;
    int rgb = 0;

    RGBScale(int r, int g, int b) {
        this->r = r;
        this->g = g;
        this->b = b;

        this->rgb = RGB(r, g, b);
    }
};

RGBScale(0xFF, 0xFF, 0xFF)로 적으면 되는데 0xFF는 그냥 16진수 이기 때문에 0부터 255 숫자중 원하는 색깔에 맞춰 적으시면 됩니다! 그리고.. 기본적으로 cv::Mat 생성할 때 RGB가 아닌 BGR로 생성하기 때문에 아마 RGB 이미지를 넣어서 적용시키면 색깔이 다르게 나올꺼기 때문에.. 인풋엔 BGR로 넣으시면 됩니다. 수정할려면 아래 적혀있는 코드에서 비트맵을 OpenCV로 바꿔주는 곳에서 b, g, r 적혀있는 것을 r, g, b로 변경하시면 될겁니다. RGBScale를 따로 만든 이유는 그냥 r, g, b도 추출하고 rgb도 출력하고 하기 위함!

 

아래는 다시 정리해본 코드!

void CPputText(cv::Mat& O_image, cv::String text, cv::Point org, int ori, const char* fontName, int fontWeight, double fontScale, RGBScale textColor, RGBScale bkColor) {
	int fontSize = (int)(10 * fontScale);
	int width = O_image.cols;
	int height = fontSize * 3 / 2;

	HDC hdc = CreateCompatibleDC(NULL); //텍스트 이미지를 만들어두는 곳 같은거

	HBRUSH hBrush = CreateSolidBrush(bkColor.rgb); //채우는 방식인데 bkColor로 단색으로 채우는거

	//텍스트 이미지 크기 정하는거
	RECT rect;
	rect.left = rect.top = 0;
	rect.right = width;
	rect.bottom = height;

	//비트맵의 구조를 사전에 정의하는 것 크기나 색
	BITMAPINFOHEADER header;
	ZeroMemory(&header, sizeof(BITMAPINFOHEADER));
	header.biSize = sizeof(BITMAPINFOHEADER);
	header.biWidth = width;
	header.biHeight = height;
	header.biPlanes = 1;
	header.biBitCount = 24;
	BITMAPINFO bitmapInfo;
	bitmapInfo.bmiHeader = header;
	HBITMAP hbmp = CreateDIBSection(NULL, (LPBITMAPINFO)&bitmapInfo, DIB_RGB_COLORS, NULL, NULL, 0);
	SelectObject(hdc, hbmp); //hdc에 적용? 하는 거

	FillRect(hdc, &rect, hBrush); //지정한 크기만큼 완전하게 채우는거 (다 채움)

	BITMAP bitmap;
	GetObject(hbmp, sizeof(BITMAP), &bitmap);

	//텍스트 이미지 만들 때 사용할 수 있는 폰트를 생성? 하는 그런거
	HFONT hFont = CreateFontA(
		fontSize,
		0,
		0,
		0,
		fontWeight,
		FALSE,
		FALSE,
		FALSE,
		DEFAULT_CHARSET, //한국어나 일본어나 해주게 하는거 (아마)
		OUT_DEFAULT_PRECIS,
		CLIP_DEFAULT_PRECIS,
		ANTIALIASED_QUALITY, //안티 에일리어싱을 켜주는거
		VARIABLE_PITCH | FF_ROMAN,
		fontName);
	SelectObject(hdc, hFont);
	SetTextColor(hdc, textColor.rgb);
	SetBkColor(hdc, bkColor.rgb);

	//계산을 위해 미리 텍스트의 사이즈 구하는거
	SIZE size;
	GetTextExtentPoint32A(hdc, text.c_str(), (int)text.length(), &size);

	TextOutA(hdc, 0, height / 3 * 1, text.c_str(), (int)text.length()); //이미지에 텍스트 적는거
	int posX = (ori == 0 ? org.x : (ori == 1 ? org.x - (size.cx / 2) : org.x - size.cx)); //기준 정하는거 0은 텍스트의 왼쪽 1은 텍스트의 중간 2는 텍스트의 오른쪽
	int posY = org.y - (size.cy / 2 + 5);

	//비트맵 사진을 OpenCV이미지에 삽입해주는거
	unsigned char* _tmp;
	unsigned char* _img;
	for (int y = 0; y < bitmap.bmHeight; y++) {
		if (posY + y >= 0 && posY + y < O_image.rows) {
			_img = O_image.data + (int)(3 * posX + (posY + y) * (((bitmap.bmBitsPixel / 8) * O_image.cols) & ~3));
			_tmp = (unsigned char*)(bitmap.bmBits) + (int)((bitmap.bmHeight - y - 1) * (((bitmap.bmBitsPixel / 8) * bitmap.bmWidth) & ~3));
			for (int x = 0; x < bitmap.bmWidth; x++) {
				if (x + posX >= O_image.cols)
					break;

				if (_tmp[0] != bkcolor.b || _tmp[1] != bkcolor.g || _tmp[2] != bkcolor.r) { //텍스트 이미지의 배경 컬러는 없애기 위한 것, bgr 순서로 하는 이유는 Mat 이미지를 처음에 만들 때 BGR 순이여서
					_img[0] = (unsigned char)_tmp[0]; //B
					_img[1] = (unsigned char)_tmp[1]; //G
					_img[2] = (unsigned char)_tmp[2]; //R
				}
				_img += 3;
				_tmp += 3;
			}
		}
	}

	//메모리에서 삭제해주는거 이거 안하면 메모리 계속 사용함
	DeleteObject(hBrush);
	DeleteObject(hFont);
	DeleteObject(hbmp);
	DeleteObject(hdc);
}
cv::Mat inputMat(500, 500, CV_8UC3);
CPputText(inputMat, "안녕!", cvPoint(inputMat.cols / 2, inputMat.rows), 1, "맑은 고딕", FW_BOLD, 6, RGBScale(0xFF, 0xFF, 0xFF), RGBScale(0, 0, 0));

아아마 이렇게 하면 500 * 500 으로 이미지를 생성하고 그 중간에 텍스트를 넣는거니 완전 중간은 아니고.. 세로가 좀 아래로 쳐져있긴 하지만.. 저렇게 하면 잘 작동은 합니다!

반응형
반응형

CVUI를 사용하여 OpenCV의 사진에다가 UI를 넣을려고 하는데

내가 코드에 쓰는 CVUI 함수들 마다 다 저딴 오류가 뜬다..

 

이거 그냥 간단하게 해결이 가능하다.

 

해결

상단에 #define "cvui.h" 하기전에 #define CVUI_IMPLEMENTATION이것만 추가하면 된다.

 

#include "cvui.h"

이걸 이렇게

#define CVUI_IMPLEMENTATION
#include "cvui.h"

 

요약

그냥 cvui.h 가져올 때 그 위에 "#define CVUI_IMPLEMENTATION" 이거 추가하기

반응형
반응형

내가 문제 생겼을 때 보기 위한 오류해결

  • C2146 : ';'이(가) 'pixel_type' 식별자 앞에 없습니다. 
  • C2027 : 정의되지 않은 형식 'dlib::image_traits<image_type>'을(를) 사용했습니다.
  • C4430 : 형식 지정자가 없습니다. int로 가정합니다. 참고: C++에서는 기본 int를 지원하지 않습니다.

일단 저기 Don't add anymore은 그냥 지금 프로젝트를 계속 여러번 테스트하며 하고 있어서 더 이상 추가안한다는 것

 

Dlib 라이브러리를 쓸 때 가끔 이래 뜬다..

이 오류는 내 문제에 대한건 그냥 dlib에 이미지 파일을 넣을 때 그 함수의 지원되지 않는 이미지 변수를 넣어서 그런 것 같음..

 

해결

해결 방법은 그냥 dlib가 지원하는 이미지 변수로 넣으면 아마 될 것이다..!

auto face = detector(camImg);

내 경우 얼굴 인식 때문에 하고 있는데

현재 저 camImg가 OpenCV의 Mat 자료형이다.

 

근데 Dlib에서 OpenCV의 자료형을 넣어버리면 분명 오류를 낼꺼다.. 자기꺼도 아닌데 (왜 넣냐고 나보고 어쩌라고..) 할 것이다.

그래서 간단하게 해결할려면 Dlib에서 지원하는 자료형으로 변환하면 되는 것임!

dlib::cv_image<dlib::bgr_pixel> cimg(camImg);
auto face = detector(camImg);

이렇게 하면 되는 것임..!

dlib의 cv_image가 OpenCV의 이미지를 Dlib의 매트릭스로 바꿔주는 그런 것인 듯

 

어쨋든 그런 것임!

 

요약

Dlib가 지원하는 자료형으로 이미지 넣으면 됨

반응형
반응형

학교에서 프로젝트를 받아버려서.. 해야하는 상황..

내 프로젝트를 위해선 DLIB와 OPENCV가 필요한데..

정말 2~3일동안 수없이 도전해보다가 굉장히 간단한 방법을 찾아서 알려줄까함! (왜이리 dlib는 잘 안깔리는거야)

 

일단 설치 방법은 요약하면 VCPKG를 설치해서 DLIB와 OPENCV를 설치하면 된다!

이미 VCPKG가 깔려있다면 2번으로 넘어가주세요.

1. VCPKG 설치하기

설치는 아래 링크로 들어가서 보시면 되겠습니다~~

https://cheongpark.tistory.com/15

 

윈도우에서 VCPKG 설치하기

VCPKG는 대충 C++ 그런거의 패키지를 자동으로 설치해주는? 느낌인 것 같다.. 자동화로 설치해줘서 짜증나는 오류들을 경험하지 않고 빠르게 설치해주는 엄청난 놈인 것 같다! VCPKG 설치하기 https://

cheongpark.tistory.com

 

2. dlib와 opencv 설치

vcpkg install dlib:x64-windows
vcpkg install opencv:x64-windows

위 명령어를 커맨드 창에 입력해주세요!

만약 vcpkg가 없는 명령어로 뜨면 환경 변수 설정이 안되거나 환경 변수 경로를 잘 못 지정했을 겁니다!

x64-windows는 vcpkg 패키지 리스트에서 보고 자기꺼에 맞는 걸로 선택하시면 됩니다!

 

이렇게 하면 모든 것이 설치 완료!!

이제 Visual Studio에서 include에서 사용하시면 됩니다!

// The contents of this file are in the public domain. See LICENSE_FOR_EXAMPLE_PROGRAMS.txt
/*

    This is an example illustrating the use of the perspective_window tool
    in the dlib C++ Library.  It is a simple tool for displaying 3D point 
    clouds on the screen.

*/

#include <dlib/gui_widgets.h>
#include <dlib/image_transforms.h>
#include <cmath>

using namespace dlib;
using namespace std;

// ----------------------------------------------------------------------------------------

int main()
{
    // Let's make a point cloud that looks like a 3D spiral.
    std::vector<perspective_window::overlay_dot> points;
    dlib::rand rnd;
    for (double i = 0; i < 20; i+=0.001)
    {
        // Get a point on a spiral
        dlib::vector<double> val(sin(i),cos(i),i/4);

        // Now add some random noise to it
        dlib::vector<double> temp(rnd.get_random_gaussian(),
                                  rnd.get_random_gaussian(),
                                  rnd.get_random_gaussian());
        val += temp/20;

        // Pick a color based on how far we are along the spiral
        rgb_pixel color = colormap_jet(i,0,20);

        // And add the point to the list of points we will display
        points.push_back(perspective_window::overlay_dot(val, color));
    }

    // Now finally display the point cloud.
    perspective_window win;
    win.set_title("perspective_window 3D point cloud");
    win.add_overlay(points);
    win.wait_until_closed();
}

//  ----------------------------------------------------------------------------

위 소스는 dlib.net의 기본 예제입니다.

위 소스를 Visual Studio에서 빈 프로젝트로 하나 만들고 소스 파일에서 test.cpp 하나 만들고 위 코드를 입력해서 실행해보시면 아주 잘 되는 것을 알 수 있습니다!

이런식으로 창이 나오면 잘 작동한다는 것이죠!

마우스로 회전도 되더라구요.. (그래프로 만들면 재밌겠다)

 

뭐 어쨌든 이 방식으로 설치했다면 빌드 폴더에 dll 파일도 나오는 경우가 있는데 이걸 같이 이동 시켜줘야지 다른 컴퓨터에서도 정상적으로 작동할 겁니다! (아무것도 설치 안되어있어도 잘 작동하더라구요.. 뭐 다른 한개는 설치해줘야 하긴 하는데..)

 

찾아보니 x64-windows-static 버전은 exe에다가 dll 파일 연결 안하고 모조리 다 때려 넣는 것 같군요! 하지만 설정은 더 힘드니 저 혼자만 하도록 하죠!

 

다만 맥이나 리눅스에서는 안될지도?

 

어쨌든 이만 끝!

VCPKG는 진짜 엄청나다..!

 

반응형

'설치 > C++' 카테고리의 다른 글

Visual Studio 자동 완성 규칙 변경  (0) 2024.01.25
VCPKG 버전 업데이트  (0) 2024.01.08
윈도우에서 VCPKG 설치하기  (0) 2022.11.06

+ Recent posts