대회 끝나니 바로 기말이 시작되어 허겁지겁 기말 프로젝트 하는 중임. 가장 급한게 영상처리 강의라서 그거 기말 프로젝트 먼저 하는 중. 근데 몇가지 생각해 본 아이디어가 다 실제로 해보다보니 영 아니다 싶어 거듭 던져야 했는데, 다행히 이번 아이디어는 나름 성과가 나오는거 같음.
디지털 영상 자막 인식기를 만드는게 목적임. OCR은 아니고, 그냥 영상을 입력 받아 해당 영상에서 자막이 들어 있는 부위만 잘라 추출해 따로 모아놓는 프로그램임. 거기서 텍스트까지 추출하는건 시간이 부족할거 같아 과감히 스킵함.
영상에 자막이 인식 되면 이런 식으로 그걸 잘라냄. 예시는 일부러 좆같은걸 고름. 의외로 인공지능 하나도 안 들어간 순수 영상처리 프로그램임.
방법을 간단히 정리하면 다음과 같음. 자막의 각 글자는 보통 서로와 근접한 위치에 수평으로 나열되어 있음. 그리고 자막은 보통 폰트 그 자체만 해도 픽셀 밝기값(intensity)가 자잘하게 계속 변동이 있는 형식을 띄고, 그 뒤에 깔린 오리지널 영상과 보통 극명한 대비를 보임. 따라서, 영상의 각 행에 푸리에 변환을 가해 주파수 영역으로 바꿀 경우 자막이 위치한 행은 타 행보다 고 주파수 계수가 높게 나오게 됨.
---->
이런 식으로 말이지.
반면 자막이 없는 경우 행 단위로 주파수 변환을 하면 비교적 값이 일관적으로 나오게 됨.
---->
차이가 보일거임. 따라서 나는 이 중 자막이 들어 있는 영상에서 보이는 특징을 더욱 극대화시키기 위해 각 행마다 그에 속한 계수값을 모두 합해 그 행의 모든 항에 다시 그 값을 할당해 넣었고, 정규화 과정을 통해 각 행의 값이 0-255 범위 안에 들어오도록 함.
그러한 과정을 거치면 자막이 포함 된 행이 더욱 두각되어 드러남. 그에 더해 Otsu's method 를 적용해 영상 이진화를 할 수 있고, 수직 커널을 가지고 모폴로지 닫힘 연산과 열림 연산을 이어 수행해 다음 이진영상을 얻어낼 수 있음.
그럼 이게 자막이 들어 있는 행의 위치가 됨. 자막이 들어 있는 영상의 경우 보통 이 단계에서 값을 가진 항의 비율이 값을 가지지 못한 항보다 더 낮음. 대충 10~30% 내외임. 반면, 자막이 들어 있지 않은 영상의 경우 특출나게 두드러지는 부위가 없고, 따라서 정규화->이진화 단계에서 값을 가진 항의 비율이 더욱 높게 나타나게 됨. 보통 55~80% 내외임. 따라서 추가 연산을 수행하기 전에 이 단계에서 그 특징을 이용해 자막 여부를 확인함. 없다고 판정되면 걸러내고, 있다 판정되면 다음 단계로 넘어감.
다음 단계라 해서 크게 대단한건 없음. 그냥 이리 뽑아낸 영역에 대해 같은 연산을 다만 열 단위로 적용해 해당 영역 중 어느 열에 자막이 들어 있는지 판정해내는게 전부임. 다만 순서가 중요한데, 행 단위 연산을 통해 먼저 영역을 뽑아낸 이후 그에 대해 열 단위 연산을 적용해야함. 왜냐면 자막 특성 상 행을 따라 나열해 있기 때문에 행 단위 연산이 더욱 정확하고, 먼저 그를 적용해 영역을 좁히고 나서야 열 단위 연산의 정확도가 쓸만해지기 때문임. 사실 그러고나서도 비교적 정확도가 낮은 편이고 따라서 인식 된 자막의 높이가 딱 맞게 나오는 것과 달리 그 너비는 좀 더 널널하게 잡힘. 부족하게 잡히는 것보다야 그게 더 나아서 좀 널널하게 연산을 수행하기 때문임.
그것 역시 수행하고 나면 다음 이진영상을 얻게 됨.
이 케이스에선 그리 나오지 않았지만, 열단위 연산에서 false positive가 나올 수 있음. 그러면 보통 자막이 위치한 곳 외에도 사각형이 자잘하게 한두개씩 남아있게 됨. 따라서 opencv의 findContours 함수를 통해 모든 외적직사각형을 찾아내고, 그 중 가장 영역이 넓은 직사삭형을 자막이 위치한 영역으로 삼음. 그럼 이제 그 영역만 잘라 출력하면 되고, 그 결과는 다음과 같음.
잘 만든거 같아 뿌듯함.
--------------------------
애니 영상을 누가 댓글에서 언급해주어서 함 그거도 돌려 봄. 고려해보지 못한 경우였음. 돌려보니 나름 잘 인식되는거 같음.
--------------------------------------------------------------------------------
https://github.com/MegasKomnenos/subscanner
깃헙에 올림
굿
애니메이션 같은 영상에서는 잘 작동하지 않을 수 도 있겠네
오 함 돌려봐야겠다. 실사 영상만 돌려서 그건 미처 생각해보지 못함. 사실 이걸 만든 이유가 하드코딩 된 자막들 이후 처리하기 쉽도록 미리 전처리해두는건데, 보통 중국발 사극에 그런 경우가 많아 애니는 떠올리지 못한거 같음.
ㄱㅅㄱㅅ
돌려보니 잘 인식함. 돌린 이미지와 출력물 모두 원문에 추가 첨부함.
나도 슥 보고 디시콘 달고 갑자기 생각났어 ㅋㅋ 그림체나 채색법에 따라 다를 것 같긴 한데 보통 애니메이션 같은 경우엔 경계면 에서 색 변화가 갑작스럽게 일어나니까.. 즉 이런게 frequency domain에서 high-frequency 요소로 작용할 수 있으니까
생각 보다 잘 되나보네
엔젤비트인가
ㅇㅇ 돌려보니 나름 잘 되는거 같음. 고려해보지 못한 경우 말해줘서 ㄱㅅㄱㅅ
애니가 뭔진 잘 몰겠음 ㅋㅋㅋㅋㅋㅋㅋ. 걍 구글검색해서 나온거 아무거나 집어 옴.
ㅠㅠ
ㅋㅋ 암튼 이런 코너 케이스들 잘 넣어서 교수님 보여드리면 좋아하실듯..
A+ 가즈아!
고수
푸리에 변환 영상이 2개가 있는데, 그중 첫번째는 그냥 행단위로 푸리에 변환을 적용한거고, 두번째는 그걸 이용해 자막이 위치한 행을 otsu's method로 구별하기 쉽게 몇가지 처리를 가한거임. 그게 전부.
구현은 간단했을지라도 가설 세우는 것과 가설 세우는데 필요한 배경지식은 전혀 간단해 보이지가 않는데
아 미안 아래 답댓 단다는거 여기 달았네. 어쩐지 사라져서 다시 달았는데 걍 여기 달린거였구나 ㅋㅋㅋㅋㅋㅋㅋㅋㅋ 미안
푸리에 변환할때 무슨값을 변환하는거임? grayscale로 바꾼다음 그 값을 변환하는거임?
아 그거 얘기를 까먹었네. grayscale로 변환해서 그걸 주파수 영역으로 변환하는거 맞음 ㅇㅇ
푸리에 변환후 그림이 이해가 안가는데, 보니까 세로축은 real space의 y좌표같고 가로축은 푸리에 변환후 각 component의 값 같은데, 저렇게 일정하게 나오는게 이상한거 같은데. 예를들어 high frequency가 클 경우 그림의 오른쪽 부분만 intensity가 크게 나와야하는거 아님?
지금보면 좌우가 대칭적으로 나오는거 같은데 원래 그런건가?
생각해보면 자막이 있는부분은 intensity가 상수값으로 지속되는 부분이 있으니까 fourier transform할때 모든 component가 일정하게 나와야 하는게 맞을것으로 보이는데, 그러면 예를들어 화면에 동일한 색상의 가로줄이 있으면 이 부분이 변환후 제일 큰 값이 나오겠네. 따져보면 자막부부분이 색의 대비가 큰게 중요한건 아닌거같은데
푸리에 변환은 그저 주어진 신호를 그 구성요소로 분해하는 것에 불과하고 이 경우 나는 각 행마다 그를 신호로 다루어 그 구성요소로 분해하여 그 계수값을 가지고 특징을 추출하는게 목적이었음. 따라서 행단위 푸리에 변환을 적용한 결과를 그 과정에 해당하는 부분에 예시로 올렸고, 그 이후 그걸 기반으로 otsu's algorithm 적용하기 쉽게 몇가지 처리를 적용한게 그 뒤에 이어 나와 있음. 그게 전부임.
그러면 저 그레이스케일 그림에서 x축과 y축 값이 뭐야
푸리에 변환 적용 이전 이후 비교하면 알겠지만 그 형상이 달라진건 없음.
아니 푸리에 변환을 했으면 변수가 바뀌었을거 아니야. 각 x,y좌표에 대해 grayscale 값을 g(x,y)로 정의하면 행단위의 푸리에 변환을했다는건 g(k,y)로 변환했다는거아님? 구리고 저 그림에 그린건 그 좌표에 대한 intensity를 그린거아님?
그리고 frequency domain에서 intensity가 일정하게 나온다는건, 다시 real space로 바꿧을때 delta function처럼 나오게 된다는건데 그걸 자막 특성상 그건 아닌거같고.. 뭔가 내가 그림을 잘못 이해했을수도있음
각 행이 하나의 신호고 각 행의 각 항의 인덱스가 주파수를 나타내며 그 값이 해당 주파수의 계수를 나타냄.
https://pastebin.com/VjZw1PtF
cv코드 보니까 일단 원점을 중심에 놓고 그리는거라 좌우대칭으로는건 맞는거같고, 오히려 자막부분에서는 low feequency쪽이 크기가 크게 나오는게 더 특징 아님? 그림보면 high frequency는 모든 영역에서 크게나오는데 low 쪽은 자막부분만 크게 나오는듯? 따지고보면 자막은 행을 기준으로 intensity가 일정하게 나오는 부분들이
계속 나왔다가 사라졌다가 하니까 square wave를 푸리에 변환 했다고 대충 생각한다고 치면 low frequency쪽이 높게 나오는게 오히려 자막부분 특징 아님?
사실 그림만 보고 해본 생각이라 헷갈리기는한데,,그냥 왜 저렇게 나오는지 궁금해서 물어본거였음
깃헙 링크 본문 아래 첨부했으니 이해 안 되는 부분 해결하고 싶으면 그거 직접 뜯어보는게 더 빠를거 같음.
미쳤다
오....
개추할 수 밖에 업따
공학자ㄷㄷ
개신기하다
ㅁㅊ ㄷㄷ
와
개지린다
개추
와 미쳤다 이건 진짜고수네 - dc App
멋지당
헤헤 완장님 부끄럽사옵니다
ㄷㄷ
오 신기하네.... 자막이 짧아도 정상적으로 인식이 됨?
생각해보지 못한 경우네. 함 돌려봐야겠음. ㄱㅅㄱㅅ
이중자막도 됨? 노래나올 때 많이 나오는데
ㄴㄴ. 다만 방학 도중 더 기능을 확장하게 되면 그때 추가할 생각임.
멋져
DFT 어떻게 쳤길래 저렇게 나오지? 각 행에 x축으로 친건가?
존나 알록달록 화려한 영화에서도 잘나오는지 궁금함. 어두운 배경에 불꽃이 튀는 장면 같은거...
dft = cv.dft(np.float32(src), flags=cv.DFT_COMPLEX_OUTPUT+cv.DFT_ROWS)
깃헙에 소스코드 이미 올려놓았으니 그거 보아도 되지만, 별거 없음. 걍 행 단위로만 돌리게 함. 원래 2D는 행단위로 돌린 결과값에 다시 열단위로 한번 더 돌리는데, 그 중 두번째 단계를 없앤거.
액션 영화는 나도 그거 보고 함 궁금해서 돌려보니, 확실히 정확도가 떨어지긴 하더라. 새로운 테스트 케이스 레포에 올렸으니 확인해 볼 수 있을텐데, 좀 정확도가 떨어졌음. 그래도 인식이 되긴 하고, 액션씬에 자막이 들어갈 일도 그리 많지 않으니 큰 문제가 되진 못할거 같음.
아이디어 좋네 굿굿 저렇게 잘 될줄은 몰랐음