직접 따라해보면서 만들어 봅시다.
※ 이전글(https://brunch.co.kr/@llun/46)에서 이어집니다.
주변에서 프로그래밍을 배워보고 싶다는 분들이 많은데
이런분들께
'어떤 언어를 배우고 싶으세요?'라든지 '백엔드에 관심이 있나요, 아니면 프론트엔드?'를 묻는 것은 어불성설이고,
가볍게
프로그래밍을 배워서 '무얼 만들어보고 싶은지' 물어봤을 때조차 답을 하시는 분들은 드물었던 듯합니다.
사실 이런 경우라면 동기부여가 이뤄지기 쉽지 않고
프로그래밍을 익힌다는 개념이 아니라 코딩 공부가 되는 순간
독서실에서 책을 쌓아놓고 하는 입시공부나 공무원시험, 자격시험을 준비하는 기분이 들 거예요.
최근 '코딩 입문' 서적을 몇 권 살펴보니 더더욱 그렇더라고요.
(저라도 이렇게 시작하면 금방 흥미를 잃어버리겠다 싶은)
교수-학습이론에도 있지만 학습자에게 중요한 것 중 하나가 관련성이거든요.
나와 관련이 있는 내용이어야 더 잘 이해되고 기억에 남고 그렇습니다.
그런 의미에서 버즈 데이터로 펭수의 미래를 예측해보자(https://brunch.co.kr/@llun/35) 같은 포스팅도 올렸었고요.
생각의 흐름은 이랬습니다.
- 어디선가 어떤 퍼즐게임류를 알게 되었다.
- 이름이 노노그램이라고 한다.
- 10x10부터 다양한 크기와 형태가 있는데 100x100은 없다고 한다.
- 사이즈와 상관없이 쉽고 간단해 보이는데?
- 이게 인터넷에 없나? 검색...
- 왜 없을까?
(당연히 어딘가에 있을 수도 있지만 50x50도 풀기가 쉽지 않은 걸로 봐선 안 만든다고 봐야될 것 같습니다.)
내가 한번 만들어볼까?
그렇게 시작한 토이 프로젝트입니다.
이 글에서 코드를 익히는 건 하나도 중요하지 않아요.
더 효율적인 방법은 얼마든지 있을 수 있는데, 일단 만들어보는 게 포인트입니다.
프로그래밍을 처음 배우면서부터 마치 당장 개발자가 될 것처럼 공부한다거나 소프트웨어 엔지니어처럼 깊이 파고들 필요는 없다는 게 제 지론입니다.
처음 문제 의식을 갖고 문제를 정의한 후에 일을 어떻게 진행을 시킬 수 있는지,
이러한 사고의 흐름을 따라가보시면 좋겠습니다.
1. 그림을 간단히 가공해서 원하는 사이즈(100x100)로 리사이즈 한 후
2. 일정 임계값(threshold)을 넘는 부분을 검은색으로, 나머지를 흰색으로 변환하고
3. 100x100 사이즈의 데이터프레임을 만들어서
4. 한줄씩 연속된 값들을 더해주는 방식으로 숫자를 구한 뒤에
5. X축, Y축 각각 100회 반복하면 상단과 왼쪽에 적어줘야하는 숫자 집합이 만들어집니다.
6. 이 값들을 적절한 위치에 HTML 태그 사이에 붙여 넣으면 문제지는 1차 완성
7. 문제를 사람이 풀어볼 수 있도록 표 칸칸마다 버튼 형태의 요소를 추가하고
8. 각 요소의 클릭 시에 색깔이 변환되는 방식으로 스크립트까지 작성해주면 진짜로 완성입니다.
본격적인 코딩에 앞서 이미지를 하나 준비합니다. - 저는 브런치 프로필 이미지를 이용해보겠습니다.
이것을 픽셀아트 혹은 모자이크처럼 보이도록 바꿔주면 될 것 같습니다.
여러가지 방법이 있겠지만
가장 먼저 떠오른 건 '크기를 100*100px로 변환해서 각 픽셀을 1:1로 대응하면 되겠네?'였습니다.
여백이 없도록 crop해주고, 이미지 사이즈를 100x100으로 조절해줍니다.
에디터에서 직접 흑백으로 바꿔주면 이미지 준비는 끝납니다.
(예시와 같이 단순한 형태에서는 '흑백' 변환 작업의 생략이 가능합니다.)
☞ 물론 이 모든 작업을 파이썬이나 R 코드로 처리할 수도 있습니다.
보통 코드를 작성하는 시간과 그림판을 다루는 시간의 효율을 따져보시는 것을 추천드립니다.
한 번으로 끝나는 작업인지 1만 번(!) 반복하는 작업인지가 중요하겠죠.
자, 이렇게 이미지를 준비한 다음에
가장 먼저 한 일은
구글에서 "픽셀값 추출 in r"을 검색(https://www.google.com/search?q=픽셀값+추출+in+r)해봤습니다.
검색 결과를 몇 가지 확인해보니
png 라이브러리의 readPNG() 함수라는 게 있네요.
RStudio를 엽니다. (R이 무언고 하면... 참고)
그리고 코딩을 시작합니다.
# png 파일을 읽어들이기 위한 라이브러리 로드
library(png)
# 배열 형태로 불러오기
img <- readPNG("nono.png")
img <- as.raster(img[,,1:3])
어떤 블로그를 보니 이렇게 하면 픽셀값을 읽을 수 있다고 하네요.
원리는 (아직) 중요치 않습니다.
내가 어떤 데이터를 가지고 있고(png 이미지 파일), 바꾸고자 하는 형태가 명확하니(코딩에 이용할 값) 이를 해결해줄 방법을 찾는 게 필요하니까요.
데이터 형태를 보면,
> dim(img)
[1] 100 100
> str(img)
'raster' chr [1:100, 1:100] "#FFFFFF" "#FFFFFF" "#FFFFFF" "#FFFFFF" "#FFFFFF" "#FFFFFF" "#FFFFFF" "#FFFFFF" "#FFFFFF" "#FFFFFF" "#FFFFFF" "#FFFFFF" ...
RGB HEX코드 100*100=10,000개의 값을 읽어왔습니다.
raster 타입은 뭔지 잘 모르겠으니 일단 익숙한 데이터프레임 형태로 바꿔준 다음에,
nono_df <- matrix(img, ncol = 100, byrow = T) %>% as.data.frame()
이제 이걸 어떻게 1과 0으로 바꿔줄 수 있을까요?
1만 개를 원하는 형태로 치환하는 가장 빠른 방법은?
여기서 '가장 빠른'이란 계산 속도가 아니라 코딩을 하기 편한, 바로 떠오르는 방법입니다.
대부분의 토이프로젝트에서 연산시간을 따질 필요는 없으니까요.
저는 우선 똑같은 크기와 형태의 데이터 프레임을 하나 더 만들어줬습니다.
nono_canvas <- matrix(seq(1, 100*100), ncol=100, byrow = T) %>% as.data.frame()
그리고 복잡하게 생각할 것 없이 2중 for문을 이용해서 모든 칸에 값을 채워줬습니다.
# nono_df의 칸칸마다 하나씩 if 조건문으로 #AAAAAA보다 크면* NA값으로 두고, 검정색을 1로 체크
*숫자가 커질수록 밝은색(#000000은 검정, #FFFFFF는 흰색)입니다.
for (i in 1:100) {
for (j in 1:100) {
nono_canvas[i,j] <- ifelse(nono_df[i,j] > "#AAAAAA", NA, 1) # 0,1을 반대로 (검정이 1)
}
}
> nono_canvas
V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 V11 V12 V13 V14 V15 V16 V17 V18 V19 V20 V21 V22 V23 V24 V25 V26 V27 V28 V29 V30 V31 V32 V33 V34 V35 V36 V37 V38 V39 V40 V41
1 NA NA NA NA NA NA NA NA NA NA NA NA NA NA 1 1 1 1 1 1 NA NA NA NA NA 1 1 1 1 NA NA NA 1 1 1 NA NA NA NA NA NA 2 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA 1 1 1 1 1 NA NA NA NA 1 1 1 1 NA NA 1 1 1 1 1 NA NA NA 1 NA
> View(nono_canvas)
0(NA)과 1로 채워진 100x100짜리 그림판이 완성되었습니다.
정답 그림 + 정답 채점에 사용할 점(point)들이겠네요.
이제 문제지를 만들 차례입니다.
-.null