exim 의 transport, router 를 이용한 스팸 필터링

익숙한 걸 사용하려다보니 procmail 을 이용해서 bogofilter 를 수행하는 방법을 사용해 왔지만, procmail 은 로컬 유져에 한해서 실행되게 되므로, alias 나 .forward 를 사용하게 되는 경우 스팸 필터링을 하지 않게 된다.
하여튼 이게 좀 신경쓰여서 transport 와 router 를 이용해서 bogofilter 를 수행하도록 설정해봤다.
우선 transport 를 다음과 같이 설정해보자.
bogofilter:
driver = pipe
command = /usr/sbin/exim -oMr bogodone -bS
use_bsmtp = true
transport_filter = /usr/bin/bogofilter -d /etc/bogofilter/ -e -p
log_output = true
return_path_add = false
temp_errors = *
home_directory = "/tmp"
current_directory = "/tmp"
message_prefix = ""
message_suffix = ""

그리고 이 transport 를 이용하는 router 를 만든다. 참고로 router 는 순서에 민감하므로 삽입할 위치를 잘 조절해야 한다. 나같은 경우는 system_alias 다음에 선언해두었다. (alias 를 사용하는 주소들중 로컬 유져에게 전달되지 않는 건 mailman 과 관련된 것들 밖에 없는데 이거야 뭐 어짜피 인증된 사용자가 보낸 메일만 받으니 상관 없겠다는 마음으로…-_-;; )
bogofilter:
domains = +local_domains
no_verify
condition = ${if !eq {$received_protocol}{bogodone} {1}{0}}
driver = accept
transport = bogofilter

여기까지만 하게 되면 bogofilter 를 수행하기는 하지만 이를 이용해서 메일을 옮긴다거나 하는 동작은 하지 않게 된다. 그러므로 이런 동작을 시키기 위한 transport 와 router 를 또 추가해주자.
역시나 transport 먼저…
spam_delivery:
driver = appendfile
directory = /home/$local_part/.maildir/.Spam
maildir_format
delivery_date_add
envelope_to_add
return_path_add

이렇게 하면 자신의 홈 디렉토리의 .maildir 아래 .Spam 이란 디렉토리를 만들고, 그 디렉토리에 스팸 메일을 저장하게 된다. IMAP 으로 접속하면 Spam 메일들을 확인할 수 있기 때문에 이렇게 했는데, POP3 만 사용하는 거라면 그냥 제목에 [Spam] prefix 를 붙이게 하는 것도 나쁘지 않을 듯…
그 다음엔 이 transport 를 이용하는 router! 역시나 어디다 위치시킬지 잘 생각해야 한다. 나같은 경우엔 bogofilter router 바로 아래에 이걸 위치시켜놓았다.
removingspam:
driver = accept
check_local_user
condition = ${if match {$h_X-Bogosity:} {Spam, tests=bogofilter} {1}{0}}
transport = spam_delivery

스팸 메일은 bogofilter 에 의해 X-Bogosity: Spam, test=bogofilter … 식의 헤더가 추가되기 때문에 이렇게 할 경우 스팸을 쉽게 분류해낼 수 있다.
잘 됐는지 확인은 메일로그를 이용해서 확인하면 된다. 나같은 경우는 metalog 를 사용하니 /var/log/mail/current 를 이용해서 확인해야 했는데 대부분의 경우 syslogd 를 사용할테니 /var/log/message 를 확인하면 될 것 같다.

$ # tail -f /var/log/mail/current |grep R=
Feb 26 16:48:31 [exim] 2008-02-26 16:48:31 1JRkT5-0001pz-Nx => 메일주소 R=procmail T=procmail
Feb 26 16:48:35 [exim] 2008-02-26 16:48:35 1JTuY0-00081q-7u => 메일주소 R=removingspam T=spam_delivery
Feb 26 16:48:44 [exim] 2008-02-26 16:48:44 1JQWAs-0002po-CR => 메일주소 R=bogofilter T=bogofilter
Feb 26 16:48:45 [exim] 2008-02-26 16:48:45 1JTuY5-000826-EK => 메일주소 R=removingspam T=spam_delivery

유심해서 봐야할건 R=, T= 다음에 나오는 것들이다. R= 다음에 나오는 것은 사용된 router 를 의미하고, T= 다음에 나오는건 transport 를 의미한다. 위의 로그를 보면 bogofilter 를 수행한 뒤 removingspam router 를 이용해서 spam_delivery trasport 가 수행되기도 하고 혹은 이를 통과해서 procmail transport 가 수행되기도 하는 걸 확인할 수 있다.
기본으로는 procmail transport 가 없으니 원랜 local_delivery 가 나올 수도 있겠고 뭐 하여튼 router 나 transport 이름은 사용자가 맘대로 지으면 되는거라 상황에 따라 다 다를 듯…
스팸 없는 세상이 올 때까지 ㅠ.ㅠ 오늘도 삽질…

요 며칠 삽질기 -_-! with Exim

어째 요새 관리해야할 서버가 늘어버렸네요. (전 언픽스 하나로 족한데 ㅠ.ㅠ) 하여튼!! 요 며칠 사이 gentoo + exim + procmail + spf + srs + clamav + bogofilter + dovecot 를 시도해봤습니다.
사실 계속 제가 맡아서 할 게 아니라 길어야 일 년 정도 만져줄 서버기 때문에 젠투가 아닌 다른 배포판을 생각했었는데, spf 와 srs 를 지원할 수 있도록 하면서 기본으로 제공되는 패키지를 이용할 수 있는 조합이 몇 가지 되질 않더군요. exim 에서 spf 와 srs 는 experimental 로 되어 있기 때문에 바이너리 배포판에선 기본으로 적용이 되어 있질 않고, postfix + milter 조합에서는 srs 를 제공할 수 없기 때문에 남은 선택은 sendmail + milter 조합 밖에 없는데 sendmail 을 사용하기는 싫었거든요.
하여튼! exim 에 procmail 을 붙이는 방법은 아래와 같습니다.
procmail:
driver = accept
transport = procmail
domains = +local_domains
check_local_user
# emulate OR with "if exists"-expansion
require_files = /usr/bin/procmail
no_verify
no_expn

router 파트의 localuser: 룰 앞에 위와 같은 코드를 추가하고
procmail:
driver = pipe
path = "/bin:/usr/bin:/usr/local/bin"
command = "/usr/bin/procmail"
return_path_add
delivery_date_add
envelope_to_add

transpotes 에 router 에서 정의한 procmail transpote 를 정의해 주면 되죠. procmail 을 이용한 스팸 필터야 예전부터 잘 사용해오던 게 있으니 그걸 사용하면 됐구요.
그 다음은 smtp 설정!! 기본으로 /etc/exim/auth_conf.sub 란 파일이 있길래 그냥 자동으로 smtp 인증 설정이 되어있는줄 알았는데 smtp 인증을 사용하려면 저 안에 있는 내용을 /etc/exim/exim.conf 에 추가해줘야합니다.
plain:
driver = plaintext
public_name = PLAIN
server_condition = "${if pam{$2:$3}{1}{0}}"
 
login:
driver = plaintext
public_name = LOGIN
server_prompts = "Username:: : Password::"
server_condition = "${if pam{$1:${sg{$2}{:}{::}}}{1}{0}}"

그 내용은 위와 같았구요. 하지만 여기서 또 문제가 발생! 예전 시스템이 오래 전에 설치된 것이었는지 비밀번호 암호화를 md5 로 사용하질 않고 있었는데, /etc/pam.d/system-auth 에서 md5 를 제거함으로써 다른 데몬들에선 문제가 해결됐지만 exim 의 stmp 에서만 인증 실패가 나오는겁니다. -_-!
참고로 smtp auth 가 어떤 과정으로 이뤄지는지는 아래 인용된 내용을 참고하세요. 테스트할 때 알아두면 편합니다.

$ telnet localhost 25
EHLO localhost
AUTH LOGIN
base64로인코딩한아이디
base64로인코딩한비밀번호

여담이지만 smtps 나 pop3, imaps 를 꼭 사용하세요. 위에서 처럼 비밀번호가 그냥 넘어갑니다. 패킷이 악의적인 라우터를 지날 경우 아이디/비밀번호가 줄줄 셀 수 있습니다.
하여튼 비굴하게 saslauthd 를 사용해서 우회시켜봤지만 요놈도 마찬가지로 인증 실패 -_-! 아예 다 안되면 모르겠는데 우리 사랑스런 dovecot 은 로그인이 아주! 잘 됩니다. exim 만 나쁜 놈이란 거죠.
saslauthd 라거나 pwcheck 같이 다른 곳에서 잘 사용도 안되는 데몬을 띄우는 게 좀 꺼림찍했는데 잘됐다 싶어서 dovecot-auth 를 활용해봤습니다. dovecot-auth 를 활용하려면 아래와 같이 dovecot.conf 파일을 수정해줘야 합니다.
auth default {
mechanisms = plain login
passdb pam {
args = "*"
}
userdb passwd {
}
user = root
 
socket listen {
client {
path = /var/run/dovecot/auth-client
mode = 0666
}
}
}

그리고 위에 써있는 auth-client 소켓을 이용해서 로그인하도록 exim.conf 를 고쳐줍시다.
login:
driver = dovecot
public_name = LOGIN
server_socket = /var/run/dovecot/auth-client

이제 끝!! 드디어 smtp 인증이 정상적으로 되기 시작합니다. 휴;;
뭐 대강 다 됐는데, 한 가지 아쉬운 점이라면 .forward 나 alias 를 이용할 경우 procmail 을 거치지 않기 떄문에 스팸 필터링이 안되는 것 정도? 이건 어떻게 해야할 지 별로 답이 안나오네요.
그렇다고 bogofilter, clamav 등이 다 procmail 에서 호출되는데 이걸 spamassassin + clamd 조합으로 새로 설정하긴 완전 귀찮고 (사실 어떻게 하는지도 모르고) 약간 쀍입니다.
그 외에 mailman 과 관련된 삽질이 조금 더 있었고, seliunx 가 적용되어 있는 CentOS 랑 관련된 것도 있었는데 또 언제 시간날 때 관련된 것들을 포스팅해봐야겠습니다.
사실 요새 포스팅 하고 싶은 내용은 많은데 내용이 길어져서 귀차니즘이 발동해버렸네요.

exim 에 spf, srs 적용하기

unfix 서버를 구축할 땐 postfix 를 사용했지만 postfix 에선 srs 를 사용할 수가 없었기 때문에 새로 설치하게 된 서버들에선 exim 을 시도 중이다.
그런데 exim 을 사용하려고 하니 문제가 하나 있는데, 구글에서 검색을 해도 원하는 결과가 잘 나오질 않는다는 점이 바로 그것! exim 에는 spf 와 srs 등을 사용하기 위해서는 따로 패치가 필요 없이 그저 EXPERIMENTAL_SPF=yes EXPERIMENTAL_SRS=yes 이 두 가지 환경변수를 세팅한 채로 빌드를 진행하면 된다.
그 이후 어떻게 해야하는 지에 대한 설명이 없어서 한참 헤맸는데 exim 과 함께 설치되는 문서를 확인해야하는 것이었다. -_-; 보면 domain key 등도 위에서와 같이 환경변수만 세팅해주면 사용이 가능해진다.
doc/exim-4.68/experimental-spec.txt.bz2
문서를 확인해보니 이제 해야할 일은 spf 와 srs 설정을 추가해주는 것 뿐 후훗!
SPF 는 global 섹션에서 acl_smtp_rcpt 로 등록되어 있는 것에 다음과 같은 설정을 넣어주면 된다.
# Query the SPF information for the sender address domain, if any,
# to see if the sending host is authorized to deliver its mail.
# If not, reject the mail.
#
deny
message = [SPF] $sender_host_address is not allowed to send mail \
from $sender_address_domain
log_message = SPF check failed.
spf = fail
 
# Add a SPF-Received: header to the message
warn
message = $spf_received

젠투에선 acl_smtp_rcpt = acl_check_rcpt 와 같이 되어 있기 떄문에 acl_check_rcpt 에 위와같은 설정을 추가해줬다. 만약 아래 warn message = $spf_received 를 넣어주지 않으면 spf 결과를 헤더에 추가해주지 않게된다. 생색을 내기 위해선 뭔가 출력될 필요가 있으므로 -_-! 난 헤더를 추가하도록 설정해줬다.
하지만 위에 대로만 할 경우 내가 메일을 보낼 경우에도 SPF-Received 헤더가 붙는 문제가 생긴다. smtp auth 를 설정해놓은 상황에서 이 서버를 통해 메일을 보낼려면 인증이 되어있어야 할테니 아래와 같이 수정해서 이런 문제를 피해주자.

# Add a SPF-Received: header to the message
warn
message = $spf_received
!authenticated = *

그리고 그 담은 SRS 복잡하게 하려면 복잡하겠지만 다 귀찮아서 hash 용 secretkey 하나만 넣어서 설정해버렸다. 아래 설정은 global 섹션에 들어가면 된다. (domainlist 같은게 들어가는 곳) 실제로 somesecretkey 를 적어주는건아니고 그냥 원하는 단어 하나를 적어주면 된다.
srs_config = somesecretkey
그 다음엔 userforward 트랜스포트에 srs 를 적용하도록 해주자 srs = forward 만이 내가 추가해준 부분;;
userforward:
driver = redirect
srs = forward
check_local_user
# local_part_suffix = +* : -*
# local_part_suffix_optional
file = $home/.forward
# allow_filter
no_verify
no_expn
check_ancestor
file_transport = address_file
pipe_transport = address_pipe
reply_transport = address_reply

이제 끝!!!
얼마 받지도 못하는 웍스테이션 조굔지 뭔지 때문에 지난 며칠간 고생한걸 생각하면 흑흑 ㅠ.ㅠ
p.s) exim 에 spf 적용하기와 관련해서 따라하기 편한 문서가 있는데 페이지 랭크가 낮은거 같아서 링크
http://slett.net/spam-filtering-for-mx/exim-spf.html

Math: line fitting

만약 서로 다른 2개의 (x,y) 쌍을 가지고 있다면 아래와 같은 직선의 방정식을 계산해낼 수 있습니다.

이를 아래와 같은 매트릭스 형태로 표현할 수도 있습니다.

그런데 (x,y) 값을 2 쌍보다 더 많이 알고 있다면, 해가 구해낼 수 없게됩니다. 이를 over constraint 라고 하며, over constraint 상태에서 가장 에러가 작은 직선의 방정식을 구해내는 것을 line fitting 이라고 부릅니다.

line fitting 을 하는 방법은 크게 2가지 방법이 있습니다. 첫번째는 에러를 최소화 하는 계수 a, b 를 찾기 위해 편미분을 이용하는 것이고, 두번째로는 pseudo inverse 를 이용하는 방법입니다.

line fitting with differencial equation

편미분을 통해 최적의 직선을 찾아내기 위해 우선 Error 를 아래와 같이 정의해 봅시다.

2차 곡선의 최소값은 기울기가 0이 되는 지점에 있으므로 a, b 각각에 대해 편미분을 한 뒤 기울기가 0 이 되는 지점을 찾습니다.

위 식을 정리하면 아래와 같은 매트릭스로 표현이 가능하며,

역함수를 양 변에 곱해주게 되면 간단히 계수 a, b 를 구할 수 있게 됩니다.

line fitting with pseudo inverse

지금까지 편미분을 이용한 line fitting 을 알아봤는데, 이 경우는 손으로 계산해야하는 것들이 많았지만 pseudo inverse 를 이용하면 계산을 모두 컴퓨터에게 맡길 수 있기 때문에 훨씬 쉽게 직선의 방정식을 구할 수 있습니다.

pseudo inverse 를 이용한 방법을 알아보기 앞서 위 매트릭스를 간단히 아래와 같이 표현하기로 하겠습니다.

X 에 대한 inverse 를 계산할 수 있다면 간단하게 계수 a, b 를 구할 수 있겠지만 불행히도 X 는 inverse 를 가지지 못하기 때문에 X 에 자신의 transpose 를 곱해준 뒤 역행렬을 구하는 방법을 사용하게 되며, 이런 식으로 역행렬을 구해내는 방법을 pseudo inverse 라고 합니다.

식을 정리하고 나니 아래와 같은 간단한(?) 행렬 연산을 통해 계수 a, b 를 계산해낼 수 있겠습니다.

Result

아래 그래프는 (1.1,0.7), (2.1,1.0), (4.3,3.2), (-1.2,-1.1), (-2.4,-2.1), (-3.5,-3.4) 이렇게 6개의 점에 대한 line fitting 결과를 gnuplot 을 이용해서 그래프로 만든 것이며, 6개의 점 사이를 지나는 직선이 구해진 것을 쉽게 확인할 수 있겠습니다.

p.s) 매트릭스 관련된 오퍼레이션들을 다 짜놨더니 이거이거 이런 간단한 것들 돌려보는건 일도 아니네요. 요 근래 뭔가 조급해하고 있었는데, 맘잡고 기초부터 탄탄히 해놓는게 나을 거 같다는 생각이 들어서. 욕심을 줄이고 있습니다. 후훗

코드: https://github.com/Tee0125/linear_regression/blob/master/numpy/numpy_linear_regression.py

CG: 3D Image Rotation

며칠 전 Perspective Projection 을 정리해놓은 김에 3D Image Rotation 도 정리를 해볼까 싶습니다.

Rotation Matrix

3D 이미지 회전은 아래와 같은 행렬을 통해 새로운 좌표를 계산할 수 있습니다. 또한 이 행렬들은 모두 unitary matrix 이기 때문에 Transpose 를 취해줌으로 역행렬을 쉽게 구할 수 있습니다.

z축 기준: (xy 평면에서의 회전)

y축 기준: (zx 평면에서의 회전)

x축 기준: (yz 평면에서의 회전)

Implementation of Image Rotation

처음에는 3차원 공간을 3차원 배열을 사용하여 모델링한 뒤 실제 3차원 좌표를 모두 뒤지며 forward/backward mapping 하는 방법을 통해 3D image rotation 을 구현해보았습니다. 3차원 배열을 이용 512×512 사이즈의 lena image 를 회전시키려면 (512*1.414)^3 만큼의 공간이 필요하게 되고, 저 공간을 모두 뒤지려면 계산 복잡도가 엄청나더군요.

실제 이 방법을 통해 이미지를 회전 시키는 데 ‘분’ 단위 시간이 필요했던 것으로 기억합니다. 게다가 변환을 반복할 수록 이미지의 디그라데이션이 심해졌기 때문에 이건 아니라는 생각이 들더군요. 이런 경우 이미 잘 설계되어 있는 그래픽 라이브러리를 참고하는 것이 좋기 때문에 OpenGL 의 인터페이스를 살펴보며 어떤 식으로 구현하면 좋을 지 생각하기 시작했습니다.

뭐 어짜피 화면이나 이미지로 보여주기 위해선 2D 평면에 projection 하는 것이 필요하므로 매 번 이미지 자체를 돌리기 보다 축을 회전시키고, 마지막에 그 축을 이용해서 원래 이미지를 새로운 좌표 공간으로 매핑시켜주면 되겠다는 결론을 얻었습니다.
우선 x, y, z 좌표를 identity matrix 로 표현한 뒤 R^T * AXIS 를 통해 새로운 축 AXIS’ 를 구할 수 있고, 회전을 시키고 싶은 만큼 위 연산을 반복해준 뒤 forward mapping 을 해주는 것으로 빠르고 훌륭한 품질을 보여주도록 구현하는걸 성공했습니다.

위 이미지는 512×512 사이즈의 lena 이미지를 z축을 기준으로 45도만큼 회전시킨 결과입니다. 왼 쪽은 단순히 forward mapping 을 해준 것이고 오른쪽은 weighted sum 을 이용해서 forward mapping 을 개선해준 것입니다.

결과적으로 Photoshop 등을 이용한 만큼 훌륭한 이미지를 얻어낼 수 있는 것을 확인할 수 있습니다.

Other Results

아래 이미지는 x 축, y축, z축을 기준으로 순서대로 30도씩 회전시킨 이미지입니다. 이런 식으로 계산을 하려면 순서를 뒤집어서 z축, y축, x 축 기준으로 30도씩 회전을 시켜주면 됩니다.

다음은 x축으로 30도, y축으로 60도 만큼 돌린 결과

실제 구현 코드에 관심이 있으신 분들은 아래 링크를 방문하시면 되겠습니다. 이런 걸 하나하나 구현해볼 때마다 느끼는 거지만 openGL 같은 라이브러리를 설계하신 분들은 상상하기 힘들 정도로 똑똑한 것 같아요.

소스:
https://github.com/Tee0125/snippet/tree/master/rotation3d

CG: Perspective Projection

HCI 과제 덕에 심심찮게 프로그래밍을 하게 되네요. 첫 과제 였던 3D rotation 관련을 구현하는 것도 상당히 흥미로웠지만, 두번째 과제인 Perspective Projection 를 구현하는 것은 정말 멋진 경험이었다고 생각합니다.

지난 며칠간 꽤나 재밌게 프로그래밍을 했던 관계로 블로그에도 살짝 정리해보는게 어떨까 하는 생각이 들었는데, 막상 쓸려니 내용이 잘 전해질지 의문이네요.

What is the Perspective Projection?

Perspective Projection 이란 아래의 왼쪽 이미지를 오른쪽 이미지 처럼 변화시키는 것을 얘기합니다. 꼭 저렇게 비뚜러진 이미지를 바로잡는것은 아니고, 이미지가 투영되는 면을 변화시키는 것이라고 생각하시면 됩니다.

이해를 돕기 위해 wikipedia 에서 이미지를 하나 가져왔습니다. 아래 이미지의 연보라색 면이 상이 맺히는 곳이라고 할 때, perspective transform 은 그 보라색 면을 이동시킨 것 같은 효과를 주기 위해 사용합니다.

How to get a projection matrix.

기본 적으로 Perspective Transform 을 위한 식은 다음과 같습니다.

homogenious coordinate 를 사용하고 있으니 x’ 와 y’ 에 관한 식은 아래와 같이 바꿔쓸 수 있습니다.

이를 정리하면 다음과 같은 꼴로 만들 수 있고,

우리가 값을 알고 싶은 변수들은 a, b, c, d, e, f, g, h 이렇게 8 개이므로, (x, y) 와 그에 대응되는 (x’, y’) 쌍을 4개만 알고 있으면 projection matrix 를 구할 수 있습니다. 이를 구하기 위한 매트릭스는 아래와 같습니다.

남은 건 8×8 matrix 의 inverse matrix 를 구한 뒤 뒤 쪽의 매트릭스에 곱해주는 것 뿐이군요.

Implementation of Perspective projection

이제까지 Perspective Transform 을 위한 매트릭스에 대해 알아봤습니다. 이제는 실제 구현을 해보는 것만 남았네요. 위에서 알아봤듯이 Perspective matrix 를 구하려면 matrix multiplication 과 inverse 를 위한 인터페이스가 필요합니다.

matrix multiplication 의 경우 서로 곱할 수 있는 형식인지를 체크한 뒤 단순한 계산을 하면 되고, inverse 는 gauss elimination 을 이용 reduced row echelon form 으로 만들어주는 것을 통해 쉽게(?) 구해낼 수 있습니다.

위의 두 가지까지 구현했다면, 이제 warping 만을 구현하면 되겠습니다. 이 warping 은 크게 두가지 방법을 통해 구현할 수 있습니다.

forward mapping

forward mapping 은 말 그대로 src 의 x, y 좌표에 대하 dst 의 x’, y’ 를 계산 한 뒤 값을 채워주는 방식입니다. 간단히 pseudo code 로 표현하면 다음과 같이 표현할 수 있겠네요.

근데 막상 구현을 해놓고 보면 pixel 이 정수단위이기 때문에 아래와 같이 hole 이 발생하는 것을 확인할 수 있습니다.

backward mapping

위에서 얘기한 hole 을 방지하기 위한 방법 중 하나로 backward warping 이란 것이 있습니다. forward warping 에서 src 의 좌표를 기준으로 dst 의 좌표를 계산했다면, backward warping 에서는 dst 의 좌표를 기준으로 src 의 좌표를 계산하게 됩니다.
간단하게 pseudo code 로 표현하면 아래와 같이 되겠습니다.

간단히 코드만 봐도 예상할 수 있겠지만 backward_warping 을 해주게 되면 hole 은 확실하게 없앨 수 있습니다. 결과 이미지는 아래와 같은데, 아주 깔끔한 결과가 나오지는 않았습니다.

forward (or backward) warping with interpolation

forward warping 을 하게 되면 hole 이 생기게 되고, 단순한 backward warping 을 하게 되면 이미지의 화질 저하가 발생하게 되는데, interpolation 을 사용하게 되면 이를 조금 더 개선할 수 있습니다.

전 linear-interpolation 을 사용해보았는데, 설명하기는 복잡하니 관심있으신 분은 저 아래 첨부할 소스를 참고해보시면 좋겠습니다. 결과는 아래와 같이 나옵니다.

우선 interpolation 을 이용한 forward warping 입니다. 복잡하게 하기는 귀찮고 해서 대강 구현했더니, hole 이 줄기는 했지만 여전히 존재하고 있습니다.

다음은 backward warping 에 linear interpolation 을 적용한 결과입니다. hole 도 없고, 보기에 상당히 괜찮아진 것을 확인할 수 있습니다.

소스코드:
https://github.com/Tee0125/snippet/tree/master/perspective_projection
참고자료:
http://en.wikipedia.org/wiki/Perspective_%28graphical%29
http://en.wikipedia.org/wiki/Gaussian_elimination
p.s) 부동 소숫점 연산에서 x – x/x*x = 0 이라는 것이 보장되질 않더군요. 코드 한 줄 줄일려다가 디버깅을 30분동안 해야했습니다. -_ㅜ

AVR

실험(4) 과목 때문에 스트레스가 여간이 아닙니다. 중간고사 평균이 8점이었고, 중간 + 기말 + Report 점수를 합산한게 70점을 넘지 않으면 F 를 주시겠다는군요. 문제는 이 과목이 전공 필수이고, 전 요번학기가 마지막이어야만 한다는 거죠.
하여튼 그런 이유로 추가 점수를 받기 위해 오랫만에 AVR 을 만져야 했습니다. AVR 은 ATMEL 에서 만든 MCU 로 ISP 인터페이스를 갖추고 있어서 복잡한 롬라이터를 만들지 않고도 간단하게 프로그램을 구울 수 있습니다.
빵판에 구성한 AVR ISP
실험(4) 시간에 atmega128 과 관련 보드 / ISP 가 지급이 되었지만, 뗌질을 하다가 칩을 태워먹은건지 아님 어딘가 뗌질이 잘못된건지 하여튼 작동하지를 않더군요. 어쩔 수 없이 예전에 사두었던 AT90S2313 을 이용하기로 마음을 먹고 위의 사진처럼 빵판에 회로를 구성했습니다. 보기엔 좀 그렇지만 하여튼 작동은 잘 하는군요. 후훗
AVR 을 이용한 자판기 에뮬레이터
결국 두 시간동안 열혈 뗌질을 한 끝에 위와 같은 최종 결과물을 만들어냈습니다. I/O 가 모잘라서 500원 투입 버튼은 만들지 않았지만, 그걸 제외하면 구현해야 하는 스펙은 모조리 구현해놓았습니다.
모처럼 오랫만에 mcu 를 가지고 놀았더니 재밌네요. 필받은 김에 아래와 같은걸 추가로 뗌질해봤습니다.
이 회로는 뭘까요~?
예전에 홍익대학교 전자전기공학부 학회인 유레카에서 유행했던 겁니다 😉 축제 때 팔기도 했던걸로 알고 있는데… 뭐 하여튼 L.E.D 에 저항연결하기도 귀찮고, 전원을 따로 입력받기도 귀찮고 해서 3V 로 동작하게 만들어버렸습니다.
아래 사진은 밤에 불을 다 꺼놓고 노출을 1s 로 해놓은 뒤 위 회로를 살짝 흔들어준 모습입니다. 메시지를 길게 넣을 수도 있지만 그 노가다가 귀찮아서 -_-;
잔상으로 그려진 스마일 마크
위 회로를 업그레이드 해서 ^_^, -_-, T.T, 하트 등을 그릴 수 있도록 업데이트 했습니다. 결과물을 보시려면 아래 url 로 😉
http://mytears.org/tmp/dir/?path=./avr&N
p.s) 참고로 AVR 을 굽기 위해선 MISO / MOSI / SCK / RESET (ISP 에서 입력을 받습니다.), VCC, GND 그리고 XTAL1, XTAL2 (크리스탈) 정도만 연결해주면 됩니다. 예전에 VCC, GND 등은 패러랠 포트에서 들어오는 건 줄 알고 삽질한 기억이 나서;;;

수업은 '무언가를 배우는 과정' 입니다…

수업은 ‘무언가를 배우는 과정’ 이지 ‘무언가를 달성하는 과정’이 아닙니다.
어떠한 제약조건을 주는 것은 제약조건에 맞는 획일화된 결과물을 받고자 함이 아니라 그 제약조건에 익숙해짐에 의해서 다른 것을 배우게 하기 위한 과정입니다. 예를 들어, 지금 하는 실험들은 남들이 이미 다 해서 결과를 뽑아 놓은 것들입니다. 그에 대하여 공부하면 되는 것인데 구태여 실험을 하는 이유는 무엇인지도 생각해 볼 수가 있겠죠. 그렇지만 왜 그런 삽질을 하느냐고 묻는다면 그 과정을 거치지 않고서는 그 다음단계로 나아갈 수 없다는 것이 대답이 될 것 같습니다.
(중간 생략)
문서의 형식이 중요한 것이 아니라 문서를 만들면서 얼마나 배우는가가 중요합니다. 사실 계속 hwp로 문서를 써도 별다른 상관은 없다고 할 수도 있겠지만, 그 과정에서 많은 것을 놓치는 것이 아닐까 저어하여 답변 길게 남겨봅니다.

언제나 학기가 끝나고 나면 … 아니라고 하면서도 결과에만 연연해 온게 아닐까 싶다… 족보를 구하려고 애쓰고… 족보에 나온 문제 유형을 파악하려 애쓰는 행위 등을 통해 ‘좋은 학점’ 을 달성하려한 거 같다…
예전에 기대했던 대학 졸업반의 모습에 미치지 못했지만… 나름대로 4년동안 참 많은 걸 배웠다는 점을 생각해보면… 좋은 학점을 달성해내지 못했다는 점에 그렇게 죄책감을 느끼지 않아도 되지 않을까 하는 생각이 들었다…
… 가끔씩 아는 사람들 블로그에 걸려있는 링크를 따라 멀리멀리 흘러갈 때가 있다… 거의 맨날 가는 사이트 몇 군데만 계속해서 돌기 때문에 자주 있는 일은 아니지만… 오랫만에 서핑을 하다… 상당히 맘에드는 글귀가 보이는 포스트를 발견해서 가볍게 스크랩성으로… 🙂

언제나 내 성적은 그저 그렇다…

뭐 딱히 성적에 목숨걸지 않는 내 성격 탓이기도 했지만… 요번 학기는 이상하게 아쉬움이 많은 거 같다… 교양 과목은 언제나 내 발목을 잡고… 전공 과목은 재밌게 들었고, 제대로 이해했음에도 결과는 그다지…
50~60% 정도까지는 B를 준다는 것을 볼 때, 내가 겨우 그 정도 밖에 안되나 싶은 생각이 와르르 밀려온다.
언제나 중간고사까지는 상위 클래스에 있다가 기말고사에서 점수를 다 까먹는다. 요번엔 시험 5과목이 이틀에 걸쳐 있었으니 더 힘들었고 (전날 저녁에 한 과목을 시험보고 나서 담 날 아침부터 밤까지 세 과목을 하루에 보는 그런 일정이었으니)
오목 프로그램은 마지막 날까지 손에 잡히질 않아서 오목 프로그램 마무리를 하느라 전자기학은 공부도 별로 못해버렸고 결국 망했다…-_- 뭐 지금 이런 얘기해봐야 핑계 밖에 안되겠지만…
하튼 언제나 내 성적은 3점 초반대가 나오는 듯… 교양이야 그렇다 치지만 전공은 재미가 없었던 것도 아니고 수업을 못따라갔던 것도 아닌데다가… 지금도 배운 내용에 대해 자신있게 대답할 수 있는데… 성적은 안나오니 뭐가 문젠지도 모르겠다. 나보다 성적이 잘 나온 사람들이 나보다 그 과목에 대해 더 많이 이해했다고는 생각치 않는데…
성적이 어떻게 나왔느냐 보다 … ‘내가 얼마나 배웠느냐’ 와 ‘얼마나 많이 내 것으로 만들었느냐’ 가 중요하다는 생각은 아직 변함이 없지만… 뭐 하튼 아쉬운 건 사실인 듯