현재도 사용하는 방식은 Residual shortcut connection에 관한 내용이다.

다 읽고 나서 드는 생각은 논문이 정말 읽기 쉽고 다양한 실험들을 포함하고 있어 정말 좋은 논문인 것 같다

 

1. introduction

  • 이 시대에 깊은 convolution network가 이미지 분류 문제를 리드하고 있었고 대부분의 논문에서 네트워크의 깊이가 정답률과 error에 매우 깊은 상관이 있다고 본다.
  • 그럼 여기서 물음이 "더 좋은 네트워크를 만드는 것이 레이어를 더 쌓는거보다 쉬운가?"가 된다 
  • 당연히 그냥 레이어를 더 쌓는것이 더 쉬운 해결책이 될 것이다. 
  • 하지만 그냥 레이어를 쌓기만 한다면 어느 시점에서 부터 training error가 증가하게 된다. - degradation problem
  • 또한 제대로 초기화되지 않은 네트워크는 vanishing/exploding gradients가 발생했고 제대로 초기화가 됐더라도 학습이 수렴함에 따라 정확도가 포화되는(saturated) 문제가 발생하였다 이 문제는 overfitting 때문이 아니다. 
  • 저자는 더 깊은 신경망을 학습시키기 위한 deep residual learning framework를 소개한다
  • 몇개의 쌓인 레이어가 다음 레이어에 직접적으로 연결되는 대신 residual mapping에 맞도록 명시적으로 연결한다 
  • 공식으로는 F(x) := h(x)-x 가 되고 이는 F(x) + x := H(x)가 된다 
  • 이 논문에서 identity mapping이라는 말이 많이 나오는데 이 뜻은 residual mapping시에 다른 작업을 하지 않고 단지 더한다는 의미이다 
  • 본 논문에서는 shortcut connection을 identity mapping으로 하였다 
  • Identity shortcut connection은 추가적인 파라미터가 필요하지 않아 complexity가 증가하지 않고 모든 네트워크에 end-to-end 학습이 가능하다 
  • 또한 기존의 문제인 정확도 포화 문제에서 네트워크의 깊이가 증가함에 따라 쉽게 정확도의 증가를 얻을 수 있었다 

2. Related work 

  • - Residual Representations
    • 과거 논문에서 벡터 양자화의 경우 원본 벡터를 인코딩 하는 것보다 residual 백터를 인코딩하는 것이 더 효과적으로 나왔다
    • 이런 solvers는 솔루션의 residual nature을 인식하지 못하는 solver보다 더 빠르게 수렴한다 
    • residual을 이용한다면 더 좋은 reformulation과 더 쉬운 preconditioning 최적화를 보인다 
  • - Shortcut connection
    • 많은 실험과 이론이 있었고 오래 연구되어온 분야이다 
    • 이전의 실험에서는 MLP에서 네트워크 입력과 출력에 linear layer를 추가하는 것도 있었다

  • 또한 Vanishing/Exploding gradients를 피하기 위해 auxiliary classifier를 추가하기도 하였다 -(Inception network) 
    • 기존의 논문들은 shortcut connection에 의해 구현된 레이어 응답, gradients, propagated errors를 centering 하는 방법을 제안했다 
    • 저자의 작업과 비슷한 시기에 highway networks라는 shortcut connections와 gating function을 사용하는 것이 제안됐는데 이 gate(highway)들은 데이터에 의존하고 파라미터를 가지고 있다. 그에 반해 identity shortcuts는 추가적인 파라미터가 없고 gated shortcut이 0으로 근사될 때 highway networks는 non-residual function이 될 수 있지만 저자의 공식은 항상 residual function을 학습하게 된다 정대 closed 되지 않고 모든 정보는 항상 전달된다 
      - 아래의 그림을 보면 처음 값이 음수 일 때 H(x)가 ReLu라고 가정한다면 음수는 0이되어 해당 노드는 더이상 값을 전파하지 않는다 - 하지만 residual을 뒤에서 합쳐주게 되면 residual의 정보가 넘어가 0이 되지 않는다 
      하지만 0이 처음부터 입력으로 들어왔다면 해당 노드는 0으로 계속 유지된다 하지만 같은 계층의 다른 노드의 값이 0이 아니라면 해당 노드에서 전파된 값으로 인해 노드는 close 되지 않는다

3. Deep Residual Learning 

  • - Residual learning 
    • 여러 비선형 레이어가 복잡한 함수를 점근적으로 근사할 수 있다고 가정하면 H(Hypothesis)가 점근적으로 residual fucntion을 근사화할 수 있다 
    • F(x) := H(x) - x  -> F(x) + x := H(x) 다음 공식이 성립하고 이 공식은 학습의 용이함이 다를 수 있다 
    • 기존의 네트워크가 깊어지면서 성능이 감소되는 문제는 solvers가 많은 비선형 레이어에 의해 identity mapping을 근사하기 어렵다는 것을 의미하는 것으로 보인다 만약 identity mapping이 최적이라면 solvers는 identity mapping에 가까워지기 위해 단순히 많은 비선형 레이어의 가중치를 0이 되도록 할 수 있다 
    • Perturbation Theory : 해석적으로 풀 수 없는 문제의 해를 매우 작다고 여길 수 있는 매개변수들의 테일러 급수로 나타내는 이론, 파라미터들이 매우 작으므로 급수의 유한개의 항을 계산하여 근사적인 해를 얻을 수 있다 
    • 최적의 함수가 zero mapping 보다 identity mapping에 가깝다면 solver가 새로운 함수를 학습하는 것보다 identity mapping을 reference 하여 perturbation을 찾는 것이 더 쉬울 수 있다 

 

  • - Identity mapping by Shortcuts 
    • Shortcuts을 사용할 때는 일반 네트워크와 더해지는 부분을 제외한다면 complexity는 똑같다
    • 여기서 F와 x 의 차원은 반드시 같아야 하기 때문에 linear projection을 이용할 수 있다 (padding, 1x1 conv)
    • Residual function을 다양한 방법으로 적용할 수 있지만 이 논문에서는 2개 또는 3개 층에서만 Shortcut을 진행한다. 
    • 단 하나의 레이어에서 Shortcut을 이용한다면 이는 linear layer와 유사하기 때문에 y = W_1x + x의 이점은 관찰되지 않았다 

4. Network Architectures

  • - Plain network를 학습시킬 때는 두가지를 따랐다
    • 1. Output feature map의 size는 같다. 레이어는 같은 수의 필터를 갖는다 
    • 2. Feature map의 size가 절반으로 줄면 레이어당 복잡도를 유지하기 위하여 필터의 수가 두 배로 늘어난다
  • - Residual Network
  •  Plain network에서 shortcut connection을 추가하였다 
  •  Input과 output의 사이즈가 같다면 directly 연결하였고 dimensions가 다르다면 두 가지 옵션을 고려하였다 
    • 1. Shortcut이 여전히 identity mapping이라면 추가로 zero padding을 추가하여 diemnsions를 증가시켰고 이러한 방법은 추가적인 파라미터를 필요로 하지 않는다 
    • 2. 1x1 convolution을 사용하여 dimentions을 맞추었다 두 방법 모두 stride는 2로 수행된다 
  • - Implementaion
    • 256, 480 사이즈 이미지에서 랜덤하게 sampled 하였고 수평 반전, 픽셀 평균 빼기, color augmentation을 사용하였고 각 convolution과 activation 사이에 batch normalization을 사용하였다 
    • SGD를 사용하였고 mini batch size는 256 
      learning rate는 0.1 error가 변하지 않으면 10으로 나누었다 
      600000 iterations를 진행 
      weight decay는 0.0001
      momentur은 0.9 
      dropout은 사용하지 않음 
    • testing에서는 기존과 같이 10개의 crop에서 테스트 하고 fully-convolution form을 사용 결과 scores를 average 함
    • 18-layer의 plain/residual nets를 비교하였을 때 성공적으로 training error를 줄였고 정확도는 비슷하지만 18-layer ResNet이 더 빠르게 수렴하는 것을 보면 이는 최적화를 용이하게 한다는 것을 볼 수 있다 

  • Compare with zero-pad between projection 
    • A : zero padding 
    • B : 차원 증가시킬 때에만 projection 사용
    • C : 모두 projection 사용
    • A의 경우 다른 것보다 성능이 별로였는데 이유는 0으로 채워진 차원이 실제로 residual learning을 하지 않기 때문이라고 본다 
    • C의 경우 B보다 약간 좋은데 이것은 추가된 파라미터 때문이고 A/B/C 간의 차이는 projection shortcuts이 degradation problem을 해결하는데 필수적이지는 않다는 것을 나타낸다 
    • B/C의 경우 파라미터 증가에 따라 모델 사이즈와 복잡도, 메모리 사용량이 증가한다 

  • Deeper bottleneck Architectures 
    • 깊은 네트워크를 학습시키는 것은 트레이닝 시간에 대한 우려가 있다 
    • 그래서 깊은 네트워크를 학습시킬 때는 bottleneck design을 사용하였다 
    • 모든 residual fucntion을 3 layer마다 사용하였고 이 3개의 레이어는 1x1, 3x3, 1x1 convolution이다 
    • 1x1 conv는 차원을 늘리고 줄이는 데 사용되었고 3x3 레이어는 더 작은 입력/출력 차원으로 병목을 남긴다. 이 디자인은 비슷한 시간 복잡도를 갖는다 
    • Figure 5에서 identity connection은 bottleneck 구조에서 특히 중요한데 아래 그림에서 identity shortcuts을 projection shortcut으로 변경하면 shortcut은 두 개의 고차원으로 연결되므로 시간 복잡도와 모델 크기가 두배로 증가함을 알 수 있다 따라서 bottleneck architecture를 위한 설계는 projection shortcut보다 identity shortcuts이 더 효율적인 모델로 볼 수 있다 

  • 50-layer ResNet 
    • 기존 34-layer Resnet에서 3-layer bottleneck block으로 변경하였고 3.8 billion FLOPSs를 가진다
    • 3-layer bottleneck blocks을 사용하여 depth가 매우 증가하여 152개의 레이어를 쌓아도 VGG-16/19보다 더 작은 complexity를 가지고 더 정확하다 또한 기존의 네트워크의 깊이가 깊어짐에 따라 정확도가 감소하는 문제도 관측되지 않았다 

  • Analysis of Layer Responses
    • 각 3x3 laayer에서 BN뒤 non-linearity에 들어가기 전 레이어의 표준편차를 보았다
    • ResNet은 plainNet보다 더 작은 응답 강도(response strength)를 보여줬다 
    • 또한 ResNet-20,56,110간의 비교를 보면 더 깊은 ResNet이 더 작은 크기의 응답을 가짐을 알 수 있다
    • 레이어가 많을수록 ResNet의 레이어는 신호를 덜 수정하는 경향이 있다

  • Exploring Over 1000 Layers 
    • 1202-layer를 사용해도 최적화에 어려움은 없는 것을 보였지만 110-layer보다 더 낮은 결과를 보면 여전히 문제는 존재한다 
    • 두 네트워크가 비슷한 training error를 보이지만 정확도가 더 낮은 것을 보면 이것은 overfitting 때문이라고 보인다
    • 부족한 데이터셋으로 인해 불필요하게 레이어가 커졌다고 보인다 
    • maxout이나 dropout 같은 강력한 정규화 방식이 있지만 저자는 테스트를 진행하지 않았고 아마 효과가 있을 것이라고 생각한다 나온다

속도를 산출하는 과정에서 핀홀 카메라에서 기준선을 그리려 보니 원근에 의해 정확하게 차량의 이동 방향과 수평되는 선을 그리기가 어려워 bird-eye view를 통해 선을 그린 후 해당 선을 다시 원본 영상에 투영하려 했다

 

먼저 bird-eye view를 만들기 위해 opencv에서 제공해주는 getPerspectiveTransform을 이용하였다 

 

실제 적용은 도로에서 실행했지만 보기 좋은 예시 그림을 가지고 포스트를 올린다.

 

1. 원본사진에서 bird-eye view로 볼 부분의 4점을 선택한다. 

source_coord = np.float32( mouse_events.points )
H = max(mouse_events.points[2][0],mouse_events.points[3][0]) - min(mouse_events.points[0][0],mouse_events.points[1][0])
W = max(mouse_events.points[0][1], mouse_events.points[1][1], mouse_events.points[2][1], mouse_events.points[3][1] ) - min(mouse_events.points[0][1], mouse_events.points[1][1], mouse_events.points[2][1], mouse_events.points[3][1] )
destination_coord = np.float32( [ [0,0], [0,H], [W,0], [W,H] ] )
M = cv2.getPerspectiveTransform(source_coord, destination_coord)

2. opencv에서 제공해주는 cv2.getPerspectiveTransform과 cv2.warpPerspective를 이용하면 쉽게 계산결과를 볼 수 있다 

( getPerspectiveTransform에서는 순서대로 선정보를 입력해줘야한다 - 좌상, 좌하, 우상, 우하 )

M = cv2.getPerspectiveTransform(coord1, coord2) 

transformed = cv2.warpPerspective(image, M, (W, H))

transformed = cv2.warpPerspective(frame, M, (W,H))

계산결과가 잘 나왔다 

 

3. 계산 결과에 선을 그린다 

4. 이제 원본 이미지에 해당 선을 다시 그려야하는데 아무것도 모른상태로 함수를 이용하여 변환했기 때문에 역변환을 제공해주는 함수가 있는가 찾아봤지만 찾지 못했다 그래서 공식을 이용해서 역변환 하기로 하였다 

 

시점변환(perspective transform)의 공식은 다음과 같다 

여기서 우리가 알고 있는 것은 x', y', x, y 이다 x'과 y'은 변경시킬 좌표이고 x, y 는 원본에서의 좌표이다 

 

x',y'은 [0,0, 0,H, W,0, W,H] 가 되고 ( W : 넓이, H : 높이) 

x,y 는 마우스를 통해 찍은 점의 위치 대략적으로 [100,30 0,400 300,100 330,450] 이라고 하겠다 

 

getPerspectiveTransform을 이용하여 바꿀 위치들을 지정해주면(x', y') 위의 공식에서 h들에 대한 값이 return 된다  (이후부터 h들을 H라고 하겠다 )

그렇다면 [wx' wy' w] = H@[x y 1] 이 되고 우리가 구한 것은 w와 x' y'이 되었다 

이제 우리는 x' y' 이 있다면 역변환을 구할 수 있을것이다 

여기서 삽질을 정말 오래 했는데 w 를 보지 못하고 계속 [x' y' 1] 만을 이용하여 왜 생각대로 안되지 라고 한 3시간 찾아보았다 

어쨋든.... 

 

우리가 구할것은 이제 x' y' w 를 알고 있을 때 x y 를 구하는 것이다 

우리가 알고 있는 것은 x' y' w H 이니 이리저리 나누고 곱하고 해보자 

 

warpPerspective를 통해 나오는 값은 wx' wy' w 이다. 그러므로 원본 을 구하기 위해서는 w 를 모든 항에 나눠 x' y' 을 구해야 한다 그럼 끝나는 건데 w 를 생략해서 계속 고민하고 있었다니....

 

역행렬을 구할 때는 numpy 에 linalg.pinv 또는 linalg.inv를 이용하면 된다 pinv는 persuade inverse이다

첫번쨰 : 원본사진 두번째 : 변환 후 사진 세번째 : 역변환 사진

line_coord = np.float32(mouse_events2.points)
One = np.squeeze(INV_M @ np.expand_dims(np.append(line_coord[0], 1), axis=1))
Two = np.squeeze(INV_M @ np.expand_dims(np.append(line_coord[1], 1), axis=1))
One = One[:2] / One[2]
Two = Two[:2] / Two[2]

선이 잘 그려졌다! 

 

해당 코드는 깃헙에 업로드 되어있다. 

https://github.com/dldidfh/tistory_code/tree/master/%EC%9B%90%EA%B7%BC%ED%88%AC%EC%98%81

 

GitHub - dldidfh/tistory_code

Contribute to dldidfh/tistory_code development by creating an account on GitHub.

github.com

 

2014년 Imagenet Challenge에서 1등을 차지한 네트워크에 대한 논문이다 

 

요약

  • 이때 시대에 Inception 영화에서 나온 meme인 "We need to go deeper"에서 유래한 논문제목인가 보다 
  • 네트워크의 이름 또한 기존 neural net의 시조 격인 Le-Net에서 자신의 회사 이름 Google을 합쳐 냈다 보다
  • 중요한 내용은 어떻게 해서 좋은 performance와 computation을 가져왔는지 이다 
  • performance는 주로 네트워크의 depth와 width를 증가시켜는 추세인데 해당 과정은 계산 비용을 증가시켜 어떻게 하면 depth와 width를 늘리고 계산 비용을 최적화할 수 있을까? 
  • Hebbian principle이 나오는데 이 원리는 기본적인 neural network의 원리인 전파와 역전파의 토대가 된다 
    • Donald hebb이라는 캐나다의 심리학자가 시냅스의 연결강도 조정을 위한 생리학적 학습 규칙을 기술
    • 만약 연접(synapse)양쪽의 뉴런이 동시에 또 반복적으로 활성화되었다면 그 두 뉴런 사이의 연결 강도는 강화된다 
    • "Neurons that fire together wire together"
  • 22-layer의 GoogLeNet이라는 이름으로 ILSVRC14에 제출되었다

Introduction

  • 이 시기에 딥러닝은 dramatic한 성장이 이뤄지고 있었고 
  • 이것이 하드웨어의 성능향상, 큰 데이터셋, 큰 모델에 국한된 것이 아닌 몇 가지 새로운 아이디어와 알고리즘, 네트워크 아키텍처의 향상에서 비롯된 것이다
  • 본 논문에 제시된 네트워크는 2012년 같은 대회에서 1등을 차지한 AlexNet보다 12배 적은 파라미터를 사용하였고 정확도 또한 높았다 
  • Classification과 object detection 문제에 대해서 computation을 고려하기 위하여 이전에 나온 R-CNN을 참고하였다 나온다 
  • 모델은 추론시에 1.5 billion의 컴퓨팅 자원으로 곱셈, 덧셈 연산을 유지하도록 하였고 실환경에서 사용할 수 있도록 하였다
  • 모델의 구조는 기존에 나온 NiN(Network in Network)에서 파생하였다 
  • 주요 쟁점은 새로운 네트워크 구성을 소개하고 어떻게 네트워크의 depth를 증가시킬 수 있냐 이다 

 

Related work 

  • LeNet-5에서 시작된 CNN은 Conv layer를 쌓고 뒤를 따르는 FCL을 가지고 있다. 이러한 디자인은 여러 이미지 분류 대회에서 가장 좋은 결과를 가져오고 있고 최신 트렌드는 레이어 수의 증가와 레이어 사이즈의 증가이고 overfitting을 막기 위해 dropout을 사용한다 
  • Serre et al. [15]이 여러 스케일을 처리하기 위해 사용한 고정된 여러 사이즈를 가지는 Gabor filters 를 사용했듯이 
    • Gabor filter - 사람의 시각체계가 반응하는 것과 비슷한 외곽선을 검출하는 기능을 하는 필터 
  • Inception layers는 계속하여 반복되면서 22-layer의 deep model로 구성된다 
  • 기존의 Network in Network 모델은  1x1 conv layer를 이용하여 뉴럴넷의 표현력을 향상하고자 하였다 하지만 GoogLeNet의 경우 다른 의미로 제안한다 
    • 차원 축소 - 컴퓨팅 병목 제거 - 네트워크 사이즈 제한 
    • 이것은 네트워크의 성능 페널티 없이 depth만 증가시키는 것이 아닌 width 또한 증가시킬 수 있다 
  • R-CNN의 경우 색상과 superpixel(비슷한 영역끼리 묶은 하나의 픽셀)과 같은 저수준 신호를 우선 추정한 뒤 CNN 분류기를 통해 각 location별 카테고리를 정의한다 - 저자도 R-CNN과 같은 파이프라인으로 detection을 구성하였고 그 두 가지 단계를 강화하기 위해 앙상블과 다중 박스 예측을 사용헀다 

Motivation and High Level Considerations 

  • 정답률을 증가시키기 위한 가장 올바른 접근은 네트워크의 사이즈를 증가시키는 것인데 이런 경우 단점이 있다 
    • 큰 사이즈 라는것은 큰 파라미터를 말하고 특히 데이터셋은 제한되어있기 때문에 이것은 오버피팅 될 수 있다 
    • 큰 사이즈는 병목현상을 부를 수 있고 고급진 데이터셋은 만드는 것이 까다롭고 검수가 필요하고 전문지식이 없는 사람이 분류하기에 어려운 부분도 있다
    • 네트워크의 사이즈를 증가시키면 GPU 메모리 같은 Computational resources가 증가하게 된다 예를 들어 Conv layer가 chained 형태로 있다면 결과는 quadratic으로 증가되게 된다 - Quadratic growth - 제곱 형식으로 증가 - 이러한 용량의 증가는 대부분의 weight가 0이 된다면 많은양의 계산 자원이 낭비되게 된다 
    • 정답률을 올릴 때에도 size를 그냥 늘리는 것보다 컴퓨팅 자원을 효율적으로 분배하는 것이 좋다고 나오는데 해당 이유는 추측으로는 학습 속도이라고 생각된다 
    • 위 두가지 문제를 해결하기 위한 방법은 모든 레이어가 서로 Fully connected 된 것이 아닌 sparsely 연결되는 것이다 

Newwork in Network [12]에서 제시된 내용

Architectural Details 

  • Arora et al 이 제안한 마지막 레이어에서 layer 별로 통계 상관관계를 분석하여 상관관계가 높은 그룹으로 클러스터링 하는 구성을 제안한다 
  • Inception은 conv filter의 크기에 따라 다른 공간적 정보를 출력할 수 있다고 판단하여 여러 개의(1x1, 3x3, 5x5) conv filter를 사용하였고 또한 max pooling은 지금까지 많은 성공이 있었기에 사용했다 
  • 이러한 Inception module이 쌓이면서 출력되는 correlation statistics는 달라질 수 밖에 없다. 높은 추상화 features는 높은 layer에서 포착될 것이고 이는 더 높은 layer로 갈 수 록 3x3, 5x5 conv가 증가해야 한다는 것을 의미한다 
  • 큰 문제는 5x5는 자원을 많이 소모하는 것인데 이것은 pooling units를 추가하면 더욱 또렷해진다 pooling layer와 conv layer의 출력을 합치면 단계에서 단계로의 출력의 수가 증가고 이는 computational blow up을 부른다 
  • 비교적 큰 이미지 patch에서는 저차원의 embeddings도 많은양의 정보를 포함하고 있을 수 있다 하지만 embedding은 정보를 밀도가 높고 압축된 형태로 나타내며 압축된 정보는 모델링하기 더 어렵다 대부분의 영역에서의 표현이 희박하게 더 유지하기 위해 신호를 일괄적으로 합쳐야 할 때마다 압축한다 그렇기 때문에 컴퓨팅 자원이 많이 드는 3x3, 5x5 앞에 1x1 conv를 차원 축소를 목적으로 사용한다 
  • 낮은 레이어에서는 그냥 conv만을 이용하여 높은 레이어에서만 Inception module을 사용하였다

GoogLeNet 구조

GoogLeNet

  • competition에는 위와 같은 모델 7개 중에 6개를 앙상블 하였다 
  • 224x224 RGB 이미지를 일반적인 Normalization인 평균뺴기를 사용하였고 ReLu를 이용하였다 
  • 전체 레이어는 22개의 레이어인데 pooling까지 합치면 27개 모든 레이어를 합치면 100개 정도 된다 
  • 마지막에 linear는 fine-tuning을 위해 - label set에 맞게 하기 위해 사용하였다 
  • FCL(Linear)를 average pooling으로 변경하면 성능이 0.6%정도 향상됐다 
  • dropout의 사용은 FCL이던 average pooling이건 필수적이였다 
  • 중간에 auxiliary classifier라는 레이어를 추가하였다
    • 중간에 연결된 보조 classifier를 통해 낮은 단계에서 discrimination을 encourage 하고 다시 전파되는 기울기 신호를 증가시키며 추가 정규화를 제공할 것으로 예상 
    • 위 사진에서 inception 4a와 4b에 추가
    • 학습동안 해당 loss들은 total loss에 더해져 함께 weight를 줄이는 데 사용됐다 
    • Inference time에는 discarded 

 

Training Methodology

  • ASGD(Asynchronous stochastic gradient descent - 0.9 momentum)
  • Learning rate schedule - decreasing 4% every 8epochs 

2014년 Imagenet Challenge에서 GoogleNet 다음으로 2등을 차지한 네트워크에 대한 논문이다 

 

요약

  • 학습에 사용된 데이터셋 : ImageNet LSVRC-2010 1000classes - validation set을 test set으로 사용하였다
  • Convolution Network의 depth는 모델의 정확도에 영향을 주지만 데이터셋에 의존된다
  • 3x3 Convolution filter는 매우 효과적이다 
  • 4GPU를 활용한 병렬 학습 

1. Network configurations 

  • 이 논문에서는 동등한 설정(Hyperparameters)에서 네트워크의 depth를 증가시킴에 따라 어떤 변화가 있는지를 측정하였다 
  • Architecture 
    • 고정된 224x224 RGB 이미지를 사용 
    • 전처리는 각 픽셀을 트레이닝셋의 RGB 값 평균으로 나누는 작업만 진행 
    • 기존에는 5x5 7x7 11x11등의 receptive field를 사용하였지만 이 논문에서는 매우 작은(상, 하, 좌, 우, 중앙의 의미를 담기에 가장 작은) 3x3 receptive field를 사용하였다 
    • 1x1 receptive filter도 사용하였는데 이것은 input 채널에 대한 선형변환, 또는 Test time에서의 multi scale input을 위한 layer이다 
    • Stride는 1로 설정하고 padding은 3x3 conv 에 대하여 1로 하였다 - 차원의 수를 그대로 가져가기 위하여 
    • Max pooling은 2x2의 window를 사용하였고 stride는 2로 하였다 - Overlapping pooling을 사용하지 않음 
    • ConvNet의 최종단에는 3개의 FCL을 이용하였고 처음 2개의 FCL은 4096개의 노드를 가지고 마지막 FCL은 클래스의 개수만큼인 1000개의 노드를 가지고 있다 
    • Acivation은 ReLU를 사용하였다. 기존 AlexNet에서 사용한 LRN은 실험결과 효과가 없어서 앝은 네트워크(11 Layers)에서 한번 사용해 보고 그 뒤로는 메모리와 연산량만 늘어난다 판단외어 사용하지 않았다 

input size가 224일 때의 네트워크

2. Discussion

  • 기존에는 첫번째 layer에 사이즈가 큰 receptive field를 이용하여 공간적 특성을 최대한 많이 얻고자 했지만 저자는 3x3 receptive field만을 사용하였다. 사이즈가 큰 receptive field는 여러개의 3x3 receptive field로 대체될 수 있다. 해당 작업에 대한 이득은 다음과 같다 
    • 비선형성 레이어의 증가 
      1. 한번의 큰 사이즈의 RF(Receptive Field) Conv layer의 아웃풋에는 한번의 activation이 적용되지만 여러개의 3x3 RF를 사용하면 여러번의 activation이 적용되어 네트워크의 비선형성을 증가시켜 특징을 식별하기 좋게 한다 
    • 총 파라미터의 감소 \(R^2C^2\) 
      • 3x3 RF 3개와 7x7 RF 1개의 아웃풋은 동일하다.
      • 3x3은 3x3x3x\(C^2\) 인 \(27C^2\)
      • 7x7은 7x7x\(C^2\)인 \(49C^2\)이 된다
    • 기존에도 3x3 RF와 1x1을 사용해 왔지만 기존 네트워크는 VGGNet처럼 깊게 구축되지 않았고 데이터셋 또한 깊은 ConvNet을 구성하기에는 작았다.
    • 본 논문에서는 다른 네트워크와 다르게 첫번째 Conv layer부터 3x3 conv를 사용함으로써 더 계산량이 적고 깊은 네트워크를 구축하였다 
    •  같은 년도에 1등을 차지한 GoogleNet의 경우 여러가지 receptive field를 사용하여 1등을 하였지만 저자의 네트워크보다 연산량이 다소 많았다

3. Training

  • 입력 이미지의 사이즈는 224x224로 고정하였고 SGD의 iteration마다 하나의 이미지에 한번 crop하였고 랜덤 수평전환과 RGB 색상 변환을 사용하였다 
  • 학습에는 Single Scale과 Multi Scale 두가지로 학습을 진행했다 
    • Single Scale - Crop을 하기 전 이미지의 size가 256x256 과 384x384로 하였고 384 size의 이미지의 경우 학습의 속도를 빠르게 하기 위하여 기존 256 size로 학습한 가중치를 initialize 하는데 사용하였다. 이때 learning rate는 기존 0.01보다 작은 0.001에서 시작하였다 
    • Multi Scale - 256~512 size로 이미지의 사이즈를 다르게 하여 crop하여 scale jittering과 같은 효과를 가져왔다 - 이때도 학습속도를 빠르게 하기 위하여 위에서 384 size로 학습된 single scale 모델을 fine-tuning 하였다

4. Testing 

  • Training scale(S)에 따라 여러가지 testing scale(Q)를 이용하면 성능이 향상됐다 한다
  • 마지막 FCL들을 testing에서는 모두 conv layer로 변경을 하였는데 첫번째 4096 FCL을 7x7 conv로 변경하고 그 뒤 layer들을 1x1 layer로 변경하였다. 다음의 이득은 아래와 같다
    1. Input size에 제한이 없다 - 다양한 해상도에 따른 클래스 수와 동일한 채널 수를 가진 class score-map이 생성된다 - class score-map을 spartial average하여 최종 결과를 도출하는 pooling 개념
  • 테스트셋을 수평반전한 이미지와 원본 이미지 모두 예측하여 softmax의 결괏값을 평균하여 최종 결과를 산출했다 
  • FCN - Fully Convolution Network로 변경한 후 test time에서는 이미지를 crop할 필요가 없었다 

최종 FCL이 Conv로 변경됨

 

Multi scale 시 conv 의 아웃풋의 width 가 증가하니 global average pooling 같은 개념을 사용하여 최종 값 도출

5. Implementation details 

  • C++ Caffe Toolbox를 사용 
  • 병렬 컴퓨팅에서 각 GPU마다 batch를 하나씩 나누고 gradient가 계산되면 평균을 구해 전체 gradient를 구했다 이는 single GPU로 계산했을 때와 동일한 결과를 도출한다 
  • 학습시간은 아키텍쳐에 따라 2~3주 소모하였다 

6. Single scale evaluation 

  • LRN을 사용하지 않았다 - ReLU의 경우 AlexNet에서는 LRN을 사용하여 하나의 노드가 다음 노드로의 영향을 끼치는 현상을 방지하려 했다. 하지만 VGGNet의 경우 해당 Normalization을 적용했을 때와 아닐 때를 비교해보니 연산량은 증가되지만 정확도 부분에서는 차이가 없어 11 layer에서만 테스트 해보고 사용하지 않았다 
  • Convolution layer가 깊어질 수 록 에러율은 감소하였다 하지만 데이터셋의 부족으로 19 layer 이상에서는 변화가 없어 더이상 depth를 늘리지 않았다 
  • 테스트한 모델중에는 1x1 conv와 3x3 conv를 비교한 실험이 있는대 해당 결과 1x1 conv보다 3x3 conv의 에러율이 더 낮았다 그 이유는 conv layer가 추가됨으로 인해 비선형성이 증가함에는 도움이 되지만 공간적 맥락(spatial context)을 잡는 부분에는 3x3이 유리했다고 생각된다 
  • 테스트한 모델중 3x3 conv 와 5x5 conv를 비교한 실험이 있는데 3x3이 7%정도의 에러율 감소가 있었다. 이를 보면 작은 여러개의 filter가 넓은 하나의 filter보다 성능이 좋음을 알 수 있었다 
  • 다양한 scale의 이미지를 훈련 했을 때가 256 또는 384 하나로 학습했을 때 보다 테스트의 에러가 작았다 - augmentation 효과

7. Multi scale evaluation

  • Trainng시의 input size와 Test 때의 input size가 너무 차이가 심하면 정확도가 떨어지니 각각 256, 384, 512 size S에 대해서 {S-32, S, S+32}로 평가하였다
  • Training시에 multi scale이 적용된 모델에 대해서는 더 넓은 범위의 스케일에 적용할 수 있으므로 {Smin, 0.5(Smin+Smax), Smax}에 대해서 평가하였다
  • Densly ConvNet과 scale jittering을 동시에 이용할 때 에러율이 가장 낮았다 - Densly ConvNet은 깊은 신경망으로 생각된다
  • Multi scale training + multi scale evaluation에서 가장 높은 정확도를 얻었다 

8. Multi crop evaluation

  • 위에서 설명한 multi-crop evaluation과 dense ConvNet을 비교한다 - dense Convnet은 이미지에서 이미지를 잘라낸 후 그것들을 각각 convnet에 적용시키는 방식이 아니라 큰 이미지에 대해 곧바로 convNet을 적용하고 일정 픽셀 간격으로 마치 sliding window를 적용하듯이 결과를 끌어낼 수 있어 연산량 관점에서는 매우 효율적이지만 grid의 크기 문제로 인해서 학습 결과가 약간 떨어질 수 있다. 
  • multi-crop을 사용한것이 더 정확도가 좋았고 둘을 모두 사용한것이 가장 정확도가 좋았다
  • 그 이유는 different treatment of convolution boundary conditions 때문이라고 가정한다 

2012 Imagenet Challenge에서 1위를 차지한 네트워크에 대한 논문이다. 

 

요약 

  • 학습에 사용된 데이터셋 : 1.2 million high-resolution images, (ImageNet LSVRC-2010), 1000 classes,
  • 모델의 파라미터 : 60 million, 650,000 neurons
  • 모델의 구성 ; 5conv, maxx-pooling, 3 FCL(Fully-Connected Layers), 1000 softmax, dropout  with 2GPU

 

1. 데이터셋 

  • 대회에서 사용된 데이터셋은 ImageNet dataset에서 각 카테고리별 대략 1000장의 이미지를 이용하여 총 대략 1.2 million의 학습 이미지와 5만 장의 validation, 15만 장의 testset을 기준으로 한다 
  • 해당 데이터의 size는 256x256이다 
  • 해당 논문에서는 이미지에서 각 픽셀 별로 전체 픽셀의 평균값을 빼는 전처리를 제외하고는 다른 전처리를 하지 않았다 
  • 3 채널의 RGB 데이터를 사용하였다 

2. 아키텍처 

  • ReLu (Rectified Linear Unit : 정류 선형 유닛) : 이전의 모델의 경우 sigmoid나 tanh(x)를 사용하지만 이 모델에서는 ReLu를 사용하였다. 
    - 이런 대규모 데이터셋을 학습시킬 때는 빠른 학습 속도도 중요하다. ReLu 같은 Non-saturating non-linearities 함수가 기존의 saturating non-linearities 함수보다 빠른 학습 속도를 가진다. 
    - 여기서 Non-saturating Non-linearities 함수는 입력 X가 무한대로 갈 때 함수의 값이 무한대로 가는 것을 의미하고 
      saturating Non-linearities 함수는 입력 X가 무한대로 갈 때 함수의 값이 어떤 범위 내에서만 움직이는 것을 의미한다 
    - 추가적으로 ReLu는 위에서 설명했듯이 함수의 값이 어떤 범위 내에서만 움직이는 것이 아니기 때문에 Gradient Vanishing에 강하다. 이런 깊은 신경망을 학습시킬 때 좋다 
  • 병렬 GPU 사용 
    - 저자가 사용한 GPU는 GTX 580이고 3GB의 memory를 가지고 있다. 이런 대용량의 데이터를 하나의 GPU에서 학습시키기에는 너무 대용량이다 - 깊은 신경망을 학습시키기에 충분하다. 그렇기 때문에 병렬 GPU 처리를 사용하였다
    - 학습을 시작할 때 첫 convolution에서 kernel별로 절반씩 서로 다른 GPU에 할당하여 각각 다른 영역을 계산하고 특정 layer에서는 각 output을 합쳐서 계산을 진행한다.
  • LRN( Local Response Normalization )
    - ReLu의 특성상 입력에 대해 positive 한 input이 생성되지만 저자는 일반화에 더 집중하고 싶어 해당 기능을 추가하였다.
    - Kernel의 특정 좌표 x, y에 pixel이 주변의 값들보다 너무 크지 않게 정규화하는 작업이다. 모든 좌표에서 앞 2, 뒤 2 커널들에 해당 좌표의 값들의 제곱합을 이용하여 해당 좌표의 값을 주변보다 너무 튀지 않게 변경한다 
  • Overlapping Pooling 
    - 이전에는 stride와 kernel size를 같게 하여 겹치는 영역이 없이 pooling을 진행했지만 수많은 이미지와 ReLu에서 특정 영역에 치중한 학습(Overfit)할 수 있어 해당 기능을 사용한 것 같다 
    - 예 : 차량의 경우 밝은 헤드라이트만을 보고 그 이미지가 차량이라고 예측할 수 있지만 겹치는 영역을 사용하면 라이트와 주변 값들에 대해서 더 고려하여 예측할 것이다.
    Overlapping pooling
    LRN(Local Response Nomalization)
    Sigmoid tanh ReLu
    전체적인 네트워크 구조

첫번째 Conv의 Kernel의 가중치를 시각화한 모양


3. 오버 피팅 감소 

  • Data Augmentation 
    - 256x256 이미지를 224x224 이미지로 랜덤 하게 Crop 하여 데이터를 생성하고 수평 반전을 이용하였다 
    - 추론에서는 이미지에서 각 코너별로 4개, 중앙 1개의 patch를 생성한 후 수평 반전을 하여 총 10개 의 이미지를 예측한 후 output의 평균을 최종 예측으로 하였다 
  • Normalization 
    - 모든 트레이닝 셋에 대하여 각 픽셀 RGB에 대하여 PCA를 진행하여 고윳값, 고유 백터와 평균 0, 표준편차 0.1을 가지는 가우시안 분포를 따르는 랜덤 값을 각 픽셀에 더하여 Normalization 하였다 
    - p : 고유 벡터, λ : 고윳값, α : 가우시안 랜덤 값 
    각 픽셀별 Normalize 공식 - 모든 RGB에 해당 값을 합
  • Dropout - 해당 시대에는 최신 기법

4. 학습 파라미터 

  • SGD 사용 
  • batch size = 128
  • momentum = 0.9
  • weight decay = 0.0005
  • learning rate = 0.01 - validation error가 계속 변하지 않으면 10으로 나눔 - 실험 중 3번 변경됨 
  • epoch = 90
  • 2개의 GTX 580 3GB로 5~6일 걸림 

1. 개요

  • 대다수의 사람들이 딥러닝 공부를 시작할 때 강의를 듣거나 책을 통해 예제를 보고 학습을 한다. 
  • 대부분의 강의나 책에는 "파이썬 기초부터 시작하는 딥러닝"이라 제목을 짓는 경우가 많은데 이런 경우 아무래도 한정된 시간과 분량으로 교육생들에게 파이썬에 대해 많이 강의해 줄 수 없다 보니
    딥러닝 예제에서 미리 준비된 데이터 셋이나 tensorflow의 경우 tensorflow dataset을 이용하는 경우가 많다. 
  • 이런 미리 준비된 데이터셋을 이용하는 방식은 아무래도 학생이 딥러닝의 확장성, 구조설계의 자유도에 대해서 체감하기 어렵다는 것이다. 
  • 개인적인 생각으로는 딥러닝에서 가장 처음에 생각 해야하는 것은 인풋과 아웃풋이다. "내가 무엇을 넣어서 무슨 답을 원하는가"이다. 
  • 딥러닝 알고리즘을 블랙박스라고 생각하고 그저 우리가 원하는 것은 인풋을 넣었을 때 내가 원하는 아웃풋이 나오게 만들고 싶다. 
  • 그러기 위해서는 가장 중요한 것은 데이터셋에 대한 이해와 출력에 대한 이해가 먼저 되어야 한다. 

 

2. 데이터셋 

  • 데이터셋은 여러가지가 있다. 숫자 또는 카테고리가 혼합된 형태 예를 들면 남자, 여자, 키, 몸무게 등이 있다. 남자, 여자는 카테고리이고 키, 몸무게는 숫자 데이터이다. 
  • 이미지 데이터가 있다. 강아지, 고양이, 송아지 
  • 시간에 따라 연속해서 나오는 시계열 데이터가 있다. 한국의 기온 정보 1월에서 12월까지, 우리집의 전기 사용량
  • 문자열 데이터가 있다. "안녕하세요. 저는 HOE입니다."
데이터의 종류 카테고리 숫자 이미지 시계열 문자열
데이터의 예시 ο 남자, 여자
ο O, X 
ο 스웨터, 티셔츠, 모자, 신발
ο 몸무게
ο  키
ο 전기세
ο 상품의 가격
ο 강아지
ο 고양이
ο 송아지
ο 매일저장된 한국의 기온정보

ο 몇년간 기록된 우리집 전기세 사용량
ο 안녕하세요. 저는 HOE입니다.
ο 너 밥 먹었어?
ο 오늘 점심은 뭐먹지?
데이터 형식 남자=0,여자=1 or 남자=[1,0] 여자=[0,1]
O=0, X=1, or O=[1,0], X=[0,1]
스웨터 = 0
티셔츠 = 1
모자 = 2
신발 = 3
int
float
uint(자연수) int or float
+
Time
안녕 = 0
저는 = 1
HOE = 2
너 = 3
밥 = 4
먹었어 = 5
? = 6
오늘 = 7
점심 = 8
뭐 = 9

위 표를 보면 데이터가 어떻게 되어있는지 알았으니 이제 해당 데이터를 딥러닝에 넣으면 된다 

 

3. 딥러닝의 인풋, 아웃풋

예시를 보며 천천히 어떻게 데이터가 들어가는지 보자 

 

ㄱ. 사람의 나이, 키, 몸무게, 성별, 자산을 가지고 인기가 있나 없나를 구분하는 예시를 해보자.

일단 나이, 키, 몸무게, 성별, 자산이 어떤 데이터 형식인지를 파악한다 

나이 = 숫자

키 = 숫자

몸무게 = 숫자

성별 = 카테고리

자산 = 숫자 

여기서 첫번 째 조건 모든 입력값은 숫자형이어야 한다

유일하게 카테고리 형식인 성별을 숫자형으로 변경하자 남자=0, 여자=1 

그렇게 된다면 이제 딥러닝에 입력되는 데이터의 모양은 나이=20, 키=180, 몸무게=75, 성별=0, 자산=10 이 된다 

이제 입력 딥러닝 그래프를 그려보자 입력은 병렬이다!

이제 출력을 생각해보자 인기가 있다. 없다 는 카테고리형 데이터 타입이다. 이런 경우 출력으로 생각할 수 있는 방법은 2가지가 있다. 하나는 0~1로 생각하는 것이고 또 하나는 0 또는 1로 생각하는 것이다 

그럼 이제 코드를 작성할 때는 어떻게 해야 하나? 

일단 들어가는 입력 데이터는 병렬이라고 말했다. 그럼 데이터가 병렬이 되어 한번에 같이 들어가려면 어떻게 해야 하나?

데이터를 같이 묶으면 된다 보통 행렬로 제작을 한다 

저런 python list를 구현하는 것은 어렵지 않을 것이다. 

인풋이 결정됐으니 아웃풋을 만들어보자 

위의 두 가지 상황에 다음과 같이 1차원 또는 2차원 행렬 형태로 만들 수 있다.

끝났다 이제 모델의 입력과 출력에 해당 형식을 지정해주면 된다 

입력 = 형식-(5)   = [20,180,75,0,10]

출력 = 형식-(1) = [0] 또는 (2) = [1,0]

 

아마 딥러닝을 조금 공부한 사람이라면 배치 사이즈(batch size)를 들어보았을 것이다. 

위의 예시는 batch size가 1일 때의 예시이다 그럼 batch size가 2이면 어떻게 넣을까? 

입력 = 형식-(2,5) = [[20,180,75,0,10], [30,160,45,1,20]]

출력 = 형식-(2,1) = (2) = [0,1] 또는 (2,2) = [[1,0], [0,1]]

그럼 batch size가 2일 때 그래프는 어떻게 그려질까? 

 

'Machine Learning > AI를 위한 수학' 카테고리의 다른 글

(4) - Norm  (0) 2021.06.12

이번 장에서는 openCV에서 특정 색상을 추출할 때 사용되는 inRange를 이용해 차선을 추출하는 예제를 해보겠다 

이전 (9) setMouseCallback을 활용하여 색상의 값을 뽑고 해당 값과 비슷한 색상의 값들을 뽑아 보자

 

1. inRange

  • cv2.inRange(src, lower range, upper range, dst ) 
    - src : 목적이 되는 이미지 
    - lower range : 픽셀의 최솟값 ( 3채널이미지는 (x, x, x) 1 채널은 (x)  ) 
    - upper range : 픽셀의 최댓값
    lower range보다 크고 upper range보다 작은 값에 속하는 픽셀을 뽑아낸다 
import cv2 
import numpy as np 

origin_image = cv2.imread('inrange_image.jpg')

mask = cv2.inRange(origin_image, (0,100,0),(200,255,200))

range_image = cv2.bitwise_and(origin_image, origin_image, mask=mask)

mask = np.stack((mask,)*3, axis=-1)

image = np.concatenate((origin_image, mask, range_image), axis=1)
cv2.imshow('image', image)
cv2.waitKey(0)

 

  • 위의  inRange lower와 upper설정을 보면 값을 사전에 설정하여 입력하였다 
    하지만 위의 사진과 같이 특정 영역을 정확히 뽑아내지 못했다 . 
    inRange의 단점은 이것이다. 특정 객체를 뽑아내려면 해당 객체가 표현하는 색상을 알고 있어야 한다. 
    이럴 때 해당 객체의 색상을 알기 위해 이전 (9) setMouseCallback을 이용하여 색상을 알고 해당 색상에서 일정 범위 안에 해당하는 픽샐들을 뽑으면 객체를 뽑아내기 쉽다 

2. setMouseCallback을 이용한 inRange 

import cv2 
import numpy as np 

def get_RBG_in_image(event, x, y, flags, param):
    if event == cv2.EVENT_LBUTTONUP:
        print("마우스가 눌린 위치의 BGR값은 : {}".format(param['image'][y,x,:]))

    if event == cv2.EVENT_RBUTTONUP:
        print('마우스오른쪽 버튼이 눌린위치와 비슷한 색상을 가진 픽셀만 뽑기')
        threshold = 20
        value = param['image'][y,x,:]
        mask = cv2.inRange(param['image'],value - threshold, value + threshold )
        range_image = cv2.bitwise_and(param['image'], param['image'], mask=mask)
        cv2.imshow("range_image",range_image)
    return 


origin_image = cv2.imread('inrange_image.jpg')

param = {
    'image' : origin_image
}
cv2.imshow('image', origin_image)
cv2.setMouseCallback('image', get_RBG_in_image, param)
cv2.waitKey(0)

하얀색 방향선을 클릭했을 때 이미지
회색 도로를 클릭했을 떄 이미지

  • setMouseCallback을 설정하여 마우스 왼쪽을 클릭하면 해당 위치에 픽셀의 값을 print 한다 
  • 마우스 오른쪽을 클릭하면 해당 위치의 픽셀과 비슷한 (threashold를 설정) 값을 가지고 있는 픽셀만을 imshow 하였다 
  • 위와 같이 inRange를 이용하여 차량이 진행중에 차선을 검출할 수도 있다 - 조금만 응용하여 동영상에 접목하는 것을 각자 해보자!

이번 장에서는 openCV에서 나온 영상 또는 이미지에서 마우스를 이용하여 할 수 있는 작업에 대해서 알아보겠다

마우스를 이용하여 그림을 그리거나 특정 색상의 값을 얻을 수 있다 

 

1. 마우스 동작 

마우스의 동작의 종류는 3가지가 있다.  
마우스 오른쪽 버튼 동작, 왼쪽 버튼 동작, 가운데 버튼 동작 

이 3가지 동작마다 각각 눌렸을 때, 눌리고 올렸을 때, 더블클릭했을 때의 기능에 대해서 정의할 수 있다. 

  • EVENT_MOUSEMOVE : 마우스가 움직였을 때 
  • EVENT_LBUTTONDOWN : 마우스 왼쪽 버튼을 눌렀을 때 
  • EVENT_LBUTTONUP :  마우스 왼쪽 버튼을 올렸을 때 
  • EVENT_RBUTTONDOWN : 마우스 오른쪽 버튼을 눌렀을 때 
  • EVENT_RBUTTONUP : 마우스 오른쪽 버튼을 올렸을 때 
  • EVENT_MBUTTONDOWN : 마우스 가운데 버튼을 눌렀을 때 
  • EVENT_MBUTTONUP : 마우스 가운데 버튼을 올렸을 때 
  • EVENT_LBUTTONDBCLICK : 마우스 왼쪽 버튼을 두번 눌렀을 때
  • EVENT_RBUTTONDBCLICK : 마우스 오른쪽 버튼을 두번 눌렀을 때 
  • EVENT_MBUTTONDBCLICK : 마우스 가운데 버튼을 두분 눌렀을 때 

2. 마우스 콜백 함수의 파라미터 

def onMouse(event, x, y, flags, param):
    event = 위에서 정의한 마우스의 동작에 대한 감지 
    x, y = 해당 동작이 감지된 x, y 좌표값 ( 영상또는 이미지에 대한 )
    flags = 마우스 이벤트가 발생할 때의 특정 조건 (컨트롤, 쉬프트, 알트 등 키 조합 생성) 
    param = 파라미터로 전송될 값 (이미지 또는 특정 변수 전송)

 

import cv2 
import numpy as np 

class MouseGesture():
    def __init__(self) -> None:
        self.is_dragging = False 
        self.x0, self.y0, self.w0, self.h0 = -1,-1,-1,-1

    def on_mouse(self, event, x, y, flags, param):
        if event == cv2.EVENT_LBUTTONDOWN:
            print("왼쪽 버튼 눌림 \t좌표 : x : {} y : {}".format(x,y) )
        elif event == cv2.EVENT_LBUTTONUP:
            print("왼쪽 버튼 올림\t좌표 : x : {} y : {}".format(x,y) )
        elif event == cv2.EVENT_RBUTTONDOWN:
            print("오른쪽 버튼 눌림\t좌표 : x : {} y : {}".format(x,y) )
        elif event == cv2.EVENT_RBUTTONUP:
            print("오른쪽 버튼 올림\t좌표 : x : {} y : {}".format(x,y) )    
        elif event == cv2.EVENT_MBUTTONDOWN:
            print("가운데 버튼 내림\t좌표 : x : {} y : {}".format(x,y) )
        elif event == cv2.EVENT_MBUTTONUP:
            print("가운데 버튼 올림\t좌표 : x : {} y : {}".format(x,y) )
        # elif event == cv2.EVENT_MOUSEMOVE:
        #     # 마우스 움직임은 너무 많이 나와서 생략    
        #     print("마우스 움직임\t좌표 : x : {} y : {}".format(x,y) )
        elif event == cv2.EVENT_MOUSEHWHEEL:
            # 가로휠이 없는 마우스라 .... 
            print("마우스 가로 휠 \t좌표 : x : {} y : {}".format(x,y) )
        elif event == cv2.EVENT_MOUSEWHEEL:
            print("마우스 그냥 휠 \t좌표 : x : {} y : {}".format(x,y) )
            
        return 

image = cv2.imread('test.JPG')
window_name = 'mouse_callback'
mouse_class = MouseGesture()

cv2.imshow(window_name, image)
cv2.setMouseCallback(window_name, mouse_class.on_mouse, param=image)
cv2.waitKey(0)

3. 적용 예시 

  • 마우스를 클릭한 위치의 픽셀의 값 확인 
    import cv2 
    import numpy as np 
    
    class MouseGesture():
        def __init__(self) -> None:
            self.is_dragging = False 
            # 마우스 위치 값 임시 저장을 위한 변수 
            self.x0, self.y0, self.w0, self.h0 = -1,-1,-1,-1
    
        def on_mouse(self, event, x, y, flags, param):
            if event == cv2.EVENT_LBUTTONDOWN:
                value = param[y,x,:]
                print("왼쪽 버튼 눌림 \t x : {} y : {} 좌표의 픽셀값은 : {}".format(x,y, value) )
            return 
    
    image = cv2.imread('test.JPG')
    window_name = 'mouse_callback'
    mouse_class = MouseGesture()
    
    cv2.imshow(window_name, image)
    cv2.setMouseCallback(window_name, mouse_class.on_mouse, param=image)
    cv2.waitKey(0)​
  • 마우스로 그림 그리기 
    import cv2 
    import numpy as np 
    
    class MouseGesture():
        def __init__(self) -> None:
            self.is_dragging = False 
            # 마우스 위치 값 임시 저장을 위한 변수 
            self.x0, self.y0, self.w0, self.h0 = -1,-1,-1,-1
    
        def on_mouse(self, event, x, y, flags, param):
            if event == cv2.EVENT_LBUTTONDOWN:
                self.x0 = x
                self.y0 = y
                self.is_dragging = True
                print("사각형의 시작 좌표는 x : {} y : {}".format(x,y) )
            elif event == cv2.EVENT_LBUTTONUP:
                self.is_dragging = False
                cv2.rectangle(param['image'], (self.x0, self.y0), (x,y),(0,0,255),2)            
                cv2.imshow(param['window_name'], param['image'])
                print("사각형의 좌표는 ({}, {}), ({}, {})".format(self.x0,self.y0,x,y) )
            elif event == cv2.EVENT_MOUSEMOVE:
                if self.is_dragging:
                    temp_img = param['image'].copy()
                    cv2.rectangle(temp_img, (self.x0, self.y0), (x,y),(0,0,255),2)            
                    cv2.imshow(param['window_name'], temp_img)
            return 
    
    
    image = cv2.imread('test.JPG')
    window_name = 'mouse_callback'
    mouse_class = MouseGesture()
    param = {
        "image" : image,
        "window_name" : window_name
    }
    cv2.imshow(window_name, image)
    cv2.setMouseCallback(window_name, mouse_class.on_mouse, param=param)
    cv2.waitKey(0)​
  •  

+ Recent posts