안녕하세요. 찾아보니 어렵지 않게 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 |