- 현황

정기 채용에서 수시 채용으로 바뀜

또는 정기 채용 인원 수가 줄어듦

0. Profile

  • 문과 졸업 ( 경영정보학과 )
    • 경영정보학과에서도 코딩 배움
    • 과가 다른 컴공이나 소프트웨어과에 비해 커리큘럼이 떨어진다는 평을 많이 받음 하지만 실제로는 문과 출신 많음
    • 경험을 쌓는다면 넘어설 수 있음
    • 과 관련 업무가 강점이 될 수 있음 (이 분은 금융용어들이 친숙하다는 강점이 있었음)
  • 한국 IBM 입사
  • 3년차 주니어 개발자
    • 19 1월 ~ 20 2월 SO프로젝트 IT여신 담당자
    • 20 2월 ~ 21 6월 SI 프로젝트 (시스템 구축?)
    • 지금은 코웨이 렌탈 시스템

 

 

1. 학생으로서의 나

  • 진로선택
    • 경영학과 가고 싶었는데 갑자기 자바 배움 -> 컴퓨터 관련 수업 F -> 경영 전과 준비하려고 했는데 2학년 끝내야함
    • 2학년 때 Data Base를 접하며 흥미를 느낌 -> SQLD 자격증 취득 -> 백엔드에 흥미를 느낌(웹 프로그래밍) & 혼자 공부 많이 함 (언어 바꿔보기, 프로그래머스나 생활 코딩 등 참고)
    • 3학년 끝나고 휴학한 후 취준함
  • 취업과정
    • 입사하고 싶은 회사 정하기
      • 내가 성장할 수 있는 회사 ( 개발 & 개인 )
        • IBM 을 한 이유는 자신이 원하는 skillset으로 프로젝트 진행 가능, 프로젝트 선택 가능
      • 직원 역량 교육이 있는 회사
      • 발전하는 회사
    • 회사 분석 -> 회사 홈페이지 참고하면 좋음
      • 인재상을 찾아보고 맞춰나감 회사 = 인재상 = 나 이런 느낌
        • 보수적인 곳은 안정적이고 고객 중심~ 어쩌구 인재상. 혁신적인 곳은 혁신적인 인재상
    • 자기소개서 & 프로젝트
      • 교내 장소 예약 시스템을 만들었음.
        • 마음가짐을 어필함. 주변의 불필요한 프로세스를 변경해낼 수 있다는 점을 어필함
      • 원하는 회사의 입사 과정을 미리 알아둬야함
      • 자기소개서를 계속 반복해서 쓴다면 마지막 제출 때 더 수월해짐

 

 

2. 직장인으로서의 나

  • 회사 소개 (IBM)
    • 100년이 넘는 역사
    • 혁신적인 회사 ( 프로세스가 계속해서 변함. 분야가 매우 다양함. OMR, 컴퓨터 제작, 클라우드, 자동화 기술 ... )
  • 직무 소개
    • IT 서비스
      • 금융, 공공, 물류, 항공 산업 등
      • 항공 산업 같은 경우를 예시로 들자면 티켓 예약, 자리 예약 등의 서비스 개발
    • 임베디드 개발
      • 반도체, 자동차 등
    • 솔루션 개발
      • sw패키지, IT 솔루션 개발
      • oracle, amazon
    • SI 프로젝트
      • 새로운 시스템 구축
      • 설계부터 개발까지 많은 시간 소요
      • 많은 경험과 실력을 쌓을 수 있는 기회 그러나 야근이 많고 요구사항이 수시로 바꿈
      • 주니어 개발자에게 추천함
    • SO 프로젝트
      • 이미 구축되어 있는 시스템을 유지 & 보수
      • 업무가 정형화되어 있어서 업무파악까지 시간이 오래걸림
      • 로그를 보면서 어떤 에러가 났고 이런 에러를 방지하기 위해서 어떻게 할건지 보고서 등 작성
      • 업무에 대한 깊이를 얻을 수 있음
      • 시스템 장애가 나는 경우 부담이 큼 -> 그 시스템이 에러가 나면 업무가 일시중단
  • 주니어 개발자의 필수 역량
    • 탄탄한 기본기
      • 언어가 휙휙 바뀌므로 기본기가 중요함. 기본적인 부분의 소스코드 및 과정을 이해하고 있으면 적용이 쉬움
      • 처음 일하러 가면 소스코드를 받고 주석을 달라고 함
    • 좋은 질문
      • 내가 분석한 부분을 정리하고 부족한 부분을 설명드림
    • 업무 이해
      • 요건 분석하고 코딩

 

 

3. Q&A

  • 취업준비 관련
    • 코테 준비? -> 회사마다 다름
    • 자격증? -> 많고 적음이 합격여부를 결정하진 않고 전문성을 증명하는 수단
    • 수상경력 필요? -> 마찬가지로 그것 때문에 당락이 결정나지는 않음. 깃이나 블로그 운영으로 증명할 수 있다면 그것으로도 가능
    • 중요하게 생각하는 활동/스펙/자격증 -> 이런 것보다는 많은 경험을 쌓았으면. 사람 대 사람이니까 경험을 쌓고 인맥을 쌓는 데 사용
  • 직무 관련
    • 코딩 실력 중요? 어떤 식의 업무를 하는지? -> 요건 분석이 중요함. 그리고 보고서를 작성하기도 함. 코드 작성 뿐 아니라 다양한 업무를 함
    • 백엔드 진로 준비? -> SQL 관련된 부분 알아두기. 자신이 필요한 부분을 깊이있게
    • sw 엔지니어는 수명이 짧다? -> 모든 IT분야가 공부를 계속하지 않으면 지속성이 없음
  • 진로 관련
    • 분야가 너무 많음 -> 본인이 결정을 해야함. 깊이있게 공부를 하거나 새로운 분야를 공부해보기. 만약 정말 아닌거 같으면 진로 상담을 받아보는 것도 추천. 일단은 깊이있는 공부!
    • 막학기만 남았는데 졸업 후 취준? 휴학 취준? -> 휴학 취준. 대학생 지원 등 교육이 많기 때문에 더 유리함
  • IT 전망 관련
    • IT 분야는 트렌드를 따라가는 게 중요함 -> 요즘은 로봇기반 소프트웨어, RPA, 딥러닝, 클라우드 하지만 언제 바뀔 지 모름
  • 개별 Q&A
    • 프로젝트 경력이나 자격증 -> 개인 프로젝트 하나, 학술제와 SQLD 자격증 + 개인적인 공부가 많았음
    • 대학원 과정이 필요한 경우? -> 깊이있는 공부가 필요한 경우 ex 빅데이터나 왓슨? 등은 학사 대상으로 채용 공고가 잘 안뜸
    • IBM 한국에서 하는 일 -> 다양한 일이 많음 영업도 있고 코딩도 있고 등등...
    • 학점? -> 3.5는 넘겨야한다
    • 영어를 자주 사용? -> 아주 많음. 업무는 대부분 영어로 진행함 외국인이랑 진행하므로. 영어 되게 많이 씀
    • 프로젝트는 어디서 구함? -> 취업 사이트나 네이버 카페에서 팀원 프로젝트를 많이 구하기도 함.
    • 다른 회사는 지원 안함? -> 자신은 올인한 케이스. 여러 군데 지원하더라도 분석은 잘해야함
    • 이직? -> 주변 사람들은 이직이 많음. 좀 더 공부한 후 좋은 조건이 있다면 이직할 생각
    • 한가지 언어? 여러가지 언어? -> 개인마다 다름. 개인적으로는 한가지 언어를 깊게 공부하는게 낫다고 생각함
    • 영어공부 방법? -> 전화영어하고 있음
    • 업무강도? -> 강도 높음. 두가지 일을 하고 있음( 개발 및 시스템 이해 ). 데드라인이 정해져 있으므로 빡셈
    • 제2외국어를 했을 경우? -> 계열사에 입사하기 좋음
    • 창업활동 적는 게 좋은가? -> 회사는 딱히 탈주할거라고 생각 안할듯. 경험을 어필하는 게 좋을듯함
    • 추천하는 언어? -> 자바를 많이 씀 프로젝트마다 다르긴한데 보통 자바를 많이 사용하고 스프링 프레임워크를 잘 씀
    • 해외로 파견된적? -> 사수가 국민은행 해외지점을 통합하는 솔루션 제작하러 갔음 2주정도
    • 적당한 초봉? -> 다 다르니까 말하기 힘듦 하지만 평균연봉을 고려해봐라
    • 깃허브나 개발 블로그? -> 깃은 했었음. 필수라기 보다는 증명하거나 관리하기 편하니까 사용하면 좋음
    • 소프트웨어 전망? 머신러닝 때문에 줄어들지 않음? -> 공감은 하지만 머신러닝이 하지 못하는 일도 있음
    • 적성에 맞지 않는 것 같다면? -> 도움을 청하는 것도 방법이므로 혼자 끙끙대지 말기
    • 프로젝트 난이도 수준? -> 안하는 것보다 쉬운게 훨씬 나음. 기능들을 추가하는 식으로 퀄리티를 높여갈 수 있음
    • 오픈소스의 중요도? -> 요즘들어서는 필수임
    • 취업준비는 언제부터? -> 포트폴리오나 프로젝트는 취업 전까지 계속. 면접은 4학년부터 취업센터나 교수님 대상으로 준비해도 충분함
    • 졸업 후 공백기간 좋지않음? -> 면접관 입장에서는 공백기간이 궁금함. 그러므로 답변만 있다면 상관없음

1. 서론

  • 클라우드 서비스의 분류
    • 메모리, 서버 등의 자원을 제공하여 주는 IaaS (Infrastructure as a Service),
    • 응용 SW를 제공하여 주는 SaaS (Software as a Service),
    • 개발 플랫폼을 구축할 필요 없이 개발 시 필요한 것들을 웹에서 쉽게 활용할 수 있게 하는 PaaS (Platform as a Service)로 크게 나눌 수 있다[1]. 특히 PaaS는 SaaS의 개념을 개발 플랫폼에 확장한 방식으로 생각할 수 있다.
  • 문제 상황
    • IT 분야에서는 위 세가지 분야에서 다양한 응용을 제공하지만 로봇분야에서는 연구가 적고 SaaS 분야로 제한되고 있고 PaaS 관련 연구는 존재하지 않음. (특히 클라우드 기반 통합 개발 환경 서비스
        1. 로봇 시뮬레이션이 필요함. 2. 2개 이상의 운영체제가 사용될 수 있음.
  • 문제 상황의 해결방안.
      1. 로봇 시뮬레이션을 클라우드에서 제공함.
      1. 윈도우와 리눅스를 가상기계 상에 구현하고 자동화된 환경 설정을 해냄

 

 

2. 클라우드 기반 로봇 개발 환경

로봇 소프트웨어 개발을 위한 클라우드 아키텍쳐

  • thin 클라이언트와 통합 개발 환경, 로봇 시뮬레이터를 연결하기 위해 원격 데스크톱 공유 시스템을 사용
    • 원격 데스크톱 공유 시스템은 VNC, Metaframe, RDP 등이 존재
  • 인증 및 관리 시스템
    • 사용자 정보 관리 및 서버와의 연결 관리
    • 클라이언트의 연결 요청 시 인증 및 관리 시스템에게 사용자 정보와 함께 원하는 플랫폼 정보를 입력받고
      인증 및 관리 시스템은 그에 해당하는 서버를 클라우드 플랫폼 내에서 찾아 클라이언트에게 전송한다.
      클라이언트는 전송받은 서버 정보를 이용해 해당 서버와 연결한다.
  • 저장소
    • 사용자의 개발을 돕기 위한 다양한 자원을 저장하고 사용자의 개발 산출물 저장

 

 

제안하는 클라우드 기반 통합개발환경

  • 하드웨어를 가상화하기 위한 #하이퍼바이저# 위에 여러 가상 머신이 존재하며 그 가상 머신 위에 다양한 운영체제가 존재
    • 하이퍼바이저는 대표적으로 VMware의 ESX Server, Citrix의 XenServer, 그리고 마이크로소프트의 하이퍼-V 등이 존재

 

 

 

3. 클라우드 기반 통합개발환경의 구현

이해가 안돼서 쓸 수가 없다.... 이제부터는 그냥 단어조사만 적음

  • OPRoS : 로봇 시뮬레이터

이번 문제는 애드 훅이 주제인 문제이다.

 

애드 훅이란 뚜렷한 알고리즘이 없는 문제를 말한다.

결국 그냥 어떤 알고리즘을 응용하는 게 아닌 이 문제만의 풀이가 있다는 뜻이다.

 

 

 

우선 문제 설명을 하자면 입력으로는 N과 K가 주어진다.

1~N까지의 배열이 존재하는데 뒤쪽 K개의 수를 앞으로 이동시켰을 때,

swap과 reverse를 이용해 정확히 5번만에 원래대로 수열을 되돌려 놓을 수 있는지, 그리고 그 방법을 출력하는 문제이다.

N = 7, K = 3

 

 

4가지의 예외 상황을 제외하고는 모두 같은 풀이로 풀 수 있다.

 

같은 풀이도 여러가지가 있는데 내가 찾은 건 두 가지 방법이다.

더보기

1. 바뀐 수열에서 reverse 1 K, reverse K+1 N, reverse 1 N(여기까지 하면 오름차순 정렬), reverse 1 N, reverse 1 N

2. 바뀐 수열에서 reverse 1 N, reverse 1 N-K, reverse N-K+1 N(여기까지 하면 오름차순 정렬), reverse 1 N, reverse 1 N

 

4가지의 예외 상황은 

1. N == 2

2. N == 3

3. 1,2 상황을 제외하고 K == 1

4. 1,2 상황을 제외하고 K == N - 1

 

 

1의 경우는 그냥 swap 1 2를 5번 해주면 된다.

3의 경우는 swap 1 2, swap 2 N, reverse 2 N-1, reverse 1 N, reverse 1 N

 

4의 경우는 3의 경우와 반대로 해주면 된다.'

 

 

2의 경우는 1 2 3 이 되는 경우가 없으므로 NO인데 이는 밑의 표로 입증가능하다. 

(이건 스터디 하면서 배움..)

 

// 편안한 수열 만들기 (골드 3)
#include <iostream>

using namespace std;

int N, K;

int main() {

	cin.tie(NULL);
	cout.tie(NULL);
	ios::sync_with_stdio(false);

	cin >> N >> K;

	// 2~3
	if (N == 2) { // K = 1
		cout << "YES" << '\n'; // ( 2 1 )  1 2    2 1   1 2    2 1   1 2
		cout << "reverse " << 1 << " " << 2 << '\n';
		cout << "reverse " << 1 << " " << 2 << '\n';
		cout << "reverse " << 1 << " " << 2 << '\n';
		cout << "reverse " << 1 << " " << 2 << '\n';
		cout << "reverse " << 1 << " " << 2 << '\n';

		return 0;
	}
	else if (N == 3) {
		cout << "NO";
        
		return 0;

	}


	// 4~
	if (K == 1) {
		cout << "YES" << '\n';
		cout << "swap " << 1 << " " << 2 << '\n';
		cout << "reverse " << 2 << " " << N << '\n';
		cout << "reverse " << 2 << " " << N - 1 << '\n';
		cout << "reverse " << 1 << " " << N << '\n';
		cout << "reverse " << 1 << " " << N << '\n';
	}
	else if (K == N - 1) {
		cout << "YES" << '\n';
		cout << "swap " << N - 1 << " " << N << '\n';
		cout << "reverse " << 1 << " " << N - 1 << '\n';
		cout << "reverse " << 2 << " " << N - 1 << '\n';
		cout << "reverse " << 1 << " " << N << '\n';
		cout << "reverse " << 1 << " " << N << '\n';
	}
	else
	{
		cout << "YES" << '\n';
		cout << "reverse " << 1 << " " << K << '\n';
		cout << "reverse " << K + 1 << " " << N << '\n';
		cout << "reverse " << 1 << " " << N << '\n';
		cout << "reverse " << 1 << " " << N << '\n';
		cout << "reverse " << 1 << " " << N << '\n';
	}

	return 0;
}
// 2020KB, 0ms

 

 

#include <iostream>

using namespace std;

int N, K;

int main() {
	cin >> N >> K;


	if (N == 3) {
		cout << "NO";
		return 0;
	}

	if (N == 2) {
		cout << "YES" << '\n';
		cout << "swap " << 1 << ' ' << 2 << '\n';
		cout << "swap " << 1 << ' ' << 2 << '\n';
		cout << "swap " << 1 << ' ' << 2 << '\n';
		cout << "swap " << 1 << ' ' << 2 << '\n';
		cout << "swap " << 1 << ' ' << 2 << '\n';

		return 0;
	}

	if (K == 1) {
		cout << "YES" << '\n';
		cout << "swap " << 1 << ' ' << 2 << '\n';
		cout << "reverse " << 2 << ' ' << N << '\n';
		cout << "reverse " << 2 << ' ' << N - 1 << '\n';
		cout << "reverse " << 1 << ' ' << N << '\n';
		cout << "reverse " << 1 << ' ' << N << '\n';

	}
	else if (K == N - 1) {   
		cout << "YES" << '\n';
		cout << "swap " << N - 1 << ' ' << N << '\n'; 
		cout << "reverse " << 1 << ' ' << N - 1 << '\n'; 
		cout << "reverse " << 2 << ' ' << N - 1 << '\n'; 
		cout << "reverse " << 1 << ' ' << N << '\n'; 
		cout << "reverse " << 1 << ' ' << N << '\n';

	}
	else {
		cout << "YES" << '\n';
		cout << "reverse " << 1 << ' ' << N << '\n';
		cout << "reverse " << 1 << ' ' << N - K << '\n';
		cout << "reverse " << N - K + 1 << ' ' << N << '\n';
		cout << "reverse " << 1 << ' ' << N << '\n';
		cout << "reverse " << 1 << ' ' << N << '\n';
	}

	return 0;
}

// 2020 KB, 0ms

'coding > baekjoon_codingstudy' 카테고리의 다른 글

13460 구슬 탈출2  (0) 2022.10.02
14891 톱니바퀴 (골드 5)  (0) 2021.10.05
11657 타임머신 (골드 4)  (0) 2021.08.15
4386 별자리 만들기 (골드 4)  (0) 2021.08.15
17404 RGB 거리2 (골드 4)  (0) 2021.08.15

Input Image

inputImg

InputData

Optical Flow

  • Optical Flow란?
    영상 내 물체의 움직임 패턴

  • 가정

    1. 연속된 프레임 사이에서 움직이는 물체의 픽셀 intensity는 변함이 없다. (color constancy)
    2. 이웃하는 픽셀은 비슷한 움직임을 가진다.(Brightness Constraint)
  • Aperture Problem
    하나의 픽셀만 관찰할 경우 조건 부족으로 인해 실제 움직임을 잘못 판별하는 문제가 발생함.

  • Lucas & Kanade Method
    주변 픽셀은 유사한 움직임을 갖는다는 조건을 이용해 추가적인 equation을 구함으로써 해결함.

result

1to2

2to3

3to4

  • 범위를 1~4로 설정했을 때의 결과이다.
  • 정지해 있는 산은 변화가 적고 상대적으로 움직이거나 밝기 변화가 있는 폭포와 하늘은 변화가 큰 모습을 볼 수 있다.

Code

void MainFrame::on_button_opticalflow_clicked()
{
    int StartImgnum = ui->spinBox_StartImg->value();
    int EndImgnum = ui->spinBox_EndImg->value();

    std::vector<KImageGray> Imgvec;

    QString q_fileName;

    for(int i = StartImgnum; i <= EndImgnum; i++)
    {
        if(i<10)
        {
            q_fileName = QString::fromStdString("./data/yos.0" + std::to_string(i) + ".pgm");
        }
        else q_fileName = QString::fromStdString("./data/yos." + std::to_string(i) + ".pgm");

        ImageForm*  q_pForm = new ImageForm(q_fileName, "Open", this);
        _plpImageForm->Add(q_pForm);

        KImageGray* PGM = &q_pForm->ImageGray();//.GaussianSmoothed(2);
        Imgvec.emplace_back(*PGM);
    }

    qDebug()<<"1";

    int row = Imgvec[0].Row();
    int col = Imgvec[0].Col();

    UV** mat_uv = new UV*[row]{0,};

    for(int i = 0; i < row; i++)
    {
        mat_uv[i] = new UV[col]{ {0,0},};
    }


    //Optical_Flow(Imgvec[0],Imgvec[2],mat_uv);
    //Draw(Imgvec[0],mat_uv);


    for(int i = 0; i < Imgvec.size()-2; i++)
    {
        Optical_Flow(Imgvec[i],Imgvec[i+2],mat_uv);
        Draw(Imgvec[i],mat_uv);

        ImageForm*  q_pForm2 = new ImageForm(Imgvec[i],"Optical Flow", this);
        _plpImageForm->Add(q_pForm2);
        q_pForm2->show();
    }




}
typedef struct UV{
    double u;
    double v;
}UV;

'21-1학기 > 컴퓨터비전' 카테고리의 다른 글

7. SIFT  (0) 2021.08.19
6. Hough Transform  (0) 2021.08.19
5. Canny Edge Operator  (0) 2021.08.19
4. Gaussian noise and salt&pepper noise & Box, Gaussian, Median Filter  (0) 2021.08.19
3. Histogram Equalization & Histogram Matching  (0) 2021.08.18

Input Image

InputImg

SIFT

  • SIFT란?
    이미지의 크기와 회전에 불변하는 특징을 추출하는 알고리즘

Scale-Space

Scale-Space

sigma : 1

DOG

DOG

result

SIFT

SIFT 특징을 찾고 DB 생성

Code

void MainFrame::on_button_GSS_DOG_clicked()
{
    KImageGray icMain,Origin,Half1,Half2;



    //포커스 된 ImageForm으로부터 영상을 가져옴
    if(_q_pFormFocused != 0 && _q_pFormFocused->ImageGray().Address() &&  _q_pFormFocused->ID() == "OPEN")
    {
        icMain = _q_pFormFocused->ImageGray();
    }

    else
        return;

    KImageColor icMain2(icMain.GrayToRGB()), icMain3(icMain.GrayToRGB());

    //Scale space 생성
    double sigma = ui->Spinsigma->value();

    KImageDouble igMain(icMain);
    Origin = igMain.ToGray();
    Half1 = igMain.HalfSize().ToGray();
    Half2 = igMain.HalfSize().HalfSize().ToGray();

    std::vector<std::vector<KImageDouble>> Octave(3); //Octave1,2,3


    for(int j=0;j<5;j++)
    {
        //qDebug() <<"1";
        Octave[0].push_back(Gaussian_Filter(Origin,sigma*pow(1.41,(j-1))));
        //qDebug() <<"2";
        Octave[1].push_back(Gaussian_Filter(Half1,sigma*pow(1.41,(j-1))));
        //qDebug() <<"3";
        Octave[2].push_back(Gaussian_Filter(Half2,sigma*pow(1.41,(j-1))));

    }



    //Dog
    std::vector<std::vector<KImageDouble>> Dog(3);
    for(int i = 0; i < 3;i++)
    {
        KImageDouble Dog_img(Octave[i][0].Row(),Octave[i][0].Col());

        for(int octave=1;octave<5;octave++)
        {

            for(int ii=0;ii<Octave[i][0].Row();ii++)
            {
                for(int jj=0;jj<Octave[i][0].Col();jj++)
                {
                    Dog_img[ii][jj] = Octave[i][octave][ii][jj]-Octave[i][octave-1][ii][jj];
                }
            }

            Dog[i].push_back(Dog_img);
            //qDebug() <<Octave[i][0].Row();
        }
    }



    //KeyPoint & filtering
    std::vector<std::vector<real_key>> KeyPoint = Find_Key_Point(Dog); //루트2 시그마, 2시그마

    qDebug() << KeyPoint[0].size();
    qDebug() << KeyPoint[1].size();

    int x, y;





    Orientation(KeyPoint,Octave[0],sigma); //orientation
    KeyPoint_descriptor(KeyPoint,Octave[0],sigma);


    for(int scale = 0; scale<KeyPoint.size(); scale++) //root 2 sigma, 2 sigma
    {
        for(int i=0;i<KeyPoint[scale].size();i++)
        {
            Mark_KeyPoint(icMain, KeyPoint[scale][i].magnitude, KeyPoint[scale][i].direction, KeyPoint[scale][i].x, KeyPoint[scale][i].y);
        }
    }

    std::ofstream writeFile("sift.txt");


    for(int i = 0; i<KeyPoint.size();i++)
    {
        for(int j=0;j<KeyPoint[i].size();j++)
        {
            //KeyPoint[i][j].x " " KeyPoint[i][j].y " " KeyPoint[i][j].direction " " KeyPoint[i][j].magnitude
            //"\n"
            writeFile << KeyPoint[i][j].x << " " << KeyPoint[i][j].y << " " << KeyPoint[i][j].direction << " " << KeyPoint[i][j].magnitude
                      << "\n";


            for(int f = 0;f<4;f++)
            {
                for(int r = 0; r<KeyPoint[i][j].feature[f].size();r++)
                //KeyPoint[i][j].feature[f][r] " "
                    writeFile << KeyPoint[i][j].feature[f][r] << " ";
                writeFile << "\n";

            }
            //"\n"
            writeFile << "\n";
        }
      }




    //show

    ImageForm*  q_pForm1 = new ImageForm(icMain, "KeyPoint", this);
    _plpImageForm->Add(q_pForm1);
    q_pForm1->show();



    ImageForm*  q_pForm2 = new ImageForm(Show(Octave), "Scale-Space", this);
    _plpImageForm->Add(q_pForm2);
    q_pForm2->show();

    ImageForm*  q_pForm3 = new ImageForm(Show(Dog), "Dog", this);
    _plpImageForm->Add(q_pForm3);
    q_pForm3->show();



}
KImageDouble Gaussian_Filter(KImageGray& icMain, double sigma)
{

    KImageDouble Return(icMain);

    int row = icMain.Row();
    int col = icMain.Col();

    int Gau_filter_size = 8*sigma + 1; //filter size

    int size = std::sqrt(Gau_filter_size);

    //qDebug()<<size;

    double mask;



    for(int ii = 0; ii<row;ii++)
    {
        for(int jj = 0; jj<col;jj++)
        {

            if(ii<size || ii>=row-size || jj<size || jj>=col-size)
            {
                Return[ii][jj] = 0;
                continue;
            }
            mask = 0;


            for(int i = -size; i<size+1; i++)
            {
                for(int j = -size; j<size+1;j++)
                {
                    mask += std::exp(-0.5*((i*i+j*j)/(sigma*sigma)))*icMain[ii-i][jj-j];

                }
            }

            Return[ii][jj] = (1/(2*M_PI*sigma*sigma))*mask;
        }

    }

    //qDebug()<<"good";
    return Return;
}
KImageGray Show(std::vector<std::vector<KImageDouble>>& Octave){

    int octave_que_size = Octave.size();
    int merged_row = Octave.front().front().Row() * 3;
    int merged_col = Octave.front().front().Col() * Octave.front().size();

    if (merged_col >= 1920) {
        merged_col = 1920;
    }
    KImageGray Base(merged_row, merged_col);


    std::vector<KImageDouble> img_vec;
    KImageGray now;

    int sum_of_prev_row = 0;


    for(int octave = 0; octave<Octave.size();octave++)
    {

        img_vec = Octave[octave];

        int img_vec_size = img_vec.size(); //6
        int each_row = img_vec.front().Row(); //359
        int each_col = img_vec.front().Col(); //640
        int poor_column = 0;


        for(int img = 0; img<img_vec_size;img++)
        {

            //qDebug()<<"ok";
            now = img_vec[img].ToGray();

            for(int i=0;i<each_row;i++)
            {
                for(int j =0; j<each_col;j++)
                {
                    Base[i+sum_of_prev_row][j+(img-poor_column)*each_col] = now[i][j];
                }
            }

            // 다음에 이어 붙일 이미지가 존재하지만 모니터의 가로 길이가 부족할 때 한 칸 아래로 내림
            if (each_col - 1 + ((img - poor_column) + 1) * each_col > merged_col && img + 1 < img_vec_size)
            {
                  sum_of_prev_row += each_row;
                  poor_column = img + 1;
            }
            //qDebug()<<"end";

        }
        sum_of_prev_row += each_row;

    }


    return Base;
}
bool IsPeak(std::vector<KImageDouble>& Dog_Scale,int i,int ii, int jj)
{
    if(Dog_Scale[i][ii][jj]<0.03) return false;

    if(Dog_Scale[i][ii][jj]>0) //극대
    {
         if(Dog_Scale[i][ii][jj]<Dog_Scale[i-1][ii-1][jj-1]||Dog_Scale[i][ii][jj]<Dog_Scale[i-1][ii-1][jj]||Dog_Scale[i][ii][jj]<Dog_Scale[i-1][ii-1][jj+1]||
            Dog_Scale[i][ii][jj]<Dog_Scale[i-1][ii][jj-1]||Dog_Scale[i][ii][jj]<Dog_Scale[i-1][ii][jj]||Dog_Scale[i][ii][jj]<Dog_Scale[i-1][ii][jj+1]||
            Dog_Scale[i][ii][jj]<Dog_Scale[i-1][ii+1][jj-1]||Dog_Scale[i][ii][jj]<Dog_Scale[i-1][ii+1][jj]||Dog_Scale[i][ii][jj]<Dog_Scale[i-1][ii+1][jj+1]||


            Dog_Scale[i][ii][jj]<Dog_Scale[i][ii-1][jj-1]||Dog_Scale[i][ii][jj]<Dog_Scale[i][ii-1][jj]||Dog_Scale[i][ii][jj]<Dog_Scale[i][ii-1][jj+1]||
            Dog_Scale[i][ii][jj]<Dog_Scale[i][ii][jj-1]||                                               Dog_Scale[i][ii][jj]<Dog_Scale[i][ii][jj+1]||
            Dog_Scale[i][ii][jj]<Dog_Scale[i][ii+1][jj-1]||Dog_Scale[i][ii][jj]<Dog_Scale[i][ii+1][jj]||Dog_Scale[i][ii][jj]<Dog_Scale[i][ii+1][jj+1]||

            Dog_Scale[i][ii][jj]<Dog_Scale[i+1][ii-1][jj-1]||Dog_Scale[i][ii][jj]<Dog_Scale[i+1][ii-1][jj]||Dog_Scale[i][ii][jj]<Dog_Scale[i+1][ii-1][jj+1]||
            Dog_Scale[i][ii][jj]<Dog_Scale[i+1][ii][jj-1]||Dog_Scale[i][ii][jj]<Dog_Scale[i+1][ii][jj]||Dog_Scale[i][ii][jj]<Dog_Scale[i][ii+1][jj+1]||
            Dog_Scale[i][ii][jj]<Dog_Scale[i+1][ii+1][jj-1]||Dog_Scale[i][ii][jj]<Dog_Scale[i+1][ii+1][jj]||Dog_Scale[i][ii][jj]<Dog_Scale[i+1][ii+1][jj+1]) return false;

     }
     else
     {
         if(Dog_Scale[i][ii][jj]>Dog_Scale[i-1][ii-1][jj-1]||Dog_Scale[i][ii][jj]>Dog_Scale[i-1][ii-1][jj]||Dog_Scale[i][ii][jj]>Dog_Scale[i-1][ii-1][jj+1]||
            Dog_Scale[i][ii][jj]>Dog_Scale[i-1][ii][jj-1]||Dog_Scale[i][ii][jj]>Dog_Scale[i-1][ii][jj]||Dog_Scale[i][ii][jj]>Dog_Scale[i-1][ii][jj+1]||
            Dog_Scale[i][ii][jj]>Dog_Scale[i-1][ii+1][jj-1]||Dog_Scale[i][ii][jj]>Dog_Scale[i-1][ii+1][jj]||Dog_Scale[i][ii][jj]>Dog_Scale[i-1][ii+1][jj+1]||


            Dog_Scale[i][ii][jj]>Dog_Scale[i][ii-1][jj-1]||Dog_Scale[i][ii][jj]>Dog_Scale[i][ii-1][jj]||Dog_Scale[i][ii][jj]>Dog_Scale[i][ii-1][jj+1]||
            Dog_Scale[i][ii][jj]>Dog_Scale[i][ii][jj-1]||                                               Dog_Scale[i][ii][jj]>Dog_Scale[i][ii][jj+1]||
            Dog_Scale[i][ii][jj]>Dog_Scale[i][ii+1][jj-1]||Dog_Scale[i][ii][jj]>Dog_Scale[i][ii+1][jj]||Dog_Scale[i][ii][jj]>Dog_Scale[i][ii+1][jj+1]||

            Dog_Scale[i][ii][jj]>Dog_Scale[i+1][ii-1][jj-1]||Dog_Scale[i][ii][jj]>Dog_Scale[i+1][ii-1][jj]||Dog_Scale[i][ii][jj]>Dog_Scale[i+1][ii-1][jj+1]||
            Dog_Scale[i][ii][jj]>Dog_Scale[i+1][ii][jj-1]||Dog_Scale[i][ii][jj]>Dog_Scale[i+1][ii][jj]||Dog_Scale[i][ii][jj]>Dog_Scale[i][ii+1][jj+1]||
            Dog_Scale[i][ii][jj]>Dog_Scale[i+1][ii+1][jj-1]||Dog_Scale[i][ii][jj]>Dog_Scale[i+1][ii+1][jj]||Dog_Scale[i][ii][jj]>Dog_Scale[i+1][ii+1][jj+1]) return false;
      }

    return true;

}
std::vector<std::vector<real_key>> Find_Key_Point(std::vector<std::vector<KImageDouble>>& Dog)
{

    double thres = 1.5;
    double r = thres;
    double b = pow(r+1,2)/r;
    double Dxx,Dyy,Dxy;
    double TrH, DetH;
    double a;

    real_key key;
    std::vector<std::vector<real_key>> key_vec(2);

    //scale = 0;
    for(int dog = 0; dog<3; dog++){ //octave 별 dog 선택
        for(int i=1;i<4;i++) //scale 변화
        {
            for(int ii=1;ii<Dog[dog][i].Row()-1;ii++)
            {
                for(int jj=1;jj<Dog[dog][i].Col()-1;jj++)
                {
                    //qDebug() << dog <<" "<<ii<<jj;


                    if(IsPeak(Dog[dog],i,ii,jj)) //6개 이미지 넘겨줌
                    {


                        Dxx = Dog[dog][i][ii][jj+1]+Dog[dog][i][ii][jj-1]-2*Dog[dog][i][ii][jj];
                        Dyy = Dog[dog][i][ii+1][jj]+Dog[dog][i][ii-1][jj]-2*Dog[dog][i][ii][jj];
                        Dxy = ((Dog[dog][i][ii+1][jj+1]-Dog[dog][i][ii+1][jj-1]) - (Dog[dog][i][ii-1][jj+1] - Dog[dog][i][ii-1][jj-1]))/4.0;
                        TrH = Dxx + Dyy;
                        DetH = Dxx*Dyy - Dxy*Dxy;

                        a = TrH*TrH/DetH;

                        //qDebug() << Dxx<<" "<<Dyy<<" "<<" " <<Dxy<<" " << DetH;
                        if(DetH<=0) continue;
                        //if(a < b) KeyPoint[ii][jj] = 255;
                        if(a >= b) continue;

                        //KeyPoint
                        key.x = ii*pow(2,dog);
                        key.y = jj*pow(2,dog);
                        key.scale = i;


                        key_vec[i-1].push_back(key);

                    }

                }

            }

        }
    }

    return key_vec;
}
void Orientation(std::vector<std::vector<real_key>>& KeyPoint, std::vector<KImageDouble> Octave_0,double sigma)
{

    KImageDouble Scale_img;
    int x,y;
    double mag;
    double phase;
    double gau_kenel;

    double bucket[36] = {0,};

    int x_t,y_t;

    int index;

    double max = 0.;
    int dir;

    //int cnt = 1;

    int KeyPoint_scale_size = 0;

    for(int scale = 0; scale<KeyPoint.size(); scale++) //root 2 sigma, 2 sigma
    {
        Scale_img = Octave_0[scale+1]; //origin roo2, origin 2
        sigma = sigma*pow(1.4,scale+1);
        KeyPoint_scale_size = KeyPoint[scale].size();
        for(int i=0;i<KeyPoint_scale_size;i++)
        {
            x = KeyPoint[scale][i].x;
            y = KeyPoint[scale][i].y;

            //16개 좌표 x+w_x, y+w_y
            for(int w_x = -5; w_x < 6; w_x++)
            {
                for(int w_y = -5; w_y < 6; w_y++)
                {
                    x_t = x+w_x;
                    y_t = y+w_y;

                    if(x_t>Scale_img.Row()-2||x_t<1||y_t>Scale_img.Col()-2||y_t<1) continue;
                    gau_kenel = 1./(2*M_PI*sigma*sigma) * exp(-0.5*(w_x*w_x + w_y*w_y)/(sigma*sigma));

                    mag = gau_kenel * sqrt(pow(Scale_img[x_t+1][y_t]-Scale_img[x_t-1][y_t],2)
                                       +pow(Scale_img[x_t][y_t+1]-Scale_img[x_t][y_t-1],2));

                    phase = atan2(Scale_img[x_t][y_t+1]-Scale_img[x_t][y_t-1],
                                  Scale_img[x_t+1][y_t]-Scale_img[x_t-1][y_t])*180/M_PI+180.0;

                    //if(phase>360) phase -= 360;

                    //if(phase<0) phase += 180;

                    index = phase/10;

                    bucket[index] += mag;

                    //qDebug() << scale << " " << i << " " << x << " " << y << " " << x_t << " " << y_t << " " << index << mag;
                    //qDebug() << index;
                }
            }

            for(int b = 0; b<36; b++)
            {
                if(bucket[b]>max)
                {
                    max = bucket[b];
                    dir = b*10;
                }

                qDebug()<< b << " " << bucket[b];
            }

            //qDebug()<< dir << " " << max;

            for(int b = 0; b<36; b++)
            {
                if(bucket[b]>max*0.8&& b != dir)
                {
                    KeyPoint[scale].push_back({KeyPoint[scale][i].x,KeyPoint[scale][i].y,(double)b*10,bucket[b]});
                   //qDebug()<< b << " " << bucket[b];
                }

                //qDebug()<< b << " " << bucket[b];
            }


            KeyPoint[scale][i].direction = dir;
            KeyPoint[scale][i].magnitude = max;
            //qDebug()<< cnt << " " << dir << bucket[8] << bucket[9];
            //cnt++;


        }

    }


}
void KeyPoint_descriptor(std::vector<std::vector<real_key>>& KeyPoint, std::vector<KImageDouble> Octave_0,double sigma)
{

    KImageDouble Scale_img;
    int x,y;
    double mag[81];
    double phase[81];
    double gau_kenel;

    double bucket[8] = {0,};

    int x_t,y_t;




    //int cnt = 1;

    int KeyPoint_scale_size = 0;

    for(int scale = 0; scale<KeyPoint.size(); scale++) //root 2 sigma, 2 sigma
    {
        Scale_img = Octave_0[scale+1]; //origin roo2, origin 2
        sigma = sigma*pow(1.4,scale+1);
        KeyPoint_scale_size = KeyPoint[scale].size();
        for(int i=0;i<KeyPoint_scale_size;i++)
        {


            x = KeyPoint[scale][i].x;
            y = KeyPoint[scale][i].y;

            //11개 좌표 x+w_x, y+w_y
            for(int w_x = -4; w_x < 5; w_x++)
            {
                for(int w_y = -4; w_y < 5; w_y++)
                {
                    x_t = x+w_x;
                    y_t = y+w_y;

                    if(x_t>Scale_img.Row()-2||x_t<1||y_t>Scale_img.Col()-2||y_t<1) continue;
                    gau_kenel = 1./(2*M_PI*sigma*sigma) * exp(-0.5*(w_x*w_x + w_y*w_y)/(sigma*sigma));

                    mag[(w_x+4)*8+w_y+4] = gau_kenel * sqrt(pow(Scale_img[x_t+1][y_t]-Scale_img[x_t-1][y_t],2)
                                                            +pow(Scale_img[x_t][y_t+1]-Scale_img[x_t][y_t-1],2));

                    phase[(w_x+4)*8+w_y+4] = atan2(Scale_img[x_t][y_t+1]-Scale_img[x_t][y_t-1],
                                                    Scale_img[x_t+1][y_t]-Scale_img[x_t-1][y_t])*180/M_PI+180.0;

                }
            }

            //index = phase/45
            for(int p = 0; p < 2; p++)
            {
                std::vector<double> dou_vec;

                bucket[(int)phase[0+5*p]/45] += mag[0+5*p];
                bucket[(int)phase[1+5*p]/45] += mag[1+5*p];
                bucket[(int)phase[2+5*p]/45] += mag[2+5*p];
                bucket[(int)phase[3+5*p]/45] += mag[3+5*p];

                bucket[(int)phase[9+5*p]/45] += mag[9+5*p];
                bucket[(int)phase[10+5*p]/45] += mag[10+5*p];
                bucket[(int)phase[11+5*p]/45] += mag[11+5*p];
                bucket[(int)phase[12+5*p]/45] += mag[12+5*p];

                bucket[(int)phase[18+5*p]/45] += mag[18+5*p];
                bucket[(int)phase[19+5*p]/45] += mag[19+5*p];
                bucket[(int)phase[20+5*p]/45] += mag[20+5*p];
                bucket[(int)phase[21+5*p]/45] += mag[21+5*p];

                bucket[(int)phase[27+5*p]/45] += mag[27+5*p];
                bucket[(int)phase[28+5*p]/45] += mag[28+5*p];
                bucket[(int)phase[29+5*p]/45] += mag[29+5*p];
                bucket[(int)phase[30+5*p]/45] += mag[30+5*p];


                for(int b = 0;b<8;b++)
                {
                    dou_vec.push_back(bucket[b]);
                }

                KeyPoint[scale][i].feature.push_back(dou_vec);
            }


            for(int p = 0; p < 2; p++)
            {
                std::vector<double> dou_vec;

                bucket[(int)phase[45+5*p]/45] += mag[45+5*p];
                bucket[(int)phase[46+5*p]/45] += mag[46+5*p];
                bucket[(int)phase[47+5*p]/45] += mag[47+5*p];
                bucket[(int)phase[48+5*p]/45] += mag[48+5*p];

                bucket[(int)phase[54+5*p]/45] += mag[54+5*p];
                bucket[(int)phase[55+5*p]/45] += mag[55+5*p];
                bucket[(int)phase[56+5*p]/45] += mag[56+5*p];
                bucket[(int)phase[57+5*p]/45] += mag[57+5*p];

                bucket[(int)phase[63+5*p]/45] += mag[63+5*p];
                bucket[(int)phase[64+5*p]/45] += mag[64+5*p];
                bucket[(int)phase[65+5*p]/45] += mag[65+5*p];
                bucket[(int)phase[66+5*p]/45] += mag[66+5*p];

                bucket[(int)phase[72+5*p]/45] += mag[72+5*p];
                bucket[(int)phase[73+5*p]/45] += mag[73+5*p];
                bucket[(int)phase[74+5*p]/45] += mag[74+5*p];
                bucket[(int)phase[75+5*p]/45] += mag[75+5*p];


                for(int b = 0;b<8;b++)
                {
                    dou_vec.push_back(bucket[b]);
                }

                KeyPoint[scale][i].feature.push_back(dou_vec);
            }


        }

    }


}
void Mark_KeyPoint(KImageGray &igScale, double mag, double ori_deg, int pX, int pY){
    double dX_circle , dY_circle;
    int iX_circle, iY_circle;

    double radius = 0;

    int iX_dir, iY_dir;
    double dX_dir, dY_dir;

    double custom = 4;

    int tmp = (int)(mag / custom);
    if(tmp < 2 ){
        radius = 5;
    }
    else if(tmp < 5){
        radius = 7;
    }
    else if(tmp < 10){
        radius = 8;
    }
    else{
        radius = 10;
    }

    double theta_rad;
    //Draw circle
    for(int angle = 0; angle <= 360; angle+= 1){

        theta_rad = (double)angle * 0.01745329; // 0.017453..=>1 / 180 * 3.14592; degree to rad

        dX_circle = (double)pX - (double)(radius * cos(theta_rad));
        dY_circle = (double)pY - (double)(radius * sin(theta_rad));

        iX_circle = (int)dX_circle; iY_circle = (int)dY_circle;

        if(iX_circle > 0 && iY_circle > 0 && iX_circle < (int)igScale.Row() && iY_circle < (int)igScale.Col()){
           igScale[iX_circle][iY_circle] = 255;
        }
    }

    //Draw Direction
    for(int range = 0; range <= radius; range++){
        theta_rad = ori_deg * 0.01745329; // 0.017453..=>1 / 180 * 3.14592; degree to rad

        dX_dir = (double)pX - (double)(range * cos(theta_rad));
        dY_dir = (double)pY - (double)(range * sin(theta_rad));

        iX_dir = (int)dX_dir; iY_dir = (int)dY_dir;

        if(iX_dir > 0 && iY_dir > 0 && iX_dir < (int)igScale.Row() && iY_dir < (int)igScale.Col()){
           igScale[iX_dir][iY_dir] = 255;
        }

    }

}
typedef struct real{
    int x,y;
    double scale;
    double direction = -1;
    double magnitude = -1;
    std::vector<std::vector<double>> feature;
}real_key;

Input Image

InputImg

  • Circle Hough Transform : 반지름이 주어져 있음
  • General Hough Transform : 도형의 모양이 좌표로 주어져 있음

Hough Transform

  • Hough Trnasform이란?
    기본적인 아이디어는 voting이다.
    1. 우선 찾으려는 도형의 파라미터를 변수로 정한다. ( 파라미터를 잘못 정할 경우 옳지 않은 경우가 생길 수 있으므로 모든 경우를 나타낼 수 있는 파라미터를 선정해야 한다.)
    2. 원래의 이미지의 한 점에서 구할 수 있는 모든 경우의 수를 파라미터 공간에 voting한다.
    3. voting이 가장 많은 파라미터가 우리가 구하려는 도형의 파라미터 변수로 정해진다.

Circle Hough Transform

  1. circle edge를 찾는 것이기 때문에 edge 점들에서만 voting을 해야한다. 이를 위해 canny edge operator 함수를 이용해 edge를 구한다.
  2. 반지름이 103 pixel로 주어져 있으므로 파라미터는 원의 중심점(a,b)이다.
  3. edge 점을 지나고 103pixel을 가진 원의 중심을 파라미터 공간에 voting한다.
  4. voting이 가장 많은 점을 원의 중심으로 해서 103 pixel의 반지름을 가진 원을 표시한다.

result

CircleImg

Code

void MainFrame::on_button_CircleHough_clicked()
{
    KImageGray icMain;

    //포커스 된 ImageForm으로부터 영상을 가져옴
    if(_q_pFormFocused != 0 && _q_pFormFocused->ImageGray().Address() &&  _q_pFormFocused->ID() == "OPEN")
    {
        icMain = _q_pFormFocused->ImageGray();
    }
    else
        return;

    KImageGray icMain2(icMain.Row(),icMain.Col());

    //canny edge operator
    double sigma = ui->SpinBox_sigma->value();

    Canny_Edge(icMain,sigma);

    //Hough Transform
    int** voting = new int*[icMain.Row()]{0,};
    for(int i = 0; i<icMain.Row(); i++)
    {
        voting[i] = new int[icMain.Col()]{0,};
    }





    std::vector<Location> edge;
    Location location;



        //Edge 좌표 받아오기
    for(int ii = 0; ii<icMain.Row();ii++){
        for(int jj = 0; jj<icMain.Col();jj++){

            if(icMain[ii][jj] == 255)
            {
                location.x = ii;
                location.y = jj;
                edge.push_back(location);

                 //qDebug() << "x : " << ii << "y : " << jj;
            }

        }
    }


        //voting
    int x,y;
    int a,b;
    for(int i=0;i<edge.size();i++)
    {
        x = edge[i].x;
        y = edge[i].y;

        //qDebug() << "x : " << x << "y : " << y;

        for(int j=0;j<360;j++){
            a = x-51.5*cos(j*M_PI/180);
            b = y-51.5*sin(j*M_PI/180);


            if(a>0 && a<icMain.Row() && b>0 && b<icMain.Col())
            {
                voting[a][b]++;
                if(icMain2[a][b]+30 < 256)
                    icMain2[a][b] += 30;
                else icMain2[a][b]  = 255;
            }


        }


    }



        //max voting -> 원의 중심점 찾기
    int max = 0;

    for(int ii = 0; ii<icMain.Row();ii++){
        for(int jj = 0; jj<icMain.Col();jj++){
            if(voting[ii][jj]>max){
                a = ii;
                b = jj;
                max = voting[ii][jj];
            }
        }
    }

    for(int ii = 0; ii<icMain.Row();ii++){
        for(int jj = 0; jj<icMain.Col();jj++){
            icMain[ii][jj] = 0;
            //icMain2[ii][jj] = 0;
        }
    }


        //circle
    for(int j=0;j<360;j++)
    {
        x = a-51.5*cos(j*M_PI/180);
        y = b-51.5*sin(j*M_PI/180);
        icMain[x][y] = 255;
    }





    //Show
    ImageForm*  q_pForm1 = new ImageForm(icMain, "Circle Hough Transform", this);
    _plpImageForm->Add(q_pForm1);
    q_pForm1->show();

    ImageForm*  q_pForm2 = new ImageForm(icMain2, "Circle Hough Transform_voting", this);
    _plpImageForm->Add(q_pForm2);
    q_pForm2->show();


}

General Hough Transform

  • 크기와 회전 각도가 고정일 때
    • 파라미터 3개
    • voting 공간 : X_c, Y_c
    1. 도형의 중심점을 잡고 파라미터 테이블을 만든다. 이 때 Edge Direction을 key로 사용한다.
    2. 아래의 수식을 이용해 각 edge에서 table를 돌면서 나올 수 있는 X_c와 Y_c의 경우를 파라미터 공간에 voting한다.

  • 크기와 회전 각도가 바뀔 때
    • 파라미터 5개
    • voting 공간 : X_c, Y_c, s, theta
    1. 크기와 회전 각도가 고정일 때와 같은 파라미터 테이블을 만든다.
    2. 위의 그림과 같이 voting 한다.

result

GeneralImg

Code

void MainFrame::on_button_GeneralHough_clicked()
{
    KImageGray icMain;
    //포커스 된 ImageForm으로부터 영상을 가져옴
    if(_q_pFormFocused != 0 && _q_pFormFocused->ImageGray().Address() &&  _q_pFormFocused->ID() == "OPEN")
    {
        icMain = _q_pFormFocused->ImageGray();
    }
    else
        return;

    KImageGray icMain2(icMain.Row(),icMain.Col());

    int f_row[86] = {0,};
    int f_col[86] = {0,};

    //파일 읽기
    std::ifstream readFile;
    readFile.open("C:/Qt/plug.txt");
    int fx,fy,i=0;

    while(!readFile.eof()){
        readFile>>fx>>fy;
        f_col[i] = fy;
        f_row[i] = fx;
        i++;
    }




    //table
    int Xc=0, Yc=0; //중심점
    for(int i=0; i<86;i++)
    {
        Xc += f_row[i];
        Yc += f_col[i];
    }
    Xc /= 86;
    Yc /= 86;


    typedef struct r{
        double Ri,Ai;
    }R;

    std::vector<R> table[4];
    R t;
    int Xi,Yi;

        //Grad 구하기
    double dTmp, dGradX, dGradY;
    int dir;

    for(int j = 1,jj = 86-2; jj; j++, jj--)
    {
        dGradX = (float)(f_row[j+1] - f_row[j-1]) + 1e-8;
        dGradY = (float)(f_col[j+1] - f_col[j-1]) + 1e-8;

        dTmp = (180.0/M_PI)*(atan2(dGradY,dGradX)) + 90;
        dir = ((((int)(dTmp/22.5) + 1) >>1) & 0x00000003);

        //qDebug() << f_row[j] << " " << f_col[j] << " " <<dir;

        t.Ri = sqrt((Xc - f_row[j])*(Xc - f_row[j]) + (Yc - f_col[j])*(Yc - f_col[j]));
        t.Ai = (180.0/M_PI) * atan2((double)(Yc - f_col[j]),(double)(Xc - f_row[j]));

        table[dir].push_back(t);

    }


    //canny edge
    double sigma = ui->SpinBox_sigma->value();

    Canny_Edge(icMain,sigma);

    std::vector<Location> edge;
    Location location;

    for(int i=0;i<icMain.Row();i++)
    {
        for(int j=0;j<icMain.Col();j++)
        {
            if(icMain[i][j] == 255)
            {
                location.x = i;
                location.y = j;
                edge.push_back(location);
                //qDebug() << "x : " << i << "y : " << j;
            }
        }
    }




    //Hough Transform
    int** voting = new int*[icMain.Row()]{0,};
    for(int i = 0; i<icMain.Row(); i++)
    {
        voting[i] = new int[icMain.Col()]{0,};
    }


    /*
    for(int i=0; i < 4; i++)
    {
        qDebug() << i << " " << table[i].size();
    }
    //qDebug() << edge.size();
    */

        //voting
    for(int i = 0; i < edge.size(); i++)
    {
        Xi = edge[i].x;
        Yi = edge[i].y;

            //qDebug() << "ok";
        for(int j=0;j<4;j++)
        {

           for(int k=0;k<table[j].size();k++)
           {

                Xc = Xi - 0.85*table[j][k].Ri*cos(table[j][k].Ai*M_PI/180);
                Yc = Yi - 0.85*table[j][k].Ri*sin(table[j][k].Ai*M_PI/180);

                //qDebug() << "i : " << i << "j : " << j << "k : " << k << "x : " << Xc << "y : " << Yc;


                if(Xc>0 && Xc<icMain.Row() && Yc>0 && Yc<icMain.Col())
                {
                    voting[Xc][Yc]++;
                    if(icMain2[Xc][Yc]+50 < 256)
                        icMain2[Xc][Yc] += 50;
                    else icMain2[Xc][Yc]  = 255;
                }

           }

        }

    }



        //max voting -> 원의 중심점 찾기
    int max = 0;

    for(int ii = 0; ii<icMain.Row();ii++){
        for(int jj = 0; jj<icMain.Col();jj++){
            if(voting[ii][jj]>max){
                Xc = ii;
                Yc = jj;
                max = voting[ii][jj];
            }
        }
    }

    for(int ii = 0; ii<icMain.Row();ii++){
        for(int jj = 0; jj<icMain.Col();jj++){
            icMain[ii][jj] = 0;
        }
    }


        //find
    for(int j = 0; j < 4; j++)
    {
        for(int k = 0; k<table[j].size();k++)
        {
            Xi = Xc + 0.85*table[j][k].Ri*cos(table[j][k].Ai*M_PI/180);
            Yi = Yc + 0.85*table[j][k].Ri*sin(table[j][k].Ai*M_PI/180);

            if(Xi>0 && Xi<icMain.Row() && Yi>0 && Yi<icMain.Col())
            {
                icMain[Xi][Yi] = 255;
            }
        }
    }





    //Show
    ImageForm*  q_pForm1 = new ImageForm(icMain, "General Hough Transform", this);
    _plpImageForm->Add(q_pForm1);
    q_pForm1->show();

    ImageForm*  q_pForm2 = new ImageForm(icMain2, "General Hough Transform_voting", this);
    _plpImageForm->Add(q_pForm2);
    q_pForm2->show();

}






함수
void Canny_Edge(KImageGray& imgSrc,double sigma)
{
    KImageGray icMain = imgSrc;


    int row = icMain.Row();
    int col = icMain.Col();

    //Gaussian Filter

    int Gau_filter_size = 8*sigma + 1; //filter size

    int size = std::sqrt(Gau_filter_size);

    //qDebug()<<size;

    double mask;


    for(int ii = size; ii<row-size;ii++){
        for(int jj = size; jj<col-size;jj++){

            mask = 0;


            for(int i = -size; i<size+1; i++){
                for(int j = -size; j<size+1;j++){
                    mask += std::exp(-0.5*((i*i+j*j)/(sigma*sigma)))*icMain[ii-i][jj-j];

                }
            }

            icMain[ii][jj] = (1/(2*M_PI*sigma*sigma))*mask;
        }
    }

    //Sobel Mask

    int dLow = 80;
    int dHigh = 120;
    double mag_x;
    double mag_y;
    double phase;

    double** magnitude = new double*[row]{0,};
    int** direction = new int*[row] {0,};
    for(int i = 0; i < row; i++)
    {
        magnitude[i] = new double[col]{0,};  // magnitude
        direction[i] = new int[col]{0,};
    }

    double Mask_w[3][3] = { {-1., 0., 1.}, {-2., 0., 2.}, {-1., 0. ,1.} };
    double Mask_h[3][3] = { {1., 2., 1.}, {0., 0., 0.}, {-1., -2. ,-1.} };

    //qDebug()<<row<< " "<<col;


    //qDebug()<<(unsigned char)((((int)(113/22.5)+1)>>1) & 0x00000003);

    for(int ii = 1; ii<row-1;ii++){
        for(int jj = 1; jj<col-1;jj++){

            mag_x = 0;
            mag_y = 0;

            for(int i = -1; i<2; i++){
                for(int j = -1; j<2;j++){
                    mag_x += (Mask_w[i+1][j+1]*icMain[ii+i][jj+j]);
                    mag_y += (Mask_h[i+1][j+1]*icMain[ii+i][jj+j]);
                }
            }

            magnitude[ii][jj] = fabs(mag_x) + fabs(mag_y);

            if(magnitude[ii][jj] > dLow)
            {
                //icMain2[ii][jj] = 255;
                phase = atan2(mag_x,mag_y)*180/M_PI;
                if(phase>360) phase -= 360;

                if(phase<0) phase += 180;

                direction[ii][jj] = (unsigned char)((((int)(phase/22.5)+1)>>1) & 0x00000003);


                //qDebug()<<ii<<" "<<jj<<" "<<phase<<" "<<direction[ii][jj];


            }
            else {
                magnitude[ii][jj] = 0;
                //icMain2[ii][jj] = 0;
            }

        }
    }


    // Non-Maxima Suppression
    //int nDx[4] = {0, 1, 1, 1};
    //int nDy[4] = {1, 1, 0, -1};
    int nDx[4] = {0, 1, 1, -1};
    int nDy[4] = {1, 1, 0, 1};

    double** buffer = new double*[row] {0,};
    int** realedge = new int*[row]{0,};
    for(int i = 0; i < row; i++)
    {
        buffer[i] = new double[col]{0,};
        realedge[i] = new int[col]{0,};
    }

    class HighEdge{
        public:
            int x;
            int y;
    };

    HighEdge h;
    std::stack<HighEdge> highedge;

    for(int ii = 1; ii<row-1;ii++){
        for(int jj = 1; jj<col-1;jj++){
            if(magnitude[ii][jj] == 0) continue;

            //if(magnitude[ii][jj] > magnitude[ii+nDx[direction[ii][jj]]][jj+nDy[direction[ii][jj]]] && magnitude[ii][jj] > magnitude[ii-nDx[direction[ii][jj]]][jj-nDy[direction[ii][jj]]])
            if(magnitude[ii][jj] > magnitude[ii+nDy[direction[ii][jj]]][jj+nDx[direction[ii][jj]]] && magnitude[ii][jj] > magnitude[ii-nDy[direction[ii][jj]]][jj-nDx[direction[ii][jj]]])
            {
                if(magnitude[ii][jj] > dHigh)
                {
                    h.x = ii;
                    h.y = jj;

                    highedge.push(h);
                    realedge[ii][jj] = 255;

                }

                //realedge[ii][jj] = 255;
                buffer[ii][jj] = magnitude[ii][jj];
            }
        }
    }


    for(int ii = 1; ii<row-1;ii++){
        for(int jj = 1; jj<col-1;jj++){
            //icMain2[ii][jj] = realedge[ii][jj];
        }
    }


    //Thresholding
    int x,y;
    while(!highedge.empty())
    {
        x = highedge.top().x;
        y = highedge.top().y;
        for(int i = -1; i < 2; i++)
        {
            for(int j = -1; j < 2; j++)
            {
                if(buffer[x+i][y+j] && buffer[x+i][y+j]<=dHigh)
                {
                    h.x = x+i;
                    h.y = y+j;
                    highedge.push(h);
                    realedge[x+i][y+j] = 255;
                    buffer[x+i][y+j] = 0;
                }
            }
        }
        highedge.pop();
    }

    for(int ii = 1; ii<row-1;ii++){
        for(int jj = 1; jj<col-1;jj++){
            imgSrc[ii][jj] = realedge[ii][jj];
        }
    }

    delete[] direction;
    delete[] magnitude;
}

'21-1학기 > 컴퓨터비전' 카테고리의 다른 글

8. Optical Flow  (0) 2021.08.19
7. SIFT  (0) 2021.08.19
5. Canny Edge Operator  (0) 2021.08.19
4. Gaussian noise and salt&pepper noise & Box, Gaussian, Median Filter  (0) 2021.08.19
3. Histogram Equalization & Histogram Matching  (0) 2021.08.18

Canny Edge Operator

Input Image

InputImg

Canny Edge Operator

  1. 노이즈 제거를 위한 smoothing
  2. Sobel mask를 이용한 edge 후보 찾기
  3. Non-Maxima Suppression
  4. Hysteresis Thresholding
    1. Sobel mask를 이용한 edge 후보 찾기
      • magnitude를 이용해 edge의 direction을 구한다.
    1. Non-Maxima Suppression
      • Local Maxima를 선택해서 얇은 edge를 구할 수 있게 하는 과정
      • 2에서 구한 direction을 통해서 edge 후보의 양옆과 비교를 해서 최대인 경우 남김
    1. Hysteresis Thresholding
      • edge의 끊김 방지를 위해 Low Threshold를 넘고 주변에 edge가 있다면 edge로

result

resultImg

Code

void MainFrame::on_button_CannyEdge_clicked()
{

    KImageGray icMain;
    KImageGray icMain2;

    //포커스 된 ImageForm으로부터 영상을 가져옴
    if(_q_pFormFocused != 0 && _q_pFormFocused->ImageGray().Address() &&  _q_pFormFocused->ID() == "OPEN")
    {
        icMain = _q_pFormFocused->ImageGray();
        icMain2 = _q_pFormFocused->ImageGray();
    }
    else
        return;

    int row = icMain.Row();
    int col = icMain.Col();

    //Gaussian smoothing
    double sigma = ui->SpinBox_sigma->value();

    int Gau_filter_size = 8*sigma + 1; //filter size

    int size = std::sqrt(Gau_filter_size);

    //qDebug()<<size;

    double mask;


    for(int ii = size; ii<row-size;ii++){
        for(int jj = size; jj<col-size;jj++){

            mask = 0;


            for(int i = -size; i<size+1; i++){
                for(int j = -size; j<size+1;j++){
                    mask += std::exp(-0.5*((i*i+j*j)/(sigma*sigma)))*icMain[ii-i][jj-j];

                }
            }

            icMain[ii][jj] = (1/(2*M_PI*sigma*sigma))*mask;
        }
    }

    //Sobel Mask

    int dLow = 80;
    int dHigh = 120;
    double mag_x;
    double mag_y;
    double phase;

    double** magnitude = new double*[row]{0,};
    int** direction = new int*[row] {0,};
    for(int i = 0; i < row; i++)
    {
        magnitude[i] = new double[col]{0,};  // magnitude
        direction[i] = new int[col]{0,};
    }

    double Mask_w[3][3] = { {-1., 0., 1.}, {-2., 0., 2.}, {-1., 0. ,1.} };
    double Mask_h[3][3] = { {1., 2., 1.}, {0., 0., 0.}, {-1., -2. ,-1.} };

    //qDebug()<<row<< " "<<col;


    qDebug()<<(unsigned char)((((int)(113/22.5)+1)>>1) & 0x00000003);

    for(int ii = 1; ii<row-1;ii++){
        for(int jj = 1; jj<col-1;jj++){

            mag_x = 0;
            mag_y = 0;

            for(int i = -1; i<2; i++){
                for(int j = -1; j<2;j++){
                    mag_x += (Mask_w[i+1][j+1]*icMain[ii+i][jj+j]);
                    mag_y += (Mask_h[i+1][j+1]*icMain[ii+i][jj+j]);
                }
            }

            magnitude[ii][jj] = fabs(mag_x) + fabs(mag_y);

            if(magnitude[ii][jj] > dLow)
            {
                //icMain2[ii][jj] = 255;
                phase = atan2(mag_x,mag_y)*180/M_PI;
                if(phase>360) phase -= 360;

                if(phase<0) phase += 180;

                direction[ii][jj] = (unsigned char)((((int)(phase/22.5)+1)>>1) & 0x00000003);


                qDebug()<<ii<<" "<<jj<<" "<<phase<<" "<<direction[ii][jj];


            }
            else {
                magnitude[ii][jj] = 0;
                //icMain2[ii][jj] = 0;
            }

        }
    }


    // Non-Maxima Suppression
    //int nDx[4] = {0, 1, 1, 1};
    //int nDy[4] = {1, 1, 0, -1};
    int nDx[4] = {0, 1, 1, -1};
    int nDy[4] = {1, 1, 0, 1};

    double** buffer = new double*[row] {0,};
    int** realedge = new int*[row]{0,};
    for(int i = 0; i < row; i++)
    {
        buffer[i] = new double[col]{0,};
        realedge[i] = new int[col]{0,};
    }

    class HighEdge{
        public:
            int x;
            int y;
    };

    HighEdge h;
    std::stack<HighEdge> highedge;

    for(int ii = 1; ii<row-1;ii++){
        for(int jj = 1; jj<col-1;jj++){
            if(magnitude[ii][jj] == 0) continue;

            //if(magnitude[ii][jj] > magnitude[ii+nDx[direction[ii][jj]]][jj+nDy[direction[ii][jj]]] && magnitude[ii][jj] > magnitude[ii-nDx[direction[ii][jj]]][jj-nDy[direction[ii][jj]]])
            if(magnitude[ii][jj] > magnitude[ii+nDy[direction[ii][jj]]][jj+nDx[direction[ii][jj]]] && magnitude[ii][jj] > magnitude[ii-nDy[direction[ii][jj]]][jj-nDx[direction[ii][jj]]])
            {
                if(magnitude[ii][jj] > dHigh)
                {
                    h.x = ii;
                    h.y = jj;

                    highedge.push(h);
                    realedge[ii][jj] = 255;

                }

                //realedge[ii][jj] = 255;
                buffer[ii][jj] = magnitude[ii][jj];
            }
        }
    }


    for(int ii = 1; ii<row-1;ii++){
        for(int jj = 1; jj<col-1;jj++){
            //icMain2[ii][jj] = realedge[ii][jj];
        }
    }


    //Thresholding
    int x,y;
    while(!highedge.empty())
    {
        x = highedge.top().x;
        y = highedge.top().y;
        for(int i = -1; i < 2; i++)
        {
            for(int j = -1; j < 2; j++)
            {
                if(buffer[x+i][y+j] && buffer[x+i][y+j]<=dHigh)
                {
                    h.x = x+i;
                    h.y = y+j;
                    highedge.push(h);
                    realedge[x+i][y+j] = 255;
                    buffer[x+i][y+j] = 0;
                }
            }
        }
        highedge.pop();
    }

    for(int ii = 1; ii<row-1;ii++){
        for(int jj = 1; jj<col-1;jj++){
            icMain2[ii][jj] = realedge[ii][jj];
        }
    }

    //Show
    ImageForm*  q_pForm1 = new ImageForm(icMain2, "Canny Edge Operator", this);
    _plpImageForm->Add(q_pForm1);
    q_pForm1->show();

}

Noise

Input Image

InputImg

Gaussian Noise

  • Gaussian Noise란?
    • 정규분포 Noise, 각 픽셀과 관련없이 독립적으로 생김.
    • 아래 수식에서 n은 정규분포를 가지는 잡음

Salt&Pepper Noise

  • Salt&Pepper Noise란?
    • impulsive noise or spike noise
    • 0 또는 1이 랜덤하게 생김

result

noise

Code

//포커스 된 ImageForm으로부터 영상을 가져옴
    KImageColor Gaussian_Noise;
    KImageColor Salt_Noise;

    //포커스 된 ImageForm으로부터 영상을 가져옴
    if(_q_pFormFocused != 0 && _q_pFormFocused->ImageColor().Address() &&  _q_pFormFocused->ID() == "OPEN")
    {
        Gaussian_Noise = _q_pFormFocused->ImageColor();
        Salt_Noise = _q_pFormFocused->ImageColor();
    }
    else
        return;




    //Gaussian Noise 생성
    double sigma = ui->spin_GaussianParameter->value();

    KGaussian GauN;
    GauN.Create(0, sigma*sigma);
    GauN.OnRandom(Gaussian_Noise.Size());

    double dNoise = 0;
    double Kp = 8;
    for(int i = 0; i<Gaussian_Noise.Row();i++){
        for(int j = 0; j<Gaussian_Noise.Col();j++){
            dNoise = GauN.Generate();

            if(Gaussian_Noise[i][j].r + dNoise*Kp > 255)
                Gaussian_Noise[i][j].r = 255;
            else if(Gaussian_Noise[i][j].r +dNoise*Kp < 0)
                Gaussian_Noise[i][j].r = 0;
            else
                Gaussian_Noise[i][j].r += dNoise*Kp;

            if(Gaussian_Noise[i][j].g + dNoise*Kp > 255)
                Gaussian_Noise[i][j].g = 255;
            else if(Gaussian_Noise[i][j].g +dNoise*Kp < 0)
                Gaussian_Noise[i][j].g = 0;
            else
                Gaussian_Noise[i][j].g += dNoise*Kp;

            if(Gaussian_Noise[i][j].b + dNoise*Kp > 255)
                Gaussian_Noise[i][j].b = 255;
            else if(Gaussian_Noise[i][j].b +dNoise*Kp < 0)
                Gaussian_Noise[i][j].b = 0;
            else
                Gaussian_Noise[i][j].b += dNoise*Kp;
        }
    }


    //Salt Noise 생성
    srand((int)time(NULL));


    double Thres = 0.005;
    double minus_Thres = (double)(1-Thres);
    double random;


    for(int i=0; i<Salt_Noise.Row(); i++){
        for(int j=0;j<Salt_Noise.Col();j++){

            random = (double) rand()/RAND_MAX;

            if(random<Thres)
            {
                Salt_Noise[i][j].r = 0;
                Salt_Noise[i][j].g = 0;
                Salt_Noise[i][j].b = 0;
            }
            else if(random>minus_Thres){
                Salt_Noise[i][j].r = 255;
                Salt_Noise[i][j].g = 255;
                Salt_Noise[i][j].b = 255;
            }


        }
    }



    //show noise
    ImageForm*  q_pForm1 = new ImageForm(Salt_Noise, "Salt_and_Pepper Noise", this);
    _plpImageForm->Add(q_pForm1);
    q_pForm1->show();

    ImageForm*  q_pForm2 = new ImageForm(Gaussian_Noise, "Gaussian Noise", this);
    _plpImageForm->Add(q_pForm2);
    q_pForm2->show();

Box, Gaussian, Median Filter

Input Image

noise

Box Filter

  • Box Filtering
    • 주변 픽셀값의 평균을 얻는 방식의 이미지 필터링
    • 이미지 샘플과 filter kernel을 곱해서 필터링된 결과값을 얻음

  • 장점
    • 빠르다
    • Box(i+1) = Box(i) - neighborhood(i)[first] + neighborhood(i+1)[last]
  • 단점
    • Signal frequencies shared with noise are lose
    • Impulse noise is diffused but not removed
    • The secondary lobes let noise into the filtered image

sinc

result

BoxFilter

Code

    //필터링
    int filtersize = ui->spin_FilterSize->value();

    //Box Filtering
        //S_n_P noise 이미지 불러오기
    ImageForm* q_pForm_s = 0;

        for(int i=0; i<_plpImageForm->Count();i++)
            if((*_plpImageForm)[i]->ID()=="Salt_and_Pepper Noise")
            {
                q_pForm_s = (*_plpImageForm)[i];
                break;
            }

    KImageColor Salt_Noise_2 = q_pForm_s->ImageColor();

    //Gaussian noise 이미지 불러오기

    ImageForm* q_pForm_g = 0;

        for(int i=0; i<_plpImageForm->Count();i++)
            if((*_plpImageForm)[i]->ID()=="Gaussian Noise")
            {
                q_pForm_g = (*_plpImageForm)[i];
                break;
            }
    KImageColor Gaussian_Noise_2 = q_pForm_g->ImageColor();

    int size = 0.5*filtersize-0.5;
    int box = filtersize*filtersize;
    //qDebug()<<size;
        //Salt Noise
    double sum_r_s,sum_g_s,sum_b_s;
    double sum_r_g,sum_g_g,sum_b_g;

    for(int ii = size; ii<Salt_Noise_2.Row()-size;ii++){
        for(int jj = size; jj<Salt_Noise_2.Col()-size;jj++){
            sum_r_s = 0;
            sum_g_s = 0;
            sum_b_s = 0;

            sum_r_g = 0;
            sum_g_g = 0;
            sum_b_g = 0;
            for(int i = -size; i<size+1; i++){
                for(int j = -size; j<size+1;j++){
                    sum_r_s+=Salt_Noise[ii-i][jj-j].r;
                    sum_g_s+=Salt_Noise[ii-i][jj-j].g;
                    sum_b_s+=Salt_Noise[ii-i][jj-j].b;

                    sum_r_g+=Gaussian_Noise[ii-i][jj-j].r;
                    sum_g_g+=Gaussian_Noise[ii-i][jj-j].g;
                    sum_b_g+=Gaussian_Noise[ii-i][jj-j].b;
                }
            }

            //qDebug()<<sum_r/(filtersize*filtersize);

            Salt_Noise_2[ii][jj].r=sum_r_s/box;
            Salt_Noise_2[ii][jj].g=sum_g_s/box;
            Salt_Noise_2[ii][jj].b=sum_b_s/box;

            Gaussian_Noise_2[ii][jj].r=sum_r_g/box;
            Gaussian_Noise_2[ii][jj].g=sum_g_g/box;
            Gaussian_Noise_2[ii][jj].b=sum_b_g/box;
        }
    }


    //show noise 제거
    ImageForm*  q_pForm3 = new ImageForm(Salt_Noise_2, "Box_Salt_n_Pepper", this);
    _plpImageForm->Add(q_pForm3);
    q_pForm3->show();

    ImageForm*  q_pForm4 = new ImageForm(Gaussian_Noise_2, "Box_Gaussian", this);
    _plpImageForm->Add(q_pForm4);
    q_pForm4->show();

Gaussian Filter

  • 장점
    • Secondary lobes가 없으므로 정확한 low-pass filter 구현이 가능하다.

result

GaussianFilter

Code

    //Gaussian 필터링

    //S_n_P noise 이미지 불러오기
    ImageForm* q_pForm_s = 0;

    for(int i=0; i<_plpImageForm->Count();i++)
        if((*_plpImageForm)[i]->ID()=="Salt_and_Pepper Noise")
        {
            q_pForm_s = (*_plpImageForm)[i];
            break;
        }

    KImageColor Salt_Noise_2 = q_pForm_s->ImageColor();

    //Gaussian noise 이미지 불러오기

    ImageForm* q_pForm_g = 0;

    for(int i=0; i<_plpImageForm->Count();i++)
        if((*_plpImageForm)[i]->ID()=="Gaussian Noise")
        {
            q_pForm_g = (*_plpImageForm)[i];
            break;
        }
    KImageColor Gaussian_Noise_2 = q_pForm_g->ImageColor();

    int Gau_filter_size = 8*sigma + 1; //filter size




    int size = std::sqrt(Gau_filter_size);


    //qDebug()<<size;
        //Salt Noise

    double mask_r_s, mask_g_s, mask_b_s;
    double mask_r_g, mask_g_g, mask_b_g;


    for(int ii = size; ii<Salt_Noise_2.Row()-size;ii++){
        for(int jj = size; jj<Salt_Noise_2.Col()-size;jj++){

            mask_r_s = 0;
            mask_g_s = 0;
            mask_b_s = 0;

            mask_r_g = 0;
            mask_g_g = 0;
            mask_b_g = 0;


            for(int i = -size; i<size+1; i++){
                for(int j = -size; j<size+1;j++){
                    mask_r_s += std::exp(-0.5*((i*i+j*j)/(sigma*sigma)))*Salt_Noise[ii-i][jj-j].r;
                    mask_g_s += std::exp(-0.5*((i*i+j*j)/(sigma*sigma)))*Salt_Noise[ii-i][jj-j].g;
                    mask_b_s += std::exp(-0.5*((i*i+j*j)/(sigma*sigma)))*Salt_Noise[ii-i][jj-j].b;

                    mask_r_g += std::exp(-0.5*((i*i+j*j)/(sigma*sigma)))*Gaussian_Noise[ii-i][jj-j].r;
                    mask_g_g += std::exp(-0.5*((i*i+j*j)/(sigma*sigma)))*Gaussian_Noise[ii-i][jj-j].g;
                    mask_b_g += std::exp(-0.5*((i*i+j*j)/(sigma*sigma)))*Gaussian_Noise[ii-i][jj-j].b;
                }
            }

            Salt_Noise_2[ii][jj].r = (1/(2*M_PI*sigma*sigma))*mask_r_s;
            Salt_Noise_2[ii][jj].g = (1/(2*M_PI*sigma*sigma))*mask_g_s;
            Salt_Noise_2[ii][jj].b = (1/(2*M_PI*sigma*sigma))*mask_b_s;

            Gaussian_Noise_2[ii][jj].r = (1/(2*M_PI*sigma*sigma))*mask_r_g;
            Gaussian_Noise_2[ii][jj].g = (1/(2*M_PI*sigma*sigma))*mask_g_g;
            Gaussian_Noise_2[ii][jj].b = (1/(2*M_PI*sigma*sigma))*mask_b_g;



        }
    }


    //show noise 제거
    ImageForm*  q_pForm3 = new ImageForm(Salt_Noise_2, "Gaussian_Salt_n_Pepper", this);
    _plpImageForm->Add(q_pForm3);
    q_pForm3->show();

    ImageForm*  q_pForm4 = new ImageForm(Gaussian_Noise_2, "Gaussian_Gaussian", this);
    _plpImageForm->Add(q_pForm4);
    q_pForm4->show();

Median Filter

  • Median Filtering
    • 주변 픽셀값을 sort한 후 중간값을 구하는 방식의 이미지 필터링
  • 장점
    • 경계가 흐려지지 않음
    • 중간값을 선택하는 방법이기 때문에 Salt and Pepper noise에 강함

  • 단점
    • sort 해야하므로 느리다

result

MedianFilter

Code

    //필터링
    int filtersize = ui->spin_FilterSize->value();

    //Median Filtering
        //S_n_P noise 이미지 불러오기
    ImageForm* q_pForm_s = 0;

        for(int i=0; i<_plpImageForm->Count();i++)
            if((*_plpImageForm)[i]->ID()=="Salt_and_Pepper Noise")
            {
                q_pForm_s = (*_plpImageForm)[i];
                break;
            }

    KImageColor Salt_Noise_2 = q_pForm_s->ImageColor();

    //Gaussian noise 이미지 불러오기

    ImageForm* q_pForm_g = 0;

        for(int i=0; i<_plpImageForm->Count();i++)
            if((*_plpImageForm)[i]->ID()=="Gaussian Noise")
            {
                q_pForm_g = (*_plpImageForm)[i];
                break;
            }
    KImageColor Gaussian_Noise_2 = q_pForm_g->ImageColor();


    int size = 0.5*filtersize-0.5;
    std::vector<double> v_r_s,v_g_s,v_b_s;
    std::vector<double> v_r_g,v_g_g,v_b_g;
    int box = filtersize*filtersize;

    //qDebug()<<size;
        //Salt Noise


    for(int ii = size; ii<Salt_Noise_2.Row()-size;ii++){
        for(int jj = size; jj<Salt_Noise_2.Col()-size;jj++){

            v_r_s.clear();
            v_g_s.clear();
            v_b_s.clear();

            v_r_g.clear();
            v_g_g.clear();
            v_b_g.clear();


            for(int i = -size; i<size+1; i++){
                for(int j = -size; j<size+1;j++){
                    v_r_s.push_back(Salt_Noise[ii-i][jj-j].r);
                    v_g_s.push_back(Salt_Noise[ii-i][jj-j].g);
                    v_b_s.push_back(Salt_Noise[ii-i][jj-j].b);

                    v_r_g.push_back(Gaussian_Noise[ii-i][jj-j].r);
                    v_g_g.push_back(Gaussian_Noise[ii-i][jj-j].g);
                    v_b_g.push_back(Gaussian_Noise[ii-i][jj-j].b);
                }
            }

            //qDebug()<<sum_r/(filtersize*filtersize);
            sort(v_r_s.begin(),v_r_s.end());
            sort(v_g_s.begin(),v_g_s.end());
            sort(v_b_s.begin(),v_b_s.end());

            sort(v_r_g.begin(),v_r_g.end());
            sort(v_g_g.begin(),v_g_g.end());
            sort(v_b_g.begin(),v_b_g.end());

            Salt_Noise_2[ii][jj].r = v_r_s[box/2];
            Salt_Noise_2[ii][jj].g = v_g_s[box/2];
            Salt_Noise_2[ii][jj].b = v_b_s[box/2];

            Gaussian_Noise_2[ii][jj].r = v_r_g[box/2];
            Gaussian_Noise_2[ii][jj].g = v_g_g[box/2];
            Gaussian_Noise_2[ii][jj].b = v_b_g[box/2];


        }
    }


    //show noise 제거
    ImageForm*  q_pForm3 = new ImageForm(Salt_Noise_2, "Median_Salt_n_Pepper", this);
    _plpImageForm->Add(q_pForm3);
    q_pForm3->show();

    ImageForm*  q_pForm4 = new ImageForm(Gaussian_Noise_2, "Median_Gaussian", this);
    _plpImageForm->Add(q_pForm4);
    q_pForm4->show();

'21-1학기 > 컴퓨터비전' 카테고리의 다른 글

6. Hough Transform  (0) 2021.08.19
5. Canny Edge Operator  (0) 2021.08.19
3. Histogram Equalization & Histogram Matching  (0) 2021.08.18
2. Otsu’s Thresholding and Labeling & dilation and erosion  (0) 2021.08.18
1. Sepia Tone  (0) 2021.08.18

+ Recent posts