본문 바로가기
장난감/GIF Maker

GIF Maker 1 - gif 만들기 with imageio

by Mr. Green 2022. 8. 14.

 안녕하세요. 찾아보니 어렵지 않게 gif를 만들 수 있는 방법을 찾았습니다. 바로 imageio를 사용해 gif 형식으로 이미지를 저장하는 방식입니다.

차례

0. imageio

1. 이미지를 gif로

1-1. 이미지 불러오기

1-2. 리사이즈

1-3. gif - 이미지

2. 영상을 gif로

2-1. moviepy

2-2. 프레임 저장

2-3. gif - 영상

2-4. 임시 파일 정리

3. 프로젝트 1

 0. imageio

 imageio는 이미지를 다루는 라이브러리 중 하나입니다. 찾아보니 JAVA에서도 널리 사용하는 것 같더군요.

 

 저는 받은 기억이 없는데 이미 있더라고요. 만약 없다면 다음과 같이 받으면 됩니다.

pip install imageio

 imageio에 대해 자세한 것은 여기에

1. 이미지를 gif로

 이미지와 영상을 gif로 만들 때 방식이 달라집니다.(저는 방법을 못 찾아서 다음과 같이 만들었는데, 좋은 방법 있으시면 알려주시면 감사드립니다.)

1-1. 이미지 불러오기

 gif를 만들 때 사전 작업이 필요합니다. 이미지를 받아서 배열에 담고, 그 배열을 mimsave로 저장하면 완성됩니다. 이 이미지를 배열에 담는 건 일단 간단하게 glob으로 만들어봅시다.

from glob import glob
import imageio

img = glob('./source/*')
#img = glob('./source/*.jpg')

images = []
for fi in img:
	reading = imageio.imread(fi)
	images.append(reading)

 source 파일에 이미지만 있으면 이대로 해도 됩니다. 다행히 jpg, png 등등 섞여있어도 오류는 나지 않았습니다. for를 이용해 glob을 통해 얻은 파일을 imread로 읽어 빈 배열에 넣습니다.

1-2. 리사이즈

 이미지의 가로세로 사이즈를 맞추지 않으면 다음처럼 첫 이미지의 크기에 맞춰져 다음 이미지가 그 크기를 넘어가면 짤려버립니다.

 <이미지 : Cupoi 님, 게임 명일방주, 벽람항로>

 

 따라서 이미지 크기를 조절해 주어야 합니다. 여기서 skimage의 resize를 사용할 겁니다. opencv를 사용하지 않는 이유는 pyinstaller에서 오류가 나서 그렇습니다.

import imageio
from skimage.transform import resize

scale = []
for file in img:
	reading = imageio.imread(file)
	scale.append([reading.shape[0], reading.shape[1])

max_scale = max(scale)
min_scale = min(scale)

 reading.shape[0] : 세로

 reading.shape[1] : 가로

 max, min : 배열을 원소로 같는 배열에서 사용하면 각 배열의 합에 따라 최대최소를 찾아줍니다.

 

 리사이즈를 적용하면 다음과 같이 크기가 통일됩니다.

1-3. gif

 gif로 저장할 때 imageio.mimsave를 사용합니다. 저장할 이미지를 (위에서 만든) 배열로 넣고, 파일 타입을 .gif로 하면 gif로 만들어줍니다.

features = {'duration':1}

for fi in img:
	reading = imagio.imread(fi)
	resized = resize(reading, (max_scale[0], max_scale[1]))
	images.append(resized)

imageio.mimsave('./output/{}.gif'.format(save_name), images, **features)
  • 'duration' : 초 → 한 장의 이미지를 입력한 초 동안 나타납니다.
  • mimsave('이름', images, **features) : images를 '이름'으로 저장합니다. features에는 저장 옵션을 지정합니다.

2. 영상을 gif로

2-1. moviepy

 영상을 읽기 위해서 moviepy의 VideoFileClip을 사용할 것입니다.

from moviepy.editor import VideoFileClip

clip = VideoFileClip(영상).subclip(a, b)

duration = int(clip.duration)
frames = round(clip.fps)
#frames = 10, 12, 20, ...

 영상을 받아주는데, subclip을 추가하면 a부터 b까지의 구간만 받아줍니다. 단위는 초입니다.

 duration은 영상의 길이, frames는 fps(초당 프레임 수)를 받습니다.

2-2. 프레임 저장

 위의 이미지를 gif로 만드는 방식을 활용해서, 영상의 프레임을 저장한 후 그 프레임을 gif로 만드는 걸로 만들어 보겠습니다. 그럼 먼저 저장해봅시다.

from PIL import Image
...

for i in range(0, duration*frames):
	frame = clip.get_frame(i/frames)
	fps = Image.fromarray(frame)

	if i < 10:
		ii = '000' + str(i)
	elif i < 100:
		ii = '00' + str(i)
	elif i < 1000:
		ii = '0' + str(i)

	fps.save('./dodo/' + ii + '.jpg')

 dodo라는 임시파일을 두고 그 안에 프레임을 따와 저장합니다.

  • duration*frames : 영상 길이에 초당 프레임 수를 곱해 총 프레임 수를 구합니다. 그 프레임 수 만큼 저장합니다.
  • clip.get_frame(i/frames) : 괄호 안 순간의 프레임을 받습니다.
  • Image.fromarray(frames) : PIL의 fromarray를 이용해 배열을 이미지로 받습니다.
  • if i < 10: .... : 제목에 숫자를 부여할 때 자리수를 맞춰줍니다.
    • 예) 0001, 0010, 0100
    • 이유는 다음에 glob을 통해 불러올 때, 파일 순서가 숫자 순서가 아닌 문자 순서로 되게 때문입니다.(1, 10, 12, 2, 20...)
  • fps.save() : 얻은 프레임을 'dodo' 안에 저장합니다.

2-3. gif

이미지 부분과 마찬가지로 glob을 통해 이미지를 모으고, gif로 만듭니다.

dodo = glob('./dodo/*')

features = {'duration':1/frames}
images = []
for file in dodo:
	reading = imageio.imread(file)
	images.append(reading)

imageio.mimsave('이름', images, **features)

 영상의 fps(혹은 지정된 fps)를 features에 담아 gif를 만듭니다. 설명은 이미지의 부분과 같지만, 사용된 이미지는 한 영상에서 나온 것이므로 리사이즈를 할 필요는 없습니다.

2-4. 임시 파일 정리

 이제 gif를 만들었으니, gif를 만들기 위해 만들어진 수 백~천 장의 프레임을 정리해 줘야 합니다.

import os
...

to_delet = glob('./dodo/*')
for fi in to_delet:
	os.remove(fi)

3. 프로젝트 1

 image2gif

from glob import glob
import imageio

from skimage.transform import resize

scale_sel = 'max'       #이미지 리사이즈

save_name = 'output3'   #input
frame = 0.5             #combobox
img_type = 'png'        #combobox + all files

source = './source/'
features = {'duration':frame}



img = glob('./source/*')

scale = []
for file_name in img:
    reading = imageio.imread(file_name)
    #print(reading.shape[0], ', ', reading.shape[1])
    scale.append([reading.shape[0], reading.shape[1]])


max_scale = max(scale)
min_scale = min(scale)
print(max_scale)
print(min_scale)


images = []
if scale_sel == 'max':
    for file_name in img:
        reading = imageio.imread(file_name)
        resized = resize(reading, (max_scale[0], max_scale[1]))
        images.append(resized)
if scale_sel == 'min':
    for file_name in img:
        reading = imageio.imread(file_name)
        resized = resize(reading, (min_scale[0], min_scale[1]))
        images.append(resized)

imageio.mimsave('./output/{}.gif'.format(save_name), images, **features)

 video2gif

from glob import glob
import os

from moviepy.editor import VideoFileClip
from PIL import Image


clip = VideoFileClip(영상).subclip(a, b)

duration = int(clip.duration)
frames = round(clip.fps)
#frames = 10, 12, 20, ...


for i in range(0, duration*frames):
	frame = clip.get_frame(i/frames)
	fps = Image.fromarray(frame)

	if i < 10:
		ii = '000' + str(i)
	elif i < 100:
		ii = '00' + str(i)
	elif i < 1000:
		ii = '0' + str(i)

	fps.save('./dodo/' + ii + '.jpg')

dodo = glob('./dodo/*')

features = {'duration':1/frames}
images = []
for file in dodo:
	reading = imageio.imread(file)
	images.append(reading)

imageio.mimsave('이름', images, **features)


to_delet = glob('./dodo/*')
for fi in to_delet:
	os.remove(fi)

'장난감 > GIF Maker' 카테고리의 다른 글

GIF Maker 4 - avif (with pillow)  (3) 2025.03.23
GIF Maker 3 - gif with pillow  (0) 2023.01.21
GIF Maker 2 - GUI  (0) 2022.08.15
GIF Maker - GIF를 만들어보자 + 3.0 업데이트!  (0) 2022.08.14