※ 리스트

리스트는 다른 언어의 배열과 같은 역할을 하며 다음과 같이 사용한다.

리스트형 변수 = [요소1, 요소2, ... ]

 

a = [1, 2, ['a', 'b', ['Life', 'is']]]

print(a[-1][-1][0])
print(a[2][2][0])

#--------출력--------#
# 둘 다 Life가 출력됨

 

 

 

※ 리스트 슬라이싱(slicing)

문자열 슬라이싱과 동일한 방법으로 진행된다.

a = [1, 2, 3, 4, 5]

print(a[1:3])   #출력: [2, 3]

 

 

 

 

※ 리스트 연산

§ 리스트 덧셈

a = [1, 2, 3]
b = [4, 5, 6]

print(a+b)

#--------출력--------#
[1, 2, 3, 4, 5, 6]

 

리스트는 같은 자료형의 집합이기에 다른 리스트간의 덧셈을 위해서는 형변환이 필요하다.

이때, str함수는 정수,실수를 문자열형태로 바꿔주는 내장함수이다.

a = [1, 2, 3]

print(str(a[2]) + "hi")   # 3hi

 

 

 

§ 리스트 반복

a = [1, 2, 3]

print(a*3)

#--------출력--------#
[1, 2, 3, 1, 2, 3, 1, 2, 3]

 

 

§ 리스트 길이(len)

a = [1, 2, 3]

print(len(a))   # 3출력

 

 

§ 리스트 수정과 삭제(del)

a = [1, 2, 3]

a[2]=4
print(a)    #출력: [1, 2, 4]

del a[1]
print(a)    #출력: [1, 4]

arr = [1,2,3,4,5]
del arr[2:]
print(arr)  #출력: [1, 2]

 

 

 

 

§ 리스트 복사

a = [1,2,3]
b = a

print(id(a))
print(id(b))

#-----출력-----#
2423373358208
2423373358208

a와 b의 값이 동일함을 알 수 있는데, 이를 아래와 같이 알 수 있다.

a = [1,2,3]
b = a

print(a is b)   # True

따라서 a객체와 b객체가 동일한 주소값을 갖고 있음을 알 수 있기에

 

아래와 같은 양상이 일어날 수 있다.

a = [1,2,3]
b = a

a[1] = 4
print(a)    # [1, 4, 3]
print(b)    # [1, 4, 3]

그렇다면 a값을 가져오지만 a와 다른 주소를 가리키도록 할 수는 없을까?

 

1. slicing 이용

a = [1,2,3]
b = a[:]

a[1] = 4

print(a)    # [1, 4, 3]
print(b)    # [1, 2, 3]

 

2. copy()함수나 copy를 import하여 사용 

a = [1,2,3]
b = a.copy()

a[1] = 4

print(a)    # [1, 4, 3]
print(b)    # [1, 2, 3]

 

from copy import copy

a = [1,2,3]
b = copy(a)

a[1] = 4

print(a)    # [1, 4, 3]
print(b)    # [1, 2, 3]

 

 

 

 

 

 

 

 

 

 

※ 리스트 관련 함수(append, sort, reverse, index, insert, remove, pop, count, extend)

 

§ 리스트 요소 추가 (append)

a = [1, 2, 3]

a.append(4)
print(a)    # [1, 2, 3, 4] 출력

a.append([5, 6])
print(a)    # [1, 2, 3, 4, [5, 6]] 출력

 

 

§ 리스트 정렬과 뒤집기 (sort ,reverse)

a = [1, 4, 3, 2]

a.sort()
print(a)    # [1, 2, 3, 4] 출력
a = [1, 4, 3, 2]

a.reverse()
print(a)    # [2, 3, 4, 1] 출력

 

 

§ 리스트 위치반환 (index)

a = [1, 4, 3, 2]

print(a.index(4))    # 1출력

 

 

 

§ 리스트 요소 삽입 ( insert(i, x) )

insert(i, x)는 i번째에 x를 삽입하는 함수이다.

a = [1, 4, 3, 2]
a.insert(4, 5)

print(a)    # [1, 4, 3, 2, 5] 출력

 

 

 

§ 리스트 요소 삭제 (remove)

첫 번째로 나오는 값을 삭제

a = [1, 2, 3, 1, 2, 3]
a.remove(2)

print(a)    # [1, 3, 1, 2, 3] 출력

 

 

 

§ 리스트 요소 끄집어내기 (pop)

리스트 마지막 값을 pop(출력 후 삭제)

pop(i)로 i번째 index를 출력 후 삭제

a = [1, 2, 3, 1, 2, 3]

a.pop()
print(a)         # [1, 2, 3, 1, 2] 출력

print(a.pop(2))  # 3출력
print(a)         # [1, 2, 1, 2] 출력

 

 

 

§ 리스트에 포함된 요소x 개수 세기 ( count (x) )

a = [1, 2, 3, 1, 1, 1]

print(a.count(1))   # 4출력

 

 

 

§ 리스트 확장 (extend(리스트))

a.extend(x)에서는 x값은 리스트만 올 수 있으며 원래 리스트 a에 x를 더한다.

a = [1, 2, 3]
b = [4, 5]

a.extend(b)
a.extend([6, 7])
print(a)   # [1, 2, 3, 4, 5, 6, 7] 출력

 

 

 

 

 

 

 

 

 

※ 튜플

리스트와 몇가지를 제외하면 거의 비슷하다.

 

<리스트와 다른점>

- 리스트는 [ ]이고 튜플은 ( )이며 (생략가능) 한 개의 요소만 가질 때는 쉼표가 필요하다.

- 리스트는 값의 생성, 삭제, 수정이 가능

- 튜플은 위와 같은 값의 변경이 불가능하다.

 

따라서 이런 값의 불변성 때문에 평균적으로는 리스트를 더 많이 사용한다.

리스트형 변수 = [요소1, 요소2, ... ]

 

a = [1, 2, ['a', 'b', ['Life', 'is']]]

print(a[-1][-1][0])
print(a[2][2][0])

#--------출력--------#
# 둘 다 Life가 출력됨

 

 

 

※ 튜플의 인덱싱과 슬라이싱(slicing)

a = (1, 2, 'a', 'b')

print(a[0])     # 1출력
print(a[0:3])   # (1, 2, 'a') 출력

 

 

※ 튜플의 덧셈, 곱셈, 길이구하기

t1 = (1, 2, 'a', 'b')
t2 = (3, 4)

print(t1+t2)    # (1, 2, 'a', 'b', 3, 4)
print(t2 * 3)   # (3, 4, 3, 4, 3, 4)
print(len(t1))  # 4 출력

 

※ python 입력, 출력 받기

§ 입력받기

a = input()

 

a = input("입력하세요: ")
print(a)

#--------출력--------#
입력하세요: 3
3

 

 

§ 출력 형식

print("Life"+"is"+"too"+"short")
print("Do", "Python")   # 콤마(,)로 문자열 사이 띄어쓰기 가능

#--------출력--------#
Lifeistooshort
Do Python

 

 

※ 곱셈, n제곱, 나눗셈, 몫 출력

a = 7*4   # 7x4
a = 7**4  # 7^4

print(a)
a = 7 / 4    # 나눗셈 결과 반환 (1.75 출력)
a = 7 // 4   # 나눗셈 후 몫 반환 (1 출력)

print(a)

 

 

§ 문자열 길이구하기

head = "Python"
print(len(head))

 

 

※ 문자열과 개행처리(\n)

str = "Life is too short\nYou need python"
print(str)

#-------출력-------#
# Life is too short
# You need python

출처:&nbsp;https://wikidocs.net/13

 

 

 

※ 문자열 더하기와 곱하기

head = "Python"
tail = " is EZ"

print("-"*15+"\n"+head+tail+"\n"+"-"*15)

<출력>
---------------
Python is EZ
---------------

 

cf. 쉼표(,)를 넣으면 한칸씩 띄워서 출력된다.

head = "Python"
tail = " is EZ"

print("-"*15,"\n",head+tail, "\n","-"*15)

<출력>
--------------- 
 Python is EZ 
 ---------------

 

 

 

 

※ 문자열 포매팅

a = "Python%d is %s" %(3, "EZ")

print(a)
a = "Python{0} is {1}".format(3, "EZ")

print(a)

 

 

 

 

※ 문자열 인덱싱과 슬라이싱

§ 문자열 인덱싱 - 한 문자만 뽑아내는 것

a = "Life is too short, You need Python"
print(a[3])  # e 출력
a = "Life is too short, You need Python"
print(a[-1])  # 가장 마지막 글자인 n 출력
a = "Life is too short, You need Python"
print(a[-6])  # P 출력

 

 

§ 문자열 인덱싱 - 한 단어를 뽑아내는 것

형식: a[시작번호:끝번호]  , 끝번호에 해당하는 것은 포함하지 않음 (0<= a < 3)

a = "Life is too short, You need Python"
print(a[0:4])   # Life 출력
a = "Life is too short, You need Python"
print(a[12:17])   # short 출력

 

a = "Life is too short, You need Python"

print(a[19:])   # 19부터 끝까지 뽑아내기에 You need Python 출력
print(a[:17])   # 처음부터 19까지 뽑아내기에 Life is too short 출력
print(a[:])     # 처음부터 끝까지, Life is too short, You need Python 모두 출력
print(a[19:-7]) # 19~끝-7까지 뽑아내기에 You need 출력

 

 

활용) Pithon을 Python으로 고치기

a = "Pithon"

print(a[:1]+"y"+a[2:])

 

 

 

 

 

 

 

 

※ 문자열관련 함수

§ 문자 개수세기  (count)

a = "My Hobby is play baseball"

print(a.count("b"))  # b의 개수인  4 출력

 

§ 문자 위치찾기  (find, index)

a = "My Hobby is play baseball"

print(a.find("b"))  # b가 처음 나오는 위치를 출력(0부터 시작)
print(a.index("b"))  # b가 처음 나오는 위치를 출력(0부터 시작)

 

§ 문자열 삽입 (join) 

a = "My Hobby is play baseball"

print(",".join(["My", "Hobby", "is", "play", "baseball"]))

#출력: My,Hobby,is,play,baseball

 

 

 

§ 문자열 바꾸기, 나누기 (replace, split)

a = "My Hobby is play baseball"

print(a.replace("baseball", "game"))
# 출력: My Hobby is play game

print(a.split("play"))
# 출력: ['My Hobby is ', ' baseball']

 

 

 

§ 대문자와 소문자 바꾸기  (upper, lower)

a = "My Hobby is play baseball"

print(a.upper())  # 출력: MY HOBBY IS PLAY BASEBALL
print(a.lower())  # 출력: my hobby is play baseball

 

• 지난 시간에는 image가 일종의 number로 생각하는 과정이었다.

https://chan4im.tistory.com/74

 

[Computer Vision].prev_ pixel과 RGB, HSV

※ Computer Vision • Computer Vision -> 자연어 처리 -> 오디오 부분 순으로 발전하였기에 가장 발전된 분야 • 시각적 세계를 해석하고 이해하도록 컴퓨터에 학습시키는 인공지능 분야로 컴퓨터를 이

chan4im.tistory.com

이번에는 image가 하나의 array라고 생각한다면 할 수 있는 방법들에 대해 배워볼 것이다.

 

 

※ image crop

• image는 일종의 "array"이므로 이런 image를 배열단위를 자르는 방식을 통해 자를 수 있다.

• 그렇다면 배열을 자르기 위해 어떤 방식을 사용할 수 있을까?

바로 slicing을 이용해 image 배열을 자를 수 있다.

구현방식: a = [1,2,3,4]  -->  a = [1, 4]로 만들려면?   =>   a[1:3]   (by. slicing) 

 

[실습] - crop

<import 부분>

 

 

 

 

 

 

 

 

※ image padding

원본 image의 바깥부분에 덧대는 방식으로 4가지 padding 기법을 소개한다.

zero-padding : 원본 image의 주위 값을 0으로 배치

mean-padding : 원본 image의 주위 값을 평균값으로 배치 (단, RGB는 R따로, G따로, B따로 평균)

reflect-padding : 데칼코마니 하듯이 뒤집어서 가져옴

symmetric-padding : 대칭되듯이 가져옴

1. zero-padding&nbsp; &nbsp;2. reflect-padding&nbsp; &nbsp; 3. symmetric-padding

 

구현방식 1: frame을 그대로 두고 이어 붙일 것을 concat 시킨다.

장점: memory 절약가능 (학습 시 많이 사용)

단점: 느리다.

 

구현방식 2: 새로 비어있는 큰 배열을 만들어 가운데 부분에 frame을 대입시킨다.

장점: 빠르다. (inference(추론), 즉 학습이 끝난 후 결과를 낼 때 많이 사용)

단점: memory가 비교적 많이 필요하다.

 

 

 

※ 배열의 축, axis이해하기.

배열에는 축이라는 개념이 있는데, 이 축이 헷갈리는 이유는 행렬개념과는 조금 다르게 받아들여야 하기 때문이다.

1차원 배열
먼저 1차원 배열의 경우, 축은 axis 0 하나뿐이다. (행열의 개념이 없음)
array([0, 1, 2, 3, 4 ,5])


2차원 배열
다음으로 2차원 배열의 경우, 축은 axis 0, axis 1 두 가지가 있다.
array([[0, 1, 2],
          [3, 4 ,5]])
위의 경우, 우리는 2행 3열로 배열을 해석하는데 행과 열에 대한 축의 매칭은 아래와 같다.
행: axis 0        열: axis 1
이때, 무작정 axis0을 행이라 외우는 것은 주의해야 한다. 고차원으로 갈수록 규칙이 깨지기 때문이다.


3차원 배열
다음으로 3차원 배열의 경우, 축은 axis 0, axis 1, axis 2  세 가지가 있다.
array([[[ 0, 1, 2, 3],
           [ 4 ,5, 6, 7],
           [ 8, 9, 10, 11]],

          [[12, 13, 14, 15],
           [16, 17, 18, 19],
           [20, 21, 22, 23]]]
)

위의 경우,  3행 4열의 행렬이 2층으로 배열을 해석하는데 행과 열, 높이에 대한 축의 매칭은 아래와 같다.
높이: axis 0        행: axis 1       열: axis 2
import numpy as np

arr1 = np.array([ [1, 2, 3], [10, 20, 30] ])
arr2 = np.array([ [4, 5, 6], [40, 50, 60] ])

np.concatenate((arr1, arr2), axis = 0)
array([[ 1,  2,  3],
       [10, 20, 30],
       [ 4,  5,  6],
       [40, 50, 60]])

np.concatenate((arr1, arr2), axis = 1)
array([[ 1,  2,  3,  4,  5,  6],
       [10, 20, 30, 40, 50, 60]])

 

 

 

 

 

[실습] - padding

 

 

 

 

 

 

※ translation

crop과 padding을 이용하여 사진을 옮길 수 있음 (정석적 방법은 아님)

• image를 100 pixel만큼 왼쪽으로 옮기고 싶다면?

-  frame을 slicing을 이용한 crop으로 자름 => [:, 100:]

- 그 후 padding

 

[실습] - translation

※ Strategy Pattern

전략패턴은 객체들의 행위를 각 전략 클래스에 생성, 유사행위를 캡슐화하는 인터페이스를 정의

객체의 행위를 동적으로 바꾸고 싶다면 전략만 바꿔줌으로 행위를 유연하게 확장하는 방법

클라이언트와 독립적으로 구현되기에 새로운 알고리즘 추가,변경이 쉬워진다.

[구조 예시]

 

#include <iostream>
using namespace std;

class SortBehavior {
public:
    virtual void sort() const = 0;
};
class Merge: public SortBehavior {
public:
    virtual void sort() const { cout << "Merge sort()" << endl; }
};
class Quick: public SortBehavior {
public:
    virtual void sort() const { cout << "Quick sort()" << endl; }
};
class Heap: public SortBehavior {
public:
    virtual void sort() const { cout << "Heap sort()" << endl; }
};

class SearchBehavior {
public:
    virtual void search() const = 0;
};
class Sequential: public SearchBehavior {
public:
    virtual void search() const { cout << "Sequential search()\n"; }
};
class BinaryTree: public SearchBehavior {
public:
    virtual void search() const { cout << "BinaryTree search()\n"; }
};
class HashTable: public SearchBehavior {
public:
    virtual void search() const { cout << "HashTable search()\n"; }
};

// Context
class Collection {
private:
    SortBehavior *m_sort;
    SearchBehavior *m_search;
public:
    Collection(){}
    void set_sort(SortBehavior *s) { m_sort = s; }
    void set_search(SearchBehavior *s) { m_search = s; }
    void sort() const { m_sort->sort(); }
    void search() const { m_search->search(); }
};


int main(int argc, char *argv[])
{
    Merge merge;
    Quick quick;
    Heap heap;

    Sequential sequential;
    BinaryTree binaryTree;
    HashTable hashTable;

    Collection colA;
    colA.set_sort(&merge);
    colA.sort();

    Collection colB;
    colB.set_search(&binaryTree);
    colB.search();

}

 

 

 

 

 

 

 

 

※ Observer Pattern

객체의 변화를 관찰하는 observer들의 목록을 객체에 등록, 변화가 있을 때 함수를 이용해 관찰대상자가 직접 observer에게 통지해 그 객체에 의존성을 가진 다른 객체가자동으로 업데이트 하는 방식

[구조 예시]

● Generator: 관찰 대상자로 현재 관찰 대상자에 붙어있는 Observer들을 관리할뿐만 아니라 현재 관찰 대상자의 상태 정보를 얻기 위한 함수를 제공
           상태 변화시 등록되어 있는 모든 관찰자들에게 상태 변화를 통지해주는 함수를 제공합니다.

● StringGenerator: Generator를 상속받는 실제 상태 정보를 가지고 있는 객체.
                  상태 변화가 발생하면 상태 변화를 통지해주는 함수를 호출.

● Observer: 관찰자들이 가져야 할 공통인터페이스를 정의.

● StringObserver: 관찰 대상자의 상태 정보를 가져와 자신의 상태와 동기화.
                 이 객체는 관찰 대상자의 string형을 모니터에 출력해주는 객체입니다.

● StringCountObsever: 마찬가지로 관찰 대상자의 상태 정보를 가져와 자신의 상태와 동기화 합니다. 
                      이 객체는 관찰 대상자인 string형 문자열의 개수를 화면에 출력하는 객체

 

#include <iostream>
#include <list>
#include <string>
using namespace std;

class IObserver {
public:
    virtual void Update(const string &message_from_subject) = 0;
    virtual ~IObserver(){ };
};

class ISubject {
public:
    virtual void Attach(IObserver *observer) = 0;
    virtual void Detach(IObserver *observer) = 0;
    virtual void Notify() = 0;
    virtual ~ISubject(){};
};

/* Subject는 일부 중요한 state를 소유, state가 변경되면 observer에게 알림*/
class Subject : public ISubject {
public:
    /* subscription 관리 함수 */
    void Attach(IObserver *observer) override { list_observer_.push_back(observer); }
    void Detach(IObserver *observer) override { list_observer_.remove(observer); }
    void Notify() override {
        list<IObserver *>::iterator iterator = list_observer_.begin();
        HowManyObserver();
        while (iterator != list_observer_.end()) {
            (*iterator)->Update(message_);
            ++iterator;
        }
    }
    void CreateMessage(string message = "Empty") {
        this->message_ = message;
        Notify();
    }

    void HowManyObserver() { cout << "There are " << list_observer_.size() << " observers in the list" << endl; }
    
    /*
     * 일반적으로 subscription logic은 Subject가 실제로 수행할 수 있는 작업의 일부이다.
     * Subject는 일반적으로 중요한 일이 발생할 때마다 통지 방법을 작동시키는 중요한 business logic를 갖고 있다.
     */
    void SomeBusinessLogic() {
        this->message_ = "change message message";
        Notify();
        cout << "I'm about to do some thing important\n";
    }

    virtual ~Subject() { cout << "Goodbye, I was the Subject" << endl; }

private:
    list<IObserver *> list_observer_;
    string message_;
};

class Observer : public IObserver {
public:
    Observer(Subject &subject) : subject_(subject) {
        this->subject_.Attach(this); // this는 observer
        cout << "Hi, I'm the Observer \"" << ++Observer::static_number_ << "\"" << endl;
        this->number_ = Observer::static_number_;
    }
    virtual ~Observer() {
        cout << "Goodbye, I was the Observer \"" << this->number_ << "\"" << endl;
    }

    void Update(const string &message_from_subject) override {
        message_from_subject_ = message_from_subject;
        PrintInfo();
    }
    void RemoveMeFromTheList() {
        subject_.Detach(this); // this는 observer
        cout << "Observer \"" << number_ << "\" removed from the list" << endl;
    }
    void PrintInfo() {
        cout << "Observer \"" << this->number_ << "\": a new message is available --> " << this->message_from_subject_ << endl;
    }

private:
    std::string message_from_subject_;
    Subject &subject_;
    static int static_number_;
    int number_;
};

int Observer::static_number_ = 0;   // static멤버변수 초기화 방법

void ClientCode() {
    Subject *subject = new Subject;
    Observer *observer1 = new Observer(*subject);
    Observer *observer2 = new Observer(*subject);
    Observer *observer3 = new Observer(*subject);
    Observer *observer4;
    Observer *observer5;

    subject->CreateMessage("Hello World! :D");
    observer3->RemoveMeFromTheList();

    subject->CreateMessage("The weather is hot today! :p");
    observer4 = new Observer(*subject);

    observer2->RemoveMeFromTheList();
    observer5 = new Observer(*subject);

    subject->CreateMessage("My new car is great! ;)");
    observer5->RemoveMeFromTheList();

    observer4->RemoveMeFromTheList();
    observer1->RemoveMeFromTheList();

    delete observer5;
    delete observer4;
    delete observer3;
    delete observer2;
    delete observer1;
    delete subject;
}

int main() {
    ClientCode();
}

 

 

 

 

 

 

 

 

※ Adapter Pattern

변환기처럼 서로 다른 두 인터페이스 사이 통신을 가능하게 해주는 디자인 패턴이다.프로그램에서 한 클래스의 인터페이스를 클라이언트로 사용하고자 하는 인터페이스로 변환 시 사용

또한 어댑터 패턴은 다중상속을 사용해 구현할 수도 있다.

[구조 예시]

 

 

 

#include <iostream>
#include <algorithm>
#include <string>
using namespace std;

/* Target: client code에서 사용되는 domain-specific interface를 정의한다. */
class Target {
public:
    virtual std::string Request() const { return "Target: The default target's behavior."; }
    virtual ~Target() = default;
};

/* Adaptee(기존객체)는 유용한 동작이 포함되어 있지만 interface가 기존client code와 호환되지 않는다.
 * 따라서 client code가 Adaptee를 사용하려면 적응할 필요가 있다.
 */
class Adaptee {
public:
    string SpecificRequest() const { return ".eetpadA eht fo roivaheb laicepS"; }
};

/* Adapter는 Adaptee의 interface가 Target's interface와 호환되게 한다. */
class Adapter : public Target {
private:
    Adaptee *adaptee_;
public:
    Adapter() { }
    Adapter(Adaptee *adaptee) : adaptee_(adaptee) {}

    string Request() const override {
        string to_reverse = this->adaptee_->SpecificRequest();
        reverse(to_reverse.begin(), to_reverse.end());
        return "Adapter: (TRANSLATED) " + to_reverse;
    }
};

/* client code는 Target interface를 따르는 모든 클래스를 지원한다. */
void ClientCode(const Target *target) { cout << target->Request(); }

int main() {
    cout << "Client: I can work just fine with the Target objects:\n";

    Target *target = new Target;
    ClientCode(target);
    cout << endl << endl;

    Adaptee *adaptee = new Adaptee;
    cout << "Client: The Adaptee class has a weird interface. See, I don't understand it: " << endl;
    cout << "Adaptee: " << adaptee->SpecificRequest();
    cout << endl << endl;
    cout << "Client: But I can work with it via the Adapter: " << endl;

    Adapter *adapter = new Adapter;
    ClientCode(adapter);
    cout << endl;

    delete target;
    delete adaptee;
    delete adapter;
}

 

 

 

 

 

[다중상속으로 구현한 코드]

#include <iostream>
#include <algorithm>
#include <string>
using namespace std;

class Target {
public:
    virtual string Request() const { return "Target: The default target's behavior."; }
    virtual ~Target() = default;
};

class Adaptee {
public:
    string SpecificRequest() const { return ".eetpadA eht fo roivaheb laicepS"; }
};

class Adapter : public Target, public Adaptee {
public:
    Adapter() { }
    string Request() const override {
        string to_reverse = SpecificRequest();
        reverse(to_reverse.begin(), to_reverse.end());
        return "Adapter: (TRANSLATED) " + to_reverse;
    }
};

void ClientCode(const Target *target) { cout << target->Request(); }

int main() {
    cout << "Client: I can work just fine with the Target objects: " << endl;
    
    Target *target = new Target;
    ClientCode(target);
    cout << endl << endl;
    
    Adaptee *adaptee = new Adaptee;
    cout << "Client: The Adaptee class has a weird interface. See, I don't understand it: " << endl;
    cout << "Adaptee: " << adaptee->SpecificRequest();
    cout << endl << endl;
    cout << "Client: But I can work with it via the Adapter: " << endl;
    
    Adapter *adapter = new Adapter;
    ClientCode(adapter);
    cout << endl;

    delete target;
    delete adaptee;
    delete adapter;
}

 

※ Computer Vision

• Computer Vision  -> 자연어 처리 -> 오디오 부분 순으로 발전하였기에 가장 발전된 분야

• 시각적 세계를 해석하고 이해하도록 컴퓨터에 학습시키는 인공지능 분야로 컴퓨터를 이용한 정보처리를 진행한다.

• 따라서 컴퓨터 비전은 정보를 데이터로 표현하기 매우 적절한 구조를 갖고 있다.

 

cf. 만약  It's late를 데이터로 표현하려면...?  (feat. 자연어 처리)
자연어의 경우, 자연어는 데이터로 표현하기 어려운데, (데이터를 많이 갖고 있기 때문)

예시에서 It's의 t와 late의 t가 똑같은 용도로 사용될까? 이런 저런 이유로 컴퓨터비전은 데이터를 표현하기 매우 좋다.

 

 

 

 

※ PASCAL VOC challenge 문제

초창기 이미지 dataset에 대해 인식성능을 겨루는 대회가 열렸고,

가장 대표적인 PASCAL VOC(Visual Object Classes) Challenge 로 이 대회를 기점으로 이미지 인식에서 다루는 문제들이 아래와 같이 정형화 되었다,

• Classification

• Detection

• Segmentation

 

 

Classification 

※ Classification 
특정 class에 해당하는 사물이 포함되어 있는지 여부를 분류하는 모델
classification은 Detection, Segmentation문제를 향한 출발점이기에 더더욱 중요하며 다음 2가지로 나눌 수 있다.

§ 단일 사물 분류 문제
- 모든 이미지가 반드시 하나의 사물만 존재
- 전체 class에 대한 confidence score  S를 비교, 가장 큰 confidence score를 지니는 class로 선정하는 방법

§ 복수 사물 분류 문제
- 이미지 상에 여러개의 사물들이 포함되어 있음
- 단순히 가장 큰 confidence score를 갖는 class 하나만을 선정하는 방법은 합리적이지 않게 된다.
- 방식이 조금씩은 다르나 class별 threshold를 정해놓고  S > threshold 일 때
==> "주어진 이미지 안에 해당 class가 포함되어 있을 것이다" 라고 결론지음

▶ 평가척도 
• accuracy  = 올바르게 분류한 이미지 수  /  전체 이미지 수
- accuracy는 classification문제에서 일반적으로 test를 위해 측정한다.
- 이 방법은 단일 사물 분류문제에서는 즉각사용해도 별 문제가 없다. 
- 하지만 복수 사물 분류문제에서는 곤란해질 수 있어서 아래와 같은 평가척도를 사용한다.

precision과 recall 은 class A에 대해 다음과 같이 측정한다.
precision = 올바르게 분류한 A 이미지 수  /  A로 예측한 이미지 수
recall = 올바르게 분류한 A 이미지 수  /  전체 A 이미지 수 

 

 

Detection

※ Detection 
image localization이라고도 불리며 classification에서 어느 위치에 포함되어 있는지 "박스형태"로 검출하는 모델을 만드는 것이 목표이다. 이 박스를 bounding box라 한다.



▶ 평가척도 IOU (Intersection Over Union)
• 사물의 Class와 위치의 예측결과를 동시에 평가해야해서 사물의 실제 위치를 나타내는 실제 bounding box (GT bounding box)정보와 예측 bounding box가 얼마나 "겹쳐지는지"를 평가한다.
Bp: 예측 bounding box
Bgt: GT bounding box
Bp와 Bgt의 IOU = Bp∩Bgt 영역넓이  /  Bp∪Bgt 영역넓이
보통 이에 대한 threshold를 0.5로 정해놓고 있으며 여러개의 bounding box가 모두 IOU를 50%를 넘겨 매칭되면
결과적으로 매칭에 실패한것으로 간주한다. (즉, 복수 개의 매칭이 되면 안된다.)

 

 

Segmentation

※ Segmentation 
어느 위치에 있는지를 "Pixel 단위로 분할"하는 것으로 Detection보다 더 자세하게 위치를 표시한다.
이미지 내 각 위치상의 Pixel들을 하나씩 조사, 조사대상 Pixel이 어느 Class에도 해당하지 않으면?
=> background Class로 규정하여 해당 위치에 0을 표기하고 이 결과물을 mask라 부른다.

§ Semantic Segmentation
- 분할의 기본단위: Class
- 동일한 Class에 해당하는 사물은 동일한 색상으로 예측하여 표시

§ Instance Segmentation
- 분할의 기본단위: 사물
- 동일한 Class에 해당하더라도 서로 다른 사물이면 다른 색상으로 예측하여 표시

▶ 평가척도 IOU (Intersection Over Union)
Ap: 예측 mask의 특정 Class 영역
Agt: GT(실제)  mask의 해당 Class 영역
Ap와 Agt의 IOU = Ap∩Agt 영역넓이  /  Ap∪Agt 영역넓이

 

 

 

 

 

※ Pixel (픽셀)이란?

• 픽셀을 이용해 정보를 표현하기에, 픽셀은 한 단위(unit)로 "인간이 정의한 것"이다. 

• 또한 pixel = brightness (밝기)를 이용해 표현하는데 이를 통해 알 수 있는 중요한 사실이 있다.

즉, 픽셀은 이미지의 최소단위이다! (픽셀이 나타내는 값 = 밝기.)

픽셀 함수에서 표현되는 데이터의 느낌과 정 반대가 있다.

vector(수학적 의미가 아닌, vision information 측면의 벡터), 벡터는 함수같은 형식


픽셀: noncontinuous (어떤 unit을 정해 vector의 특정 부분을 묶은 것)
벡터: continuous한 것


실제 존재하는 사물들은 vector자체로 저장하기는 너무 방대하고 연속적이기에

이를 grid로 모두 잘라 pixel로 하여 특정범위별밝기를 0.1, 0.9처럼 하여 그 값들을 data로 3, 23처럼 저장하는 것

이렇듯 pixel은 숫자여서 우리가 보고 있는 모든 것들을 숫자로 표현가능하기에 컴퓨터 비전이 발전할 수 있었다.

 

 

 

 

※ Pixel과 RGB

pixel은 숫자로 이루어져 있는데 그 크기가 1 Byte이다. (0~255의 숫자)
R(1 Byte)     G(1 Byte)     B(1 Byte) 이렇게 (1Byte)^3으로  총 1600만가지 색을 표현할 수 있다.


A(1 Byte): Alpha, 즉 투명도까지 이용한다면?

 

결과적으로 image는 4 Byte로 표현이 가능하며 이것이 바로 1 Pixel 이다.


 

[실습]

<import 부분>

import numpy as np
import cv2  # camera, image 등 conputer vision을 쓰기 위한 openCV
import numpy as np  # 빠른 계산을 위한 라이브러리
import matplotlib.pyplot as plt  # visualization을 위한 matplotlib
cap = cv2.VideoCapture(1)  # cap이라면 변수에 카메라 기능을 가져옴 0번이 전면 카메라
ret,frame = cap.read()  #ret은 성공, 실패, frame은 image frame
print(frame)

1번 카메라로 촬영한 사진을 데이터화한 것.

 

 

 

 

 

아래 사진처럼 frame[0][0][0]에 해당하는 숫자인 121이 uint8 즉, unsigned int 8bit값인 것을 알 수 있다.

따라서 정리하자면 frame이라는 변수는 uint 8bit의 집합 data로 이루어져있음을 알 수 있다.

( image type의  default 값은 uint8이다)

 

 

 

 

 

찍은 사진에 대해 출력을 해보자.

 

 

여기서 잠깐! 

실습을 하던 중 앞에서 배운 내용을 상기시켜보자.

image = 숫자라는 것이 중요하다 하였다.

 

Q. 그렇다면, 픽셀은 밝기를 대변하니까 frame x 2를 하면?

A. 밝기가 밝아진다!

다만, 기존의 255크기를 넘어설 수 있으며 (ex 150 * 2 = 300) 이때 overflow가 발생하기에

아래와 같은 방식을 사용한다.

 

 

 

 

 

 

 

※ Contrast와 Gamma

contrast: 대비, 밝은것을 더 밝게, 어두운것을 더 밝게! 2(frame-127)+127  (x절편 이하값은 손실 발생)

따라서 손실발생을 거의 없애주기 위해서 sigmoid 함수사용!

 

[과제]. sigmoid함수를 사용한 contrast해보기!

위 사진에서 보면 빨간색 부분이 손실되는 것을 알 수 있다.



Gamma (사진변수 / 255)^(감마)  x 255
감마가 높으면 변화량이 커지고 감마가 낮아지면 변화량이 낮아지는데, 선형 contrast보다 손실발생율이 거의 없어서 훨씬 사용에 유리하다.


cf. astype을 통해 형변환이 가능하다!

plt.imshow(frame에 대한 연산.astype(np.int32))

 

 

 

 

 

 

 

 

 

 

 

 

 

※ RGB와 HSV

RGB말고도 사용할 수 있는 색 공간이 있을까?


RGB의 color space: 정육면체형
HSV의 color space: 원뿔형이고, uint를 사용한다.

 - Hue: 0~179사이로 표현 ( pi값으로 value값 1당 2도를 의미 )

           모든 pixel이 색이 조금씩 바뀜 (각도의 이동)  /  hsv[..., 0]  , 0번 채널 즉, hue에 접근하는 것.

 - Saturation: 채도, 색의 농도, 클수록 진하다  /   plt.hsv[... , 1]  ,1이라는 것은 1번 채널만 본다는 뜻이다.

 - Value: 명도, 색의 밝기를 의미





[실습]







 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

[과제 해보기]

 

※ 유클리드 호제법 (Euclidean Algorithm)

 

 

※ 등수 구하기

점수가 주어졌을 때, 등수를 구한다.

 

 

 

 

 

 

 

 

 

 

※ 방향 탐색 (knight의 이동)

 § 4방향 탐색, 8방향 탐색

 

 

 § Knight의 방향탐색

 

 

 

https://www.acmicpc.net/problem/7562

 

7562번: 나이트의 이동

체스판 위에 한 나이트가 놓여져 있다. 나이트가 한 번에 이동할 수 있는 칸은 아래 그림에 나와있다. 나이트가 이동하려고 하는 칸이 주어진다. 나이트는 몇 번 움직이면 이 칸으로 이동할 수

www.acmicpc.net

https://www.acmicpc.net/problem/16959

 

16959번: 체스판 여행 1

크기가 N×N인 체스판이 있고, 체스판의 각 칸에는 1부터 N2까지의 정수가 한 번씩 적혀있다. 지학이는 이 체스판을 이용해서 재미있는 게임을 해보려고 한다. 지학이가 가지고 있는 말은 나이트,

www.acmicpc.net

https://www.acmicpc.net/problem/16952

 

16952번: 체스판 여행 2

크기가 N×N인 체스판이 있고, 체스판의 각 칸에는 1부터 N2까지의 정수가 한 번씩 적혀있다. 지학이는 이 체스판을 이용해서 재미있는 게임을 해보려고 한다. 지학이가 가지고 있는 말은 나이트,

www.acmicpc.net

 

 

 

 

 

※ 이차원 배열을 이용한 부분합

 

 

 

 

 

 

 

 

 

 

- Euclidean Algorithm(유클리드 호제법)
https://www.acmicpc.net/problemset?sort=ac_desc&algo=26

- 등수 구하기

- 방향 탐색(4방향, 8방향, knight)
https://www.acmicpc.net/problem/7562

- 이차원 배열의 부분합
- 이차원 배열의 부분합(feat. 포함-배제의 원리)
https://www.acmicpc.net/problemset?sort=ac_desc&algo=139

 

※ Design Pattern이란?

[Buschmann, et al. 1996]_ 최고의 캡슐화의 한 방법으로 매우 효율적으로 프로그래머들이 문제를 해결할 수 있을 것이다.

안정성, 확장성 등에도 효율적이며 패턴이 반복되는 설계문제를 해결하도록 하며 대표적으로 다음과 같은 예시들이 있다.

 

이렇게 많은 디자인 패턴 종류에서 유명한 3가지인 싱글톤, 팩토리 메소드, 브릿지 패턴을 이번에 정리할 것이다.

 

 

※ Singleton

하나의 (전역)인스턴스만 생성하여 사용하는 디자인 패턴.

인스턴스가 필요할 때, 기존의 인스턴스를 활용하는 것.

한번의 new를 통한 객체생성으로 메모리 낭비를 방지가능!

딱 하나의 독자적인 클래스생성을 진행하며 그 클래스의 객체가 복사되면 안된다.

[구조]

class Singleton {
private:
    // 생성자는 private으로 막는다.
    // 따라서 외부에서 new를 이용한 객체생성이 불가능하다.
    Singleton();

    Singleton(const Singleton& ref) { }

    Singleton& operator=(const Singleton& ref) { }

    ~Singleton() { }

    // 객체 하나를 담을 수 있는 포인터 변수를 선언
    // 이때, static으로 선언해서 단 하나만 존재할 수 있게 한다.
    static Singleton *single;

public:
    // single을 가져오거나 해제하는 멤버함수 선언
    // static변수에 접근하고 외부에서 쓸 수 있어야 해서 publc으로 지정
    static Singleton *getInstance();
    static void DestroySingle();
};

Singleton *Singleton::single = nullptr;     // static멤버변수이기에 클래스 외부에서 초기화
Singleton::Singleton() { }

Singleton *Singleton::getInstance() {   
    if (!single)        // single이 nullptr일 때, 
        single = new Singleton;   // 새로 생성해 객체를 초기화
    return single;      // 앞으로 호출될 때마다 이미 생성된 객체를 return
}

void Singleton::DestroySingle() {   // getInstance() 함수와 유사
    if (!single)
        return;
    delete single;
    single = nullptr;
}

 

 

 

 

 

※ Factory Method

객체생성이 복잡하거나 어렵다면, 이를 대행하는 함수를 두는 설계방식.객체를 생성하지만 생성자를 호출하지 않고 대행함수를 통해 간접적으로 객체를 생성한다.

팩토리 메서드 패턴은 복잡도가 낮고 높은 수준의 유연성을 제공해야할 때, 매우 유용하다.

즉, 생성자 기능을 대신하는 메소드를 별도로 정의하는 방식이다.

[구조 예시]

#include <iostream>
#include <string>
using namespace std;


class Product {  // 인터페이스(interface) 선언
public:
    virtual string Operation() const = 0;
    virtual ~Product() { }
};

class ConcreteProduct1 : public Product {   // 인터페이스 상속
public:
    string Operation() const override {
        return "{Result of the ConcreteProduct1}";
    }
};
class ConcreteProduct2 : public Product {   // 인터페이스 상속
public:
    string Operation() const override {
        return "{Result of the ConcreteProduct2}";
    }
};

/* Product 인터페이스의 객체를 return하는 factory method를 선언하는 Creator 클래스
 * Creator클래스의 하위 클래스는 factory method를 상속받음 */

class Creator {     // 인터페이스 클래스는 선언만 진행 (구현X)
public:
    virtual Product *FactoryMethod() const = 0;
    virtual ~Creator(){};

    /* factory method에서 반환된 product 객체에 의존한다. */
    string SomeOperation() const {

        // Product객체생성을 위한 factory method 호출.
        Product *product = this->FactoryMethod();

        string result = "Creator: 동일한 creator의 코드가 " + product->Operation() + "과 작동중";
        delete product;
        return result;
    }
};

/* product type변환을 위해 factory method를 재정의하는  Concrete Creator 클래스들 */
class ConcreteCreator1 : public Creator {
public:
    Product *FactoryMethod() const override {
        return new ConcreteProduct1();
    }
};
class ConcreteCreator2 : public Creator {
public:
    Product *FactoryMethod() const override {
        return new ConcreteProduct2();
    }
};

/* ClientCode 함수는 ConcreteCreator 객체와 함께 작동
 * 기본 인터페이스로 어떤 creator 하위클래스에도 전달 가능 */
void ClientCode(const Creator& creator) {
    cout << "Client: I'm not aware of the creator's class, but it still works.\n" << creator.SomeOperation() << endl;
}

int main() {
    cout << "App: Launched with the ConcreteCreator1.\n";
    Creator *creator = new ConcreteCreator1();
    ClientCode(*creator);
    
    cout << endl;
    
    cout << "App: Launched with the ConcreteCreator2.\n";
    Creator *creator2 = new ConcreteCreator2();
    ClientCode(*creator2);

    delete creator;
    delete creator2;
    return 0;
}

 

 

 

 

 

 

 

 

 

※ Bridge pattern

구현부에서 추상층을 분리, 각각 독립적으로 변형과 확장이 가능하게 하는 패턴  java에선 super키워드도 사용

따라서 두 계층 모두 추상화된 상위 타입을 갖고 의존성은 상위 타입간에만 이뤄진다.

인터페이스와 구현방식이 완전 결합되는 것을 피할 때 사용

하위 클래스 구조가 서로 다른 형태이길 원할 때 사용

 

과거 C++개발자들은 컴파일 단축을 위해 Pimpl이라는 독특한 관례를 사용했다. (Pointer to Implement)

Pimpl은 말그대로 구현부를 포인터로 참조하는 것이다.

장점 1: 클래스의 private, protected멤버가 헤더로 노출되기에 불필요한 노출을 막을 수 있다.

장점 2: 숨겨진 구현클래스에 대한 수정이 바이너리 호환성에 영향X

장점 3: 헤더파일에 구현에 필요한 다른 헤더를 포함하지 않아도 된다.

 

컴포넌트 간 다양한 조합이 가능할 때 효과적이며 실제 구현을 모두 인터페이스에 위임한다.

[구조 예시]

 

 

Abstraction: 기능 계층최상위 클래스.

구현부분에 해당하는 클래스를 객체를 이용구현부분의 함수를 호출

Refind Abstraction: 기능 계층에서 새로운 부분을 확장한 클래스

 

Implementation: 추상클래스의 기능구현하기 위한 인터페이스 정의

Concrete Implementor: 실제 기능구현하는 것

 

#include <iostream>
#include <string>
using namespace std;

/******************************<인터페이스 구현>******************************/
class Implementation {
public:
    virtual string OperationImplementation() const = 0;     // 순수가상함수
    virtual ~Implementation() { }
};
class ConcreteImplementationA : public Implementation {
public:
    string OperationImplementation() const override  {
        return "ConcreteImplementationA: Here's the result on the platform A.\n";
    }
};
class ConcreteImplementationB : public Implementation {
public:
    string OperationImplementation() const override {
        return "ConcreteImplementationB: Here's the result on the platform B.\n";
    }
};

/******************************<추상클래스 구현>******************************/
class Abstraction {
protected:
    // 인터페이스 클래스에 대한 포인터참조로 브릿지 패턴을 잘 나타내고 있음을 알 수 있다.
    Implementation *implementation_;    
public:
    Abstraction(Implementation *implementation) : implementation_(implementation) { }
    virtual string Operation() const {
        return "Abstraction: Base operation with:\n" + this->implementation_->OperationImplementation();
    }
    virtual ~Abstraction() { }
};
class ExtendedAbstraction : public Abstraction {
public:
    ExtendedAbstraction(Implementation* implementation) : Abstraction(implementation) { }
    string Operation() const override {
        return "ExtendedAbstraction: Extended operation with:\n" + this->implementation_->OperationImplementation();
    }
};

void ClientCode(const Abstraction& abstraction) {
    cout << abstraction.Operation();
}

int main() {
    Implementation *implementation = new ConcreteImplementationA;
    Abstraction *abstraction = new Abstraction(implementation);
    ClientCode(*abstraction);

    cout << endl;

    delete implementation;
    delete abstraction;

    implementation = new ConcreteImplementationB;
    abstraction = new ExtendedAbstraction(implementation);
    ClientCode(*abstraction);

    delete implementation;
    delete abstraction;
}

출처: https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=kyung778&logNo=60154874584

 

 

 

※ 스트림(stream)

순서가 있는 데이터의 연속적인 흐름으로 다음과 같은 입출력 관련 클래스가 있다.

  • ofstream  => 출력파일 stream클래스, 출력파일생성 및 파일에 데이터 쓸 때 사용
  • ifstream   => 입력파일 stream클래스, 파일에서 데이터를 읽을 때 사용한다.
  • fstream    => 일반적인 파일 스트림

 

 

※ 파일 읽기

#include <iostream>
#include <fstream>
#include <string>
using namespace std;


int main(){
    ifstream is{"example.txt"};
    string str;

    while(is){
        is >> str;
        cout << str << " ";
    }
    cout << endl;
    // 객체 is가 범위를 벗어나면 ifstream 소멸자가 파일을 닫는다.
}

 

※ 파일 쓰기

 

※ 파일 모드

쉽게 풀어쓴 C 프로그래밍 참조

 

 

 

 

 

※ 파일에서 읽은 data를 객체로 vector에 저장했다 출력하기

class Temperature {
public:
    int hour;
    double temperature;
};

int main(){
    ifstream is{"temp.txt"};
    if (!is){
        cerr << "file open failed" << endl;
        exit(1);
    }

    vector<Temperature> v;
    int hour;   double temperature;

    while (is >> hour >> temperature){
        v.push_back(Temperature{hour, temperature});
    }
    for (Temperature t:v) {
        cout << t.hour << "시의 온도는 " << t.temperature << endl;
    }
}

 

 

 

 

 

※ 저장된 txt파일을 읽어 앞에 숫자를 붙인 후 출력파일에 기록해보자.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

cf-1. 절대경로로 File I/O, File Open 하는법

1. Edit Configurations를 클릭한다.
Working directory를 원하는 절대경로로 지정해준다. (원상복구 시키고 싶으면 비워주면 됨)

 

 

 

cf-2) 상대경로로 txt파일을 내부에 만들고 사용하기

※ 약수, 배수 (최대공약수, 최소공배수)

cf. 최대공약수, 최소공배수를 재귀함수로 해결하기. https://chan4im.tistory.com/58

 

[Baekjoon/백준] 13241, 5347번: [Recursion] (C/C++) ★★☆

※ 이번 문제에서 알게된 점 § 재귀함수로 구현하는 gcd, lcm 함수 // 최대공약수 auto gcd(long long A, long long B){ if (B == 0) return A; else return gcd(B, A%B); } // 최소공배수 auto lcm (long long A, long long B){ return A*B

chan4im.tistory.com

 

 

※ 최소, 최대, 최빈값 찾기

§ 최소, 최대값을 찾기 위한 방법: https://chan4im.tistory.com/48

 

this.algorithm(2). merge() 함수, copy() max(), min() 함수 ★★★★★

※ vector를 이용한 합병정렬 구현 #include #include #include using namespace std; int main() { vector v1(2); vector v2(3); for (int i = 0; i < v1.size(); i++){ cin >> v1[i]; } for (int i = 0; i < v2.size(); i++){ cin >> v2[i]; } vector v3; v3.res

chan4im.tistory.com

 

 

§ 최빈값 찾는 알고리즘

 

 

 

※ palindrome (회문) 

https://www.acmicpc.net/problem/17609

 

17609번: 회문

각 문자열이 회문인지, 유사 회문인지, 둘 모두 해당되지 않는지를 판단하여 회문이면 0, 유사 회문이면 1, 둘 모두 아니면 2를 순서대로 한 줄에 하나씩 출력한다.

www.acmicpc.net

 

 

 

 

 

 

※ 일정구간 연속값 찾기

 

 

 

※ 소수판별과 에라토스테네스의 체

 

 

 

 

 

 

 

※ 부분합 (누적합)

 

 

 

 

 

 

 

 

 

 

 

 

- 약수, 배수, 최대공약수, 최소공배수

- 소문자->대문자 , 완전수

- Palindrome, 최대,최소,최빈값
https://www.acmicpc.net/problem/17609

- 소수판별, 에라토스테네스의 체
https://www.acmicpc.net/problem/1929

- 부분합
https://www.acmicpc.net/problemset?sort=ac_desc&algo=139

※ 매크로와 preprocessor

 

 

 

※ 조건부 컴파일

 

 

 

 

 

 

 

※ 파일분할

 

 

 

 

 

※ 헤더파일 디자인

 

 

 

+ Recent posts