// STREET VIEW ver0.1 :: HANDONG
//
// Created by Seongho Hong / Mihyun Wendy Yang / on 2015. 7. 16..
// Copyright (c) 2015년 Seongho Hong. All rights reserved.
//
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <fstream> // 텍스트 파일 읽기
#include <conio.h> // 방향키 읽기
#include <math.h>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\opencv.hpp>
#define MAX_PICTURE 100
#define MAX_DIRECTION 4
#define MAX_TEXT 256
#define MAX_DIST 10000000000
using namespace std;
using namespace cv;
//입력받은 점의 인덱스와 좌표값
typedef struct pts_info{
int id;
double direc[4];
double length[4]; //0=동, 1=서, 2=남, 3=북
double x, y;
int capturedCompass; // 캡쳐된 당시의 나침반 방향, 자이로센서의 x 축
}PTS_INFO;
// 마우스 이벤트 처리
typedef struct MouseChange{
int direction = -1;
int change = 0;
Size frameSize; // 버튼 영역 설정하려고 받음
int user_dir; // 유저가 바라보는 방향
}MOUSECHANGE;
// 위치 관계표 만들 때 사용
double F_dist(double x1, double y1, double x2, double y2); // 두점 거리
double F_ang(double x1, double y1, double x2, double y2); // 두점 각도
void makeDirectionMap(PTS_INFO dstMap[MAX_PICTURE],int &out_num_pic); // 관계표 만들기
// 스트리트 뷰 화면 구현에 사용
void showRelation(int(*map)[MAX_DIRECTION]); // 콘솔창에 사진간의 관계 보여주기
void showDirMap(PTS_INFO *dirMap,int in_num_pic); // 콘솔창에 사진간의 관계 보여주기
int getDirection(int inputKey); // eswn 으로 동서남북 이동
int getAsciiDirection(int inputKey); // 방향키로 동서남북 이동
String getFileName(PTS_INFO map, int compass); // 숫자 넣으면 .jpg로 만들어주기
void readText(String fileName, PTS_INFO* &io_dstMap); // 텍스트 파일에서 사진 관계 불러오기
void slideImage(Mat & io_streetImage, PTS_INFO map, MOUSECHANGE & io_mouse, int & io_user_compass, int in_indexNum); //좌우 화면 전환
// 스트리트 뷰 화면 컨트롤
//void drawArrow(Mat img, int map[MAX_DIRECTION]); // 화살표 그리기
void drawArrowAndGps(Mat img, PTS_INFO mapint, int & in_user_compass);
void callBackFunc(int event, int x, int y, int flags, void* userdata); // 마우스 클릭 이벤트
// 창 이름
const char* win_name = "STREET VIEW ver0.1 :: HANDONG";
const char* txtFileName = "20150729151637.txt";
const int N = 0, W = 1, S = 2, E = 3;
int main(){
PTS_INFO dirMap[MAX_PICTURE];
int num_pic;
makeDirectionMap(dirMap,num_pic);
showDirMap(dirMap, num_pic);
//변수 초기화
int index = 0;
int indexNum = 0;
int user_compass = 0;
//인덱스 받고 첫화면
cout << "input index number : ";
cin >> indexNum;
if ((MAX_PICTURE < indexNum) || (indexNum < 0)){
cout << "Error : out of range !" << endl;
cout << "You have to input 0 < input < " << MAX_PICTURE << endl;
return -1;
}
Mat streetImage = imread(getFileName(dirMap[indexNum], 0)); //사진 불러오기
MOUSECHANGE mouse;
namedWindow(win_name, WINDOW_NORMAL);
setMouseCallback(win_name, callBackFunc, &mouse); // 마우스 클릭 이벤트 설정
drawArrowAndGps(streetImage, dirMap[indexNum], user_compass);// 화살표 생성
imshow(win_name, streetImage);
resizeWindow(win_name, 640, 480);
mouse.frameSize = streetImage.size();
while ((mouse.change == 0) || (dirMap[indexNum].direc[mouse.direction] == -1)){
waitKey(20);
}
Mat temp = streetImage.clone();
while (1){
if ((mouse.change == 1) && (dirMap[indexNum].direc[mouse.direction] != -1) ){ // 가고자 하는 방향에 사진이 있을 때만
indexNum = dirMap[indexNum].direc[mouse.direction]; //indexNUm에 그 사진 index 넣어주기
streetImage = imread(getFileName(dirMap[indexNum], user_compass), 1); // 사진 불러오기
drawArrowAndGps(streetImage, dirMap[indexNum], user_compass); // 화살표 생성
imshow(win_name, streetImage);
mouse.frameSize = streetImage.size();
}
slideImage(streetImage, dirMap[indexNum], mouse, user_compass, indexNum); // 왼쪽 오른쪽 UI
mouse.change = 0; // 0으로 바꿔주어 다시 마우스 클릭 됐을 때를 알 수 있도록
waitKey(200);
}
//종료
destroyWindow(win_name);
return 0;
}
///////////////////////////////////
//////스트리트 뷰 화면 컨트롤//////
///////////////////////////////////
// jpg 파일 이름 받아오기
// compass에는 사용자가 바라보고 있는 방향 정보를 받아옴
// 기본값으로 동쪽으로 설정 0
String getFileName(PTS_INFO map, int compass){
// 0 북 1 서 2 남 3 동
char buf[MAX_TEXT];
sprintf(buf, "OH3F\\idx%d_camera%d.jpg", map.id,compass);
//sprintf(buf, "%d.jpg", fileIndex,compass);
String dst = buf;
cout << buf << endl;
return dst;
}
// index들의 관계 보여주기
void showRelation(int(*map)[MAX_DIRECTION]){
cout << "########### STREET VIEW ver1.0 ##########" << endl;
cout << "#index\t북\t서\t남\t동\t#" << endl;
for (int i = 0; i < MAX_PICTURE; i++){
cout << "# " << i << '\t';
for (int j = 0; j < MAX_DIRECTION; j++){
cout << map[i][j] << '\t';
}
cout << "#" << endl;
}
cout << "#########################################" << endl;
}
// 좌표 받은 것의 관계 보여주기
void showDirMap(PTS_INFO* in_dstMap,int in_num_pic){
cout << "########### STREET VIEW ver1.0 ##########" << endl;
cout << "#index\t북\t서\t남\t동\t#" << endl;
for (int i = 0; i < in_num_pic; i++){
cout << "# " << i << '\t';
for (int j = 0; j < MAX_DIRECTION; j++){
cout << in_dstMap[i].direc[j] << '\t';
}
cout << "#" << endl;
}
cout << "#########################################" << endl;
}
// txt 읽기
void readText(String fileName, PTS_INFO* &io_dstMap){
ifstream file(fileName);
if (!file.is_open()){
cout << "file is not exist" << endl;
return;
}
char buffer[MAX_TEXT];
char* token;
int i = 0;
while (!file.eof()){
file.getline(buffer, 100);
token = strtok(buffer, "\"id");
for (int j = 0; token != NULL; j++){
if (j == 0){
io_dstMap[i].id = atoi(token);
cout << token << endl;
}
else if (j == 2){ // 3번째
io_dstMap[i].y = atof(token);
cout << i << " : y 좌표 " << token << endl;
}
else if (j == 3){ // 4번째
io_dstMap[i].x = atof(token);
cout << i << " : x 좌표 " << token << endl;
}
else if (j == 5) { //6번째
io_dstMap[i].capturedCompass = atoi(token);
cout << i << " : 촬영 될 때 나침반 각도" <<token << endl;
}
token = strtok(NULL, "\";");
}
i++;
cout << endl;
}
file.close();
}
// 마우스 이벤트
void callBackFunc(int event, int x, int y, int flags, void* userdata){
MOUSECHANGE* mouse = (MOUSECHANGE*)userdata;
int rows = mouse->frameSize.height;
int cols = mouse->frameSize.width;
if (event == EVENT_LBUTTONDOWN){
//cout << "Left button of the mouse is clicked - position (" << x << ", " << y << ")" << endl;
// if 화살표 위를 클릭하면 동, 서, 남, 북
//cout << "cols가로" << cols/2 << endl;
//cout << "rows세로" << rows / 2 << endl;
if (((x> cols / 2) && (x < cols / 2 + 100)) && (y>rows / 2 - 25) && (y < rows / 2 + 25)){
mouse->direction = 3; //동
mouse->change = 1;
}
else if (((x>cols / 2-100) && (x < cols / 2)) && (y>rows / 2 - 25) && (y < rows / 2 + 25)){
mouse->direction = 1; //서
mouse->change = 1;
}
else if (((x>cols / 2 -25 ) && (x < cols / 2 + 25)) && (y>rows / 2 ) && (y < rows / 2 + 50)){
mouse->direction = 2; //남
mouse->change = 1;
//cout << "남" << endl;
}
else if (((x>cols / 2 - 25) && (x < cols / 2 + 25)) && (y>rows / 2 -50 ) && (y < rows / 2)){
mouse->direction = 0; //북
mouse->change = 1;
//cout << "북" << endl;
}
// 왼쪽 오른쪽 여백
if ((x > 0) && (x < cols * 1 / 15) && (y>0) && (y < rows)){
//cout << "hi" << endl;
mouse->change = 2;
}
else if ((x> cols * 14 / 15) && (x < cols) && (y>0) && (y < rows)){
//cout << "there" << endl;
mouse->change = 3;
}
}
else if (event == EVENT_RBUTTONDOWN){
//cout << "Right button of the mouse is clicked - position (" << x << ", " << y << ")" << endl;
}
else if (event == EVENT_MBUTTONDOWN){
//cout << "Middle button of the mouse is clicked - position (" << x << ", " << y << ")" << endl;
}
else if (event == EVENT_MOUSEMOVE){
//cout << "Mouse move over the window - position (" << x << ", " << y << ")" << endl;
}
}
//화살표 그려주기
void drawArrowAndGps(Mat img, PTS_INFO map,int & in_user_compass){
// user compass에서 0번 카메라의 나침반을 가져왔다고 생각하면
if (map.direc[in_user_compass % 4] != -1){ // 북
arrowedLine(img, Point(img.cols / 2, img.rows / 2), Point(img.cols / 2, img.rows / 2 - 50), Scalar(255, 255, 255), 5);
}
if (map.direc[(in_user_compass + 1) % 4] != -1){ // 서
arrowedLine(img, Point(img.cols / 2, img.rows / 2), Point(img.cols / 2 - 100, img.rows / 2), Scalar(255, 255, 255), 5);
}
if (map.direc[(in_user_compass + 2) % 4] != -1){ // 남
arrowedLine(img, Point(img.cols / 2, img.rows / 2), Point(img.cols / 2, img.rows / 2 + 50), Scalar(255, 255, 255), 5);
}
if (map.direc[(in_user_compass + 3) % 4] != -1){ // 동
arrowedLine(img, Point(img.cols / 2, img.rows / 2), Point(img.cols / 2 + 100, img.rows / 2), Scalar(255, 255, 255), 5);
}
char xText[MAX_TEXT];
char yText[MAX_TEXT];
sprintf(xText, "X :%f", map.x);
sprintf(yText, "Y :%f", map.y);
putText(img, xText, Point(img.cols * 4 / 5, img.rows * 8 / 10), FONT_HERSHEY_PLAIN, 1, Scalar(255, 255, 255),2);
putText(img, yText, Point(img.cols * 4 / 5, img.rows * 9 / 10), FONT_HERSHEY_PLAIN, 1, Scalar(255, 255, 255), 2);
}
// 좌우 화면 전환
// 마우스 이벤트, 사용자의 시점을 고려
// 이미지, 관계정보, 마우스 처리, 사용자 시점, 인덱스
void slideImage(Mat & io_streetImage, PTS_INFO map, MOUSECHANGE & io_mouse, int & io_user_compass, int in_indexNum){
if (io_mouse.change == 2)
{
if (io_user_compass == E){
io_user_compass = S;
io_streetImage = imread(getFileName(map, io_user_compass), 1);
drawArrowAndGps(io_streetImage, map, io_user_compass); // 화살표 생성
imshow(win_name, io_streetImage);
}
else if (io_user_compass == S){
io_user_compass = W;
io_streetImage = imread(getFileName(map, io_user_compass), 1);
drawArrowAndGps(io_streetImage, map, io_user_compass); // 화살표 생성
imshow(win_name, io_streetImage);
}
else if (io_user_compass == W){
io_user_compass = N;
io_streetImage = imread(getFileName(map, io_user_compass), 1);
drawArrowAndGps(io_streetImage, map, io_user_compass); // 화살표 생성
imshow(win_name, io_streetImage);
}
else if (io_user_compass == N){
io_user_compass = E;
io_streetImage = imread(getFileName(map, io_user_compass), 1);
drawArrowAndGps(io_streetImage, map, io_user_compass); // 화살표 생성
imshow(win_name, io_streetImage);
}
io_mouse.user_dir = io_user_compass;
io_mouse.frameSize = io_streetImage.size();
}
else if (io_mouse.change == 3)
{
if (io_user_compass == E){
io_user_compass = N;
io_streetImage = imread(getFileName(map, N), 1);
drawArrowAndGps(io_streetImage, map, io_user_compass); // 화살표 생성
imshow(win_name, io_streetImage);
}
else if (io_user_compass == N){
io_user_compass = W;
io_streetImage = imread(getFileName(map, W), 1);
drawArrowAndGps(io_streetImage, map, io_user_compass); // 화살표 생성
imshow(win_name, io_streetImage);
}
else if (io_user_compass == W){
io_user_compass = S;
io_streetImage = imread(getFileName(map, S), 1);
drawArrowAndGps(io_streetImage, map, io_user_compass); // 화살표 생성
imshow(win_name, io_streetImage);
}
else if (io_user_compass == S){
io_user_compass = E;
io_streetImage = imread(getFileName(map, E), 1);
drawArrowAndGps(io_streetImage, map, io_user_compass); // 화살표 생성
imshow(win_name, io_streetImage);
}
io_mouse.user_dir = io_user_compass;
io_mouse.frameSize = io_streetImage.size();
}
}
/////////////////////////////////////
//////좌표로 부터 관계표 만들기//////
/////////////////////////////////////
//두 점의 거리 계산
double F_dist(double x1, double y1, double x2, double y2){
return sqrt(((x1 - x2)*(x1 - x2)) + ((y1 - y2)*(y1 - y2)));
}
//두 점의 각도 계산
double F_ang(double x1, double y1, double x2, double y2){
return (atan2((double)(y2 - y1), (double)(x2 - x1))*180.0f / 3.14159265);
}
//관계표 만들기
void makeDirectionMap(PTS_INFO* io_dstMap, int &out_num_pic) {
int i = 0, n = 0, j = 0;
printf("점의 갯수를 입력하시오 \n");
cin >> n;
double ang_result[MAX_PICTURE][MAX_PICTURE], dis_result[MAX_PICTURE][MAX_PICTURE];
for (i = 0; i<n; i++){
io_dstMap[i].x = io_dstMap[i].y = 0.0;
for (j = 0; j<4; j++){
io_dstMap[i].length[j] = MAX_DIST;
io_dstMap[i].direc[j] = -1;
}
for (j = 0; j<n; j++)
ang_result[i][j] = dis_result[i][j] = 0.0;
}
readText(txtFileName, io_dstMap);
for (i = 0; i<n; i++){
for (j = 0; j<n; j++){
if (i == j) continue;
ang_result[i][j] = F_ang(io_dstMap[i].x, io_dstMap[i].y, io_dstMap[j].x, io_dstMap[j].y);
dis_result[i][j] = F_dist(io_dstMap[i].x, io_dstMap[i].y, io_dstMap[j].x, io_dstMap[j].y);
//printf("i:%d j:%d %f %f ang %f\n", i, j, io_dstMap[i].x, io_dstMap[i].y, ang_result[i][j]);
if (-45 <= ang_result[i][j] && ang_result[i][j]<45){
//동쪽 0과 1. 0과 3
if (io_dstMap[i].length[3] >= dis_result[i][j]){
io_dstMap[i].direc[3] = j;
io_dstMap[i].length[3] = dis_result[i][j];
}
}
else if (45 <= ang_result[i][j] && ang_result[i][j]<135){
//북쪽
if (io_dstMap[i].length[0] >= dis_result[i][j]){
io_dstMap[i].direc[0] = j;
io_dstMap[i].length[0] = dis_result[i][j];
}
}
else if (135 <= ang_result[i][j] || ang_result[i][j]<-135){
//서쪽
if (io_dstMap[i].length[1] >= dis_result[i][j]){
io_dstMap[i].direc[1] = j;
io_dstMap[i].length[1] = dis_result[i][j];
}
}
else if (-135 <= ang_result[i][j] && ang_result[i][j]<-45){
//남쪽
if (io_dstMap[i].length[2] >= dis_result[i][j]){
io_dstMap[i].direc[2] = j;
io_dstMap[i].length[2] = dis_result[i][j];
}
}
}
}
/*for (i = 0; i<n; i++){
printf("점 %d의 동쪽에는 점: %d, 서쪽에는 점: %d, 남쪽에는 점: %d, 북쪽에는점: %d가 있습니다 \n",
i, io_dstMap[i].direc[0], io_dstMap[i].direc[1], io_dstMap[i].direc[2], io_dstMap[i].direc[3]);
}*/
out_num_pic = n;
}