Java 동영상 합치기 - Java dong-yeongsang habchigi

(원문) ffmpeg로 timelapse 동영상 만들기

 

 

카메라로부터 획득된 영상을 이미지 파일로 저장하였 때 이를 합쳐서 동영상을 만들고 싶은 경우 활용할 수 있는 간단하면서도 효과적인 방법이다.

 

 

1. FFmpeg Build 다운로드 받기

(링크) https://ffmpeg.zeranoe.com/builds/

 

다운로드 받은 후 원하는 디렉토리에 압축해제 하면 됨.

 

Java 동영상 합치기 - Java dong-yeongsang habchigi

 

 

2. 연속된 이미지 준비

 

2.1 동영상으로 만들 이미지를 한폴더에 정리하기

 

2.2. 파일명을 연속된 숫자로 일괄 변경하기

# 알씨의 경우, 이름 바꾸기 기능 이용하여 손쉽게 파일명을 일괄 변경할 수 있다.

 

## 실행창(윈도우+R -> cmd 엔터)을 열어 FFmpeg Build 압축 해제 한 폴더의 bin 폴더로 이동

 

또는

 

## 윈도우 탐색기에서 FFmpeg Build 압축 해제 한 폴더의 bin 폴더로 이동 후, Shift 누른 채 우클릭 하여 '여기서 명령 창 열기' 클릭하여 명령창 열기

 

2.3 명령어 입력

ffmpeg -f image2 -r 30 -i <이미지 있는 폴더\%04d.jpg> -vcodec libx264 <동영상 저장될 폴더>

 

eg.) ffmpeg -f image2 -r 30 -i C:\image\%04d.jpg -vcodec libx264 C:\temp\output.mp4

 

-f : 입력 파일의 포맷 의미, image2는 jpg 파일 의미

-r : 출력 동영상 fps 지정

-i : 입력 파일 위치 및 형식 %04d.jpg는 4자리 숫자로 구성된 jpg 파일을 의미한다. (ex. 0000.jpg, 0001.jpg, ..., 9999.jpg)

-vcodec : 출력 동영상의 압축 코덱을 의미, H.264가 가장 무난하다고 함

 

 

Java 동영상 합치기 - Java dong-yeongsang habchigi

<명령창에서 실행 하였을 때 나오는 출력 예>

 

 

 

 

 

반응형

공유하기

게시글 관리

구독하기Warehouse B

저작자표시 비영리 변경금지

'IT > Others' 카테고리의 다른 글

Visual Studio Community 이전 버전 다운로드 링크  (0)2018.08.30Visual Studio 제품명과 버전  (0)2017.04.06[팟플레이어] 스피커 파열음 제거  (0)2015.04.08YouTube 동영상 다운로드  (0)2015.03.05Amazon 가격 추적 - camelcamelcamel.com  (1)2015.02.25

 

Java 동영상 합치기 - Java dong-yeongsang habchigi


버추얼덥을 실행

Java 동영상 합치기 - Java dong-yeongsang habchigi




동영상을 합칠 여러 파일중에 첫부분으로 쓰실 동영상을 불러옵니다 File -> Open video file 을 선택해서 파일을 엽니다
파일순서가 동영상순서가 됩니다


Java 동영상 합치기 - Java dong-yeongsang habchigi


그 다음 합칠 동영상 파일을 불러옵니다 File -> Append AVI segment 를 선택합니다

합칠 동영상파일을 엽니다 파일을 합치는 순서가 동영상순서가 됩니다
그리고 그림밑 부분의 Autodetect additional segments by filename 은 비슷한 이름의 파일을 다 불러오는 기능입니다
합칠 파일이 많으면 체크하시고 그냥 보통은 체크해제 하시고 파일을 열면 됩니다


Java 동영상 합치기 - Java dong-yeongsang habchigi



동영상 파일을 다 불러 오셨으면 Video -> Direct stream copy (원본 그대로 출력) 를 선택하시고


Java 동영상 합치기 - Java dong-yeongsang habchigi


마지막으로 저장을 하시면 됩니다 File -> Save as AVI 선택

공유하기

게시글 관리

구독하기황박사의 정보바다

  • 카카오스토리
  • 트위터
  • 페이스북

어디까지나 가장 일반적으로 구현되는 방식이 절반으로 나누는 방식일 뿐이다. 보통 위와 같이 두 개의 부분리스트로 나누는 방식을 two-way 방식이라고 하니 참고하시면 좋을 듯 하다.

 

 

 

 

 

그러면 일단 두 개씩 나누어 부분리스트를 생성한다는 것은 이해했을 것이다. 문제는 두 부분리스트를 정렬하고 합치는 방식인데, 그리 어렵지 않으니 빠르게 알아보도록 하자.

 

일단 우리가 이해하고 있어야 할 점은 각각의 부분리스트는 '정렬된 상태'라는 점이다.

두 부분리스트를 합쳐서 정렬할 때 굳이 삽입, 버블 정렬 등을 활용할 필요가 없다는 것이다. 그럼 어떻게 정렬을해? 라고 묻는다면 각 부분리스트의 첫 번째 원소부터 순차적으로 비교만 해주면 된다.

 

말로는 어려울 수 있으니 다음 이미지를 보자.

 

Java 동영상 합치기 - Java dong-yeongsang habchigi
Java 동영상 합치기 - Java dong-yeongsang habchigi

 

 

 

이미 각각의 부분리스트는 오름차순으로 정렬되어있기 때문에 앞부분부터 시작하여 하나씩 비교해주며 정렬해주면 된다.

즉 이 부분만 코드로 보자면 다음과 같다.

 

 

 

 

/**
 * 부분리스트는 a배열의 left ~ right 까지이다. 
 * 
 * @param a		정렬할 배열
 * @param left	배열의 시작점
 * @param mid	배열의 중간점
 * @param right	배열의 끝 점
 */
private static void merge(int[] a, int left, int mid, int right) {
	int l = left;		// 왼쪽 부분리스트 시작점
	int r = mid + 1;	// 오른쪽 부분리스트의 시작점 
	int idx = left;		// 채워넣을 배열의 인덱스
	
	
	while(l <= mid && r <= right) {
		/*
		 *  왼쪽 부분리스트 l번째 원소가 오른쪽 부분리스트 r번째 원소보다 작거나 같을 경우
		 *  왼쪽의 l번째 원소를 새 배열에 넣고 l과 idx를 1 증가시킨다.
		 */
		if(a[l] <= a[r]) {
			sorted[idx] = a[l];
			idx++;
			l++;
		}
		/*
		 *  오른쪽 부분리스트 r번째 원소가 왼쪽 부분리스트 l번째 원소보다 작거나 같을 경우
		 *  오른쪽의 r번째 원소를 새 배열에 넣고 r과 idx를 1 증가시킨다.
		 */
		else {
			sorted[idx] = a[r];
			idx++;
			r++;
		}
	}
		
	/*
	 * 왼쪽 부분리스트가 먼저 모두 새 배열에 채워졌을 경우 (l > mid)
	 * = 오른쪽 부분리스트 원소가 아직 남아있을 경우
	 * 오른쪽 부분리스트의 나머지 원소들을 새 배열에 채워준다.
	 */
	if(l > mid) {
		while(r <= right) {
			sorted[idx] = a[r];
			idx++;
			r++;
		}
	}
		
	/*
	 * 오른쪽 부분리스트가 먼저 모두 새 배열에 채워졌을 경우 (r > right)
	 * = 왼쪽 부분리스트 원소가 아직 남아있을 경우
	 * 왼쪽 부분리스트의 나머지 원소들을 새 배열에 채워준다.
	 */
	else {
		while(l <= mid) {
			sorted[idx] = a[l];
			idx++;
			l++;
		}
	}
	
	/*
	 * 정렬된 새 배열을 기존의 배열에 복사하여 옮겨준다.
	 */
	for(int i = left; i <= right; i++) {
		a[i] = sorted[i];
	}
}

 

 

 

 

 

 

 

 

그리고 혹시 몰라 움직이는 이미지로 이해할 수 있도록 이미지를 첨부했다.

전체적인 흐름을 보자면 다음과 같은 형태로 정렬 한다.

 

 

Java 동영상 합치기 - Java dong-yeongsang habchigi
https://en.wikipedia.org/wiki/Merge_sort

 

 

Java 동영상 합치기 - Java dong-yeongsang habchigi
https://en.wikipedia.org/wiki/Merge_sort

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

그 외에도 많은 참고 영상들이 있는데, 영상으로 보는 것이 더 쉬울 것 같아 같이 첨부하도록 하겠다.

 

 

 

https://www.youtube.com/watch?v=ZRPoEKHXTJg

 

 

 

 

 

 

 

 

 

 

 





  • Merge Sort 구현하기

 

 

 

 

그럼 본격적으로 구현 된 코드를 보자.

 

 

[Top-Down 형식]

public class Merge_Sort {

	private static int[] sorted;		// 합치는 과정에서 정렬하여 원소를 담을 임시배열
	
	public static void merge_sort(int[] a) {
		
		sorted = new int[a.length];
		merge_sort(a, 0, a.length - 1);
		sorted = null;
	}
	
	// Top-Down 방식 구현
	private static void merge_sort(int[] a, int left, int right) {
		
		/*
		 *  left==right 즉, 부분리스트가 1개의 원소만 갖고있는경우 
		 *  더이상 쪼갤 수 없으므로 return한다.
		 */
		if(left == right) return;
		
		int mid = (left + right) / 2;	// 절반 위치 
		
		merge_sort(a, left, mid);		// 절반 중 왼쪽 부분리스트(left ~ mid)
		merge_sort(a, mid + 1, right);	// 절반 중 오른쪽 부분리스트(mid+1 ~ right)
		
		merge(a, left, mid, right);		// 병합작업
		
	}
	
	/**
	 * 합칠 부분리스트는 a배열의 left ~ right 까지이다. 
	 * 
	 * @param a		정렬할 배열
	 * @param left	배열의 시작점
	 * @param mid	배열의 중간점
	 * @param right	배열의 끝 점
	 */
	private static void merge(int[] a, int left, int mid, int right) {
		int l = left;		// 왼쪽 부분리스트 시작점
		int r = mid + 1;	// 오른쪽 부분리스트의 시작점 
		int idx = left;		// 채워넣을 배열의 인덱스
		
		
		while(l <= mid && r <= right) {
			/*
			 *  왼쪽 부분리스트 l번째 원소가 오른쪽 부분리스트 r번째 원소보다 작거나 같을 경우
			 *  왼쪽의 l번째 원소를 새 배열에 넣고 l과 idx를 1 증가시킨다.
			 */
			if(a[l] <= a[r]) {
				sorted[idx] = a[l];
				idx++;
				l++;
			}
			/*
			 *  오른쪽 부분리스트 r번째 원소가 왼쪽 부분리스트 l번째 원소보다 작거나 같을 경우
			 *  오른쪽의 r번째 원소를 새 배열에 넣고 r과 idx를 1 증가시킨다.
			 */
			else {
				sorted[idx] = a[r];
				idx++;
				r++;
			}
		}
		
		/*
		 * 왼쪽 부분리스트가 먼저 모두 새 배열에 채워졌을 경우 (l > mid)
		 * = 오른쪽 부분리스트 원소가 아직 남아있을 경우
		 * 오른쪽 부분리스트의 나머지 원소들을 새 배열에 채워준다.
		 */
		if(l > mid) {
			while(r <= right) {
				sorted[idx] = a[r];
				idx++;
				r++;
			}
		}
		
		/*
		 * 오른쪽 부분리스트가 먼저 모두 새 배열에 채워졌을 경우 (r > right)
		 * = 왼쪽 부분리스트 원소가 아직 남아있을 경우
		 * 왼쪽 부분리스트의 나머지 원소들을 새 배열에 채워준다.
		 */
		else {
			while(l <= mid) {
				sorted[idx] = a[l];
				idx++;
				l++;
			}
		}
		
		/*
		 * 정렬된 새 배열을 기존의 배열에 복사하여 옮겨준다.
		 */
		for(int i = left; i <= right; i++) {
			a[i] = sorted[i];
		}
	}
}

 

 

 

 

기본적으로 위와같이 재귀를 이용하여 풀이한 방식이 가장 이해하기 빠를 것이다.

 

위와같이 분할정복 방식으로 두 부분을 짤라 들어가면서 서브 문제를 해결하는 방식으로 구현 되는 가장 일반적인 방식이다.

위 코드를 이해하면 반대로 Bottom-Up 방식으로도 구현이 가능하다.

 

 

 

 

 

[Bottom-Up 형식]

 

public class Merge_Sort {

	private static int[] sorted;		// 합치는 과정에서 정렬하여 원소를 담을 임시배열
	
	public static void merge_sort(int[] a) {
		
		sorted = new int[a.length];
		merge_sort(a, 0, a.length - 1);
		sorted = null;
	}
	
	// Bottom-Up 방식 구현
	private static void merge_sort(int[] a, int left, int right) {
		
		/*
		 * 1 - 2 - 4 - 8 - ... 식으로 1부터 서브리스트를 나누는 기준을 두 배씩 늘린다.
		 */
		for(int size = 1; size <= right; size += size) {
			
			/*
			 * 두 부분리스트을 순서대로 병합해준다.
			 * 예로들어 현재 부분리스트의 크기가 1(size=1)일 때
			 * 왼쪽 부분리스트(low ~ mid)와 오른쪽 부분리스트(mid + 1 ~ high)를 생각하면
			 * 왼쪽 부분리스트는 low = mid = 0 이고,
			 * 오른쪽 부분리스트는 mid + 1부터 low + (2 * size) - 1 = 1 이 된다.
			 *  
			 * 이 때 high가 배열의 인덱스를 넘어갈 수 있으므로 right와 둘 중 작은 값이
			 * 병합되도록 해야한다. 
			 */
			for(int l = 0; l <= right - size; l += (2 * size)) {
				int low = l;
				int mid = l + size - 1;
				int high = Math.min(l + (2 * size) - 1, right);
				merge(a, low, mid, high);		// 병합작업
			}
		}
		
		
		
	}
	
	/**
	 * 합칠 부분리스트는 a배열의 left ~ right 까지이다. 
	 * 
	 * @param a		정렬할 배열
	 * @param left	배열의 시작점
	 * @param mid	배열의 중간점
	 * @param right	배열의 끝 점
	 */
	private static void merge(int[] a, int left, int mid, int right) {
		int l = left;		// 왼쪽 부분리스트 시작점
		int r = mid + 1;	// 오른쪽 부분리스트의 시작점 
		int idx = left;		// 채워넣을 배열의 인덱스
		
		
		while(l <= mid && r <= right) {
			/*
			 *  왼쪽 부분리스트 l번째 원소가 오른쪽 부분리스트 r번째 원소보다 작거나 같을 경우
			 *  왼쪽의 l번째 원소를 새 배열에 넣고 l과 idx를 1 증가시킨다.
			 */
			if(a[l] <= a[r]) {
				sorted[idx] = a[l];
				idx++;
				l++;
			}
			/*
			 *  오른쪽 부분리스트 r번째 원소가 왼쪽 부분리스트 l번째 원소보다 작거나 같을 경우
			 *  오른쪽의 r번째 원소를 새 배열에 넣고 r과 idx를 1 증가시킨다.
			 */
			else {
				sorted[idx] = a[r];
				idx++;
				r++;
			}
		}
		
		/*
		 * 왼쪽 부분리스트가 먼저 모두 새 배열에 채워졌을 경우 (l > mid)
		 * = 오른쪽 부분리스트 원소가 아직 남아있을 경우
		 * 오른쪽 부분리스트의 나머지 원소들을 새 배열에 채워준다.
		 */
		if(l > mid) {
			while(r <= right) {
				sorted[idx] = a[r];
				idx++;
				r++;
			}
		}
		
		/*
		 * 오른쪽 부분리스트가 먼저 모두 새 배열에 채워졌을 경우 (r > right)
		 * = 왼쪽 부분리스트 원소가 아직 남아있을 경우
		 * 왼쪽 부분리스트의 나머지 원소들을 새 배열에 채워준다.
		 */
		else {
			while(l <= mid) {
				sorted[idx] = a[l];
				idx++;
				l++;
			}
		}
		
		/*
		 * 정렬된 새 배열을 기존의 배열에 복사하여 옮겨준다.
		 */
		for(int i = left; i <= right; i++) {
			a[i] = sorted[i];
		}
	}
}

 

 

 

merge하는, 즉 병합하는 메소드는 그대로 두고 부분리스트로 나누는 과정만 Bottom-Up 방식으로 변경해주면 된다.

 

 

 

 

 

이렇게 두 가지로 나누어 구현 할 수 있다.

 

 

대부분의 경우 정렬 과정은 최대한 재귀는 피하여 구현하는게 일반적이기 때문에 Bottom-Up 으로 구현하는 것이 좋다.

 

 

 

 

 

 

 

 

 

 





 

  • 병합 정렬의 장점 및 단점




[장점]

1. 항상 두 부분리스트로 쪼개어 들어가기 때문에 최악의 경우에도 O(NlogN) 으로 유지가 된다.

2. 안정정렬이다.

 

 

 

[단점]

1. 정렬과정에서 추가적인 보조 배열 공간을 사용하기 때문에 메모리 사용량이 많다.

2. 보조 배열에서 원본배열로 복사하는 과정은 매우 많은 시간을 소비하기 때문에 데이터가 많을경우 상대적으로 시간이 많이 소요된다.

 

 

 

 

 

위에서 시간 복잡도에 대해 O(NlogN) 으로 유지가 된다고 했다.

 

왜 그러한 시간복잡도가 나오는지 한 번 같이 알아보도록 하자. 

 

 

Java 동영상 합치기 - Java dong-yeongsang habchigi

 

 

 

 

 

N개의 데이터가 있는 리스트를 1개까지 쪼개어 트리로 나타내게 되면 이진트리(Binary Tree) 형태로 나온다는 것은 우리가 확인 할 수 있다.

N개 노드에 대한 이진트리의 높이(h)는 logN 인 것은 힙정렬에서 다뤘으므로 해당 링크를 참고하시길 바란다.

 

그러면 비교 및 정렬 과정을 생각해보아야 한다.

 

우리가 두 개의 서브리스트(배열)을 sorted 배열에 합치는 merge과정을 생각해보자.

이미 두 서브리스트는 정렬된 형태라 앞 원소부터 차례대로 비교하며 안착시키기만 하면 된다. 즉, 아무리 최악의 상황이어도 두 개의 서브리스트 원소 개수만큼의 비교 및 새 배열로 안착시킨다.