Secara terprogram menghasilkan video atau animasi GIF dengan Python?


221

Saya memiliki serangkaian gambar tempat saya ingin membuat video. Idealnya saya bisa menentukan durasi frame untuk setiap frame tetapi frame rate yang tetap akan baik-baik saja. Saya melakukan ini di wxPython, jadi saya dapat merender ke wxDC atau saya dapat menyimpan gambar ke file, seperti PNG. Apakah ada pustaka Python yang memungkinkan saya membuat video (AVI, MPG, dll) atau animasi GIF dari bingkai ini?

Sunting: Saya sudah mencoba PIL dan sepertinya tidak berhasil. Adakah yang bisa mengoreksi saya dengan kesimpulan ini atau menyarankan alat lain? Tautan ini tampaknya mendukung kesimpulan saya mengenai PIL: http://www.somethinkodd.com/oddthinking/2005/12/06/python-imaging-library-pil-and-animated-gifs/

Jawaban:


281

Saya akan merekomendasikan tidak menggunakan images2gif dari visvis karena memiliki masalah dengan PIL / Bantal dan tidak dipelihara secara aktif (saya harus tahu, karena saya penulisnya).

Sebagai gantinya, silakan gunakan imageio , yang dikembangkan untuk memecahkan masalah ini dan lebih banyak lagi, dan dimaksudkan untuk tetap.

Solusi cepat dan kotor:

import imageio
images = []
for filename in filenames:
    images.append(imageio.imread(filename))
imageio.mimsave('/path/to/movie.gif', images)

Untuk film yang lebih lama, gunakan pendekatan streaming:

import imageio
with imageio.get_writer('/path/to/movie.gif', mode='I') as writer:
    for filename in filenames:
        image = imageio.imread(filename)
        writer.append_data(image)

37
juga parameter durasi = 0,5 mengatur durasi 0,5 detik untuk setiap frame.
Alleo

3
ValueError: Tidak dapat menemukan format untuk membaca file yang ditentukan dalam mode 'i' - Saya mendapatkan kesalahan ini di windows 2.7 winpython. Ada petunjuk?
Vanko

1
@Vo, kesalahannya tampaknya terkait dengan pembacaan file, Anda dapat mencoba imagio.mimread, atau jika ini film dengan banyak bingkai, gunakan objek pembaca seperti di sini: imageio.readthedocs.io/en/latest/…
Almar

2
@Alleo: "juga parameter durasi = 0,5 mengatur durasi 0,5 detik untuk setiap frame". Ada fitur durasi untuk imageio? Jika demikian, di mana ini didokumentasikan? Saya membaca semua dokumen dan tidak menemukan argumen durasi.
Chris Nielsen

3
Luar biasa! imageio in anacondahasil True, yay!
uhoh

47

Nah, sekarang saya menggunakan ImageMagick. Saya menyimpan bingkai saya sebagai file PNG dan kemudian memanggil convert.exe ImageMagick dari Python untuk membuat animasi GIF. Yang menyenangkan tentang pendekatan ini adalah saya dapat menentukan durasi frame untuk setiap frame secara individual. Sayangnya ini tergantung pada ImageMagick yang diinstal pada mesin. Mereka memiliki pembungkus Python tetapi terlihat sangat jelek dan tidak didukung. Masih terbuka untuk saran lainnya.


21
Saya seorang pria Python tetapi menemukan ImageMagick jauh lebih mudah di sini. Saya baru saja membuat urutan gambar dan menjalankan sesuatu seperticonvert -delay 20 -loop 0 *jpg animated.gif
Nick

Saya setuju, ini adalah solusi terbaik yang pernah saya temui. Berikut adalah contoh minimal (berdasarkan pada kode contoh pengguna Steve B yang diposting di stackoverflow.com/questions/10922285/… ): pastebin.com/JJ6ZuXdz
andreasdr

Menggunakan ImageMagick, Anda juga dapat dengan mudah mengubah ukuran animasi gif seperticonvert -delay 20 -resize 300x200 -loop 0 *jpg animated.gif
Jun Wang

@Nick, Bagaimana Anda menjalankan kode itu untuk membuat GIF? Haruskah saya mengimpor sesuatu ke Spyder IDE?
BULAN

@ MOON perintah ImageMagic yang saya tambahkan di atas hanya dijalankan melalui baris perintah.
Nick

43

Pada Juni 2009, posting blog yang awalnya dikutip memiliki metode untuk membuat GIF animasi dalam komentar . Unduh skrip images2gif.py (sebelumnya images2gif.py , perbarui dari @geographika).

Kemudian, untuk membalikkan frame dalam gif, misalnya:

#!/usr/bin/env python

from PIL import Image, ImageSequence
import sys, os
filename = sys.argv[1]
im = Image.open(filename)
original_duration = im.info['duration']
frames = [frame.copy() for frame in ImageSequence.Iterator(im)]    
frames.reverse()

from images2gif import writeGif
writeGif("reverse_" + os.path.basename(filename), frames, duration=original_duration/1000.0, dither=0)

2
Ada versi baru dari skrip ini yang membuat output kualitas yang jauh lebih baik di visvis.googlecode.com/hg/vvmovie/images2gif.py dapat digunakan sebagai skrip mandiri terpisah dari paket.
geografi

1
Script yang disebutkan dalam komentar ini secara konsisten memberikan kesalahan segmentasi bagi saya ketika digunakan pada Mac, bahkan ketika dijalankan (menggunakan nama __ == '__ main ' contoh). Saya mencoba skrip yang disebutkan dalam jawaban, dengan harapan skrip ini akan berfungsi dengan baik. EDIT - Saya dapat mengonfirmasi bahwa skrip yang dirujuk dalam jawaban di atas berfungsi dengan benar di Mac saya.
scubbo

6
Daripada hanya mengunduh skrip gunakan pip misalnya pip install visvis, kemudian di skrip Anda from visvis.vvmovie.images2gif import writeGif.
Daniel Farrell

8
Saya mencoba ini dengan Python 2.7.3 di windows 8 dan saya mendapatkan UnicodeDecodeError: 'ascii' codec tidak dapat mendekode byte 0xc8 di posisi 6: ordinal tidak dalam jangkauan (128). Dari menjalankan python images2gif.py
reckoner

3
Saya penulis visivis (dan images2gif) dan merekomendasikan untuk tidak menggunakannya untuk tujuan ini. Saya telah mengerjakan solusi yang lebih baik sebagai bagian dari proyek imageio (lihat jawaban saya).
Almar

40

Berikut ini cara melakukannya dengan hanya menggunakan PIL (instal dengan :)pip install Pillow :

import glob
from PIL import Image

# filepaths
fp_in = "/path/to/image_*.png"
fp_out = "/path/to/image.gif"

# https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#gif
img, *imgs = [Image.open(f) for f in sorted(glob.glob(fp_in))]
img.save(fp=fp_out, format='GIF', append_images=imgs,
         save_all=True, duration=200, loop=0)

4
ini seharusnya jawaban yang diterima, terima kasih @Kris
ted930511

1
Apa yang dipegang oleh variabel tanda bintang ("* imgs")?
denisb411

3
Itu fitur bahasa python. Itu membongkar iterable . Anda kira-kira bisa menganggapnya sebagai membongkar x = [a, b, c]untuk *xyang dapat dianggap sebagai a, b, c(tanpa tanda kurung melampirkan). Dalam fungsi panggilan ini identik: f(*x) == f(a, b, c). Dalam membongkar tuple itu sangat berguna dalam kasus di mana Anda ingin membagi iterable menjadi kepala (elemen pertama) dan ekor (sisanya), yang adalah apa yang saya lakukan dalam contoh ini.
Kris

25

Saya menggunakan images2gif.py yang mudah digunakan. Tampaknya memang menggandakan ukuran file ..

26 file PNG 110kb, saya harapkan 26 * 110kb = 2860kb, tapi my_gif.GIF adalah 5.7mb

Juga karena GIF adalah 8bit, png yang bagus menjadi sedikit kabur di GIF

Berikut adalah kode yang saya gunakan:

__author__ = 'Robert'
from images2gif import writeGif
from PIL import Image
import os

file_names = sorted((fn for fn in os.listdir('.') if fn.endswith('.png')))
#['animationframa.png', 'animationframb.png', 'animationframc.png', ...] "

images = [Image.open(fn) for fn in file_names]

print writeGif.__doc__
# writeGif(filename, images, duration=0.1, loops=0, dither=1)
#    Write an animated gif from the specified images.
#    images should be a list of numpy arrays of PIL images.
#    Numpy images of type float should have pixels between 0 and 1.
#    Numpy images of other types are expected to have values between 0 and 255.


#images.extend(reversed(images)) #infinit loop will go backwards and forwards.

filename = "my_gif.GIF"
writeGif(filename, images, duration=0.2)
#54 frames written
#
#Process finished with exit code 0

Berikut adalah 3 dari 26 frame:

Berikut adalah 3 dari 26 frame

menyusut gambar mengurangi ukuran:

size = (150,150)
for im in images:
    im.thumbnail(size, Image.ANTIALIAS)

gif kecil


Saya membuat posting blog tentang ini .. robert-king.com/#post2-python-makes-gif
robert king

2
Saya mendapatkan kesalahan .. File "C: \ Python27 \ lib \ images2gif.py", baris 418, di writeGifToFile globalPalette = palet [terjadi.index (maks (muncul))] ValueError: max () arg adalah urutan kosong
Harry

terjadi mungkin kosong. File images2gif.py saya tidak memiliki variabel "globalPalette".
robert king

bagaimana cara mengubahnya? Saya menggunakan skrip images2gif.py terbaru di luar sana ( bit.ly/XMMn5h )
Harry

4
@robertking dengan kode saya mendapat pesan kesalahanfp.write(globalPalette) TypeError: must be string or buffer, not list
LWZ

19

Untuk membuat video, Anda dapat menggunakan OpenGL ,

#load your frames
frames = ...
#create a video writer
writer = cvCreateVideoWriter(filename, -1, fps, frame_size, is_color=1)
#and write your frames in a loop if you want
cvWriteFrame(writer, frames[i])

9

Saya menemukan posting ini dan tidak ada solusi yang bekerja, jadi inilah saya solusi yang berfungsi

Masalah dengan solusi lain sejauh ini:
1) Tidak ada solusi eksplisit tentang bagaimana durasi dimodifikasi
2) Tidak ada solusi untuk iterasi direktori out of order, yang penting untuk GIF
3) Tidak ada penjelasan tentang cara menginstal imageio untuk python 3

instal imageio seperti ini: python3 -m pip instal imageio

Catatan: Anda akan ingin memastikan frame Anda memiliki semacam indeks dalam nama file sehingga mereka dapat diurutkan, jika tidak Anda tidak akan tahu di mana GIF dimulai atau berakhir

import imageio
import os

path = '/Users/myusername/Desktop/Pics/' # on Mac: right click on a folder, hold down option, and click "copy as pathname"

image_folder = os.fsencode(path)

filenames = []

for file in os.listdir(image_folder):
    filename = os.fsdecode(file)
    if filename.endswith( ('.jpeg', '.png', '.gif') ):
        filenames.append(filename)

filenames.sort() # this iteration technique has no built in order, so sort the frames

images = list(map(lambda filename: imageio.imread(filename), filenames))

imageio.mimsave(os.path.join('movie.gif'), images, duration = 0.04) # modify duration as needed

1
sortmungkin menghasilkan hasil yang tidak terduga jika skema penomoran Anda tidak termasuk nol di depan. Juga mengapa Anda menggunakan peta alih-alih pemahaman daftar sederhana?
TIDAK

Saya sarankan untuk melakukanfilenames.append(os.path.join(path, filename))
trueter

Secodning Nohs, images = [imageio.imread(f) for f in filenames]lebih bersih, lebih cepat, dan lebih pythonic.
Brandon Dube

6

Seperti kata Warren tahun lalu , ini adalah pertanyaan lama. Karena orang masih melihat halaman, saya ingin mengarahkan mereka ke solusi yang lebih modern. Seperti yang dikatakan blakev di sini , ada contoh Bantal di github .

 import ImageSequence
 import Image
 import gifmaker
 sequence = []

 im = Image.open(....)

 # im is your original image
 frames = [frame.copy() for frame in ImageSequence.Iterator(im)]

 # write GIF animation
 fp = open("out.gif", "wb")
 gifmaker.makedelta(fp, frames)
 fp.close()

Catatan: Contoh ini sudah usang ( gifmakerbukan modul yang dapat diimpor, hanya skrip). Bantal memiliki GifImagePlugin (yang sumbernya ada di GitHub ), tetapi dokumen tentang ImageSequence tampaknya mengindikasikan dukungan terbatas (hanya baca)



5

Pertanyaan lama, banyak jawaban bagus, tapi mungkin masih ada minat pada alternatif lain ...

The numpngwmodul yang saya baru-baru memasang di github ( https://github.com/WarrenWeckesser/numpngw ) dapat menulis file PNG animasi dari array numpy. ( Pembaruan : numpngwsekarang di pypi: https://pypi.python.org/pypi/numpngw .)

Misalnya, skrip ini:

import numpy as np
import numpngw


img0 = np.zeros((64, 64, 3), dtype=np.uint8)
img0[:32, :32, :] = 255
img1 = np.zeros((64, 64, 3), dtype=np.uint8)
img1[32:, :32, 0] = 255
img2 = np.zeros((64, 64, 3), dtype=np.uint8)
img2[32:, 32:, 1] = 255
img3 = np.zeros((64, 64, 3), dtype=np.uint8)
img3[:32, 32:, 2] = 255
seq = [img0, img1, img2, img3]
for img in seq:
    img[16:-16, 16:-16] = 127
    img[0, :] = 127
    img[-1, :] = 127
    img[:, 0] = 127
    img[:, -1] = 127

numpngw.write_apng('foo.png', seq, delay=250, use_palette=True)

menciptakan:

animasi png

Anda akan memerlukan browser yang mendukung PNG animasi (baik secara langsung atau dengan plugin) untuk melihat animasi.


Chrome sekarang juga, BTW. Satu pertanyaan - bisakah seq menjadi pilihan? Apakah Anda mendukung "streaming" (yaitu membuka target APNG, dan menambahkan bingkai satu per satu dalam satu lingkaran)?
Tomasz Gandor

Itu tidak mendukung iterable atau streaming yang sewenang-wenang, tetapi itu tidak berarti tidak bisa di masa depan. :) Buat masalah pada halaman github dengan peningkatan yang diusulkan. Jika Anda memiliki ide tentang API untuk fitur ini, tolong jelaskan mereka dalam masalah ini.
Warren Weckesser

Saya memiliki beberapa kesalahan aneh yang membuat masalah pada repo Anda.
mLstudent33

5

Seperti salah satu anggota yang disebutkan di atas, imageio adalah cara terbaik untuk melakukan ini. imageio juga memungkinkan Anda untuk mengatur frame rate, dan saya benar-benar menulis sebuah fungsi dengan Python yang memungkinkan Anda untuk mengatur penahanan pada frame terakhir. Saya menggunakan fungsi ini untuk animasi ilmiah di mana perulangan berguna tetapi restart segera tidak. Inilah tautan dan fungsinya:

Cara membuat GIF menggunakan Python

import matplotlib.pyplot as plt
import os
import imageio

def gif_maker(gif_name,png_dir,gif_indx,num_gifs,dpi=90):
    # make png path if it doesn't exist already
    if not os.path.exists(png_dir):
        os.makedirs(png_dir)

    # save each .png for GIF
    # lower dpi gives a smaller, grainier GIF; higher dpi gives larger, clearer GIF
    plt.savefig(png_dir+'frame_'+str(gif_indx)+'_.png',dpi=dpi)
    plt.close('all') # comment this out if you're just updating the x,y data

    if gif_indx==num_gifs-1:
        # sort the .png files based on index used above
        images,image_file_names = [],[]
        for file_name in os.listdir(png_dir):
            if file_name.endswith('.png'):
                image_file_names.append(file_name)       
        sorted_files = sorted(image_file_names, key=lambda y: int(y.split('_')[1]))

        # define some GIF parameters

        frame_length = 0.5 # seconds between frames
        end_pause = 4 # seconds to stay on last frame
        # loop through files, join them to image array, and write to GIF called 'wind_turbine_dist.gif'
        for ii in range(0,len(sorted_files)):       
            file_path = os.path.join(png_dir, sorted_files[ii])
            if ii==len(sorted_files)-1:
                for jj in range(0,int(end_pause/frame_length)):
                    images.append(imageio.imread(file_path))
            else:
                images.append(imageio.imread(file_path))
        # the duration is the time spent on each image (1/duration is frame rate)
        imageio.mimsave(gif_name, images,'GIF',duration=frame_length)

Contoh GIF menggunakan metode ini



4

Dengan windows7, python2.7, opencv 3.0, berikut ini berfungsi untuk saya:

import cv2
import os

vvw           =   cv2.VideoWriter('mymovie.avi',cv2.VideoWriter_fourcc('X','V','I','D'),24,(640,480))
frameslist    =   os.listdir('.\\frames')
howmanyframes =   len(frameslist)
print('Frames count: '+str(howmanyframes)) #just for debugging

for i in range(0,howmanyframes):
    print(i)
    theframe = cv2.imread('.\\frames\\'+frameslist[i])
    vvw.write(theframe)

3

Hal termudah yang membuatnya berfungsi bagi saya adalah memanggil perintah shell dengan Python.

Jika gambar Anda disimpan seperti dummy_image_1.png, dummy_image_2.png ... dummy_image_N.png, maka Anda dapat menggunakan fungsi ini:

import subprocess
def grid2gif(image_str, output_gif):
    str1 = 'convert -delay 100 -loop 1 ' + image_str  + ' ' + output_gif
    subprocess.call(str1, shell=True)

Eksekusi saja:

grid2gif("dummy_image*.png", "my_output.gif")

Ini akan membuat file gif Anda, my_output.gif.


2

Tugas dapat diselesaikan dengan menjalankan skrip python dua baris dari folder yang sama dengan urutan file gambar. Untuk file yang diformat png, skripnya adalah -

from scitools.std import movie
movie('*.png',fps=1,output_file='thisismygif.gif')

1
Sudah mencobanya ... tidak bekerja untuk saya di bawah Python 2.6. Dikembalikan: "fungsi scitools.easyviz.movie menjalankan perintah: / convert -delay 100 g4testC _ *. Png g4testC.gif / Parameter Tidak Valid - 100"
Dan H

Masalahnya bukan dengan Python pasti. Instal ulang imagemagick pada sistem Anda dan coba lagi.
ArKE

2

Saya sedang mencari kode baris tunggal dan menemukan berikut ini berfungsi untuk aplikasi saya. Inilah yang saya lakukan:

Langkah Pertama: Instal ImageMagick dari tautan di bawah ini

https://www.imagemagick.org/script/download.php

masukkan deskripsi gambar di sini

Langkah Kedua: Arahkan baris cmd ke folder tempat gambar (dalam kasus .png format saya) ditempatkan

masukkan deskripsi gambar di sini

Langkah Ketiga: Ketikkan perintah berikut

magick -quality 100 *.png outvideo.mpeg

masukkan deskripsi gambar di sini

Terima kasih FogleBird untuk idenya!


0

Saya baru saja mencoba yang berikut dan sangat berguna:

Pertama Unduh perpustakaan Figtodatdan images2gifke direktori lokal Anda.

Kedua, Kumpulkan angka-angka dalam array dan konversikan menjadi animasi gif:

import sys
sys.path.insert(0,"/path/to/your/local/directory")
import Figtodat
from images2gif import writeGif
import matplotlib.pyplot as plt
import numpy

figure = plt.figure()
plot   = figure.add_subplot (111)

plot.hold(False)
    # draw a cardinal sine plot
images=[]
y = numpy.random.randn(100,5)
for i in range(y.shape[1]):
    plot.plot (numpy.sin(y[:,i]))  
    plot.set_ylim(-3.0,3)
    plot.text(90,-2.5,str(i))
    im = Figtodat.fig2img(figure)
    images.append(im)

writeGif("images.gif",images,duration=0.3,dither=0)

0

Saya menemukan modul ImageSequence PIL , yang menawarkan informasi GIF yang lebih baik (dan lebih standar). Saya juga menggunakan metode after () Tk kali ini, yang lebih baik daripada time.sleep () .

from Tkinter import * 
from PIL import Image, ImageTk, ImageSequence

def stop(event):
  global play
  play = False
  exit() 

root = Tk()
root.bind("<Key>", stop) # Press any key to stop
GIFfile = {path_to_your_GIF_file}
im = Image.open(GIFfile); img = ImageTk.PhotoImage(im)
delay = im.info['duration'] # Delay used in the GIF file 
lbl = Label(image=img); lbl.pack() # Create a label where to display images
play = True;
while play:
  for frame in ImageSequence.Iterator(im):
    if not play: break 
    root.after(delay);
    img = ImageTk.PhotoImage(frame)
    lbl.config(image=img); root.update() # Show the new frame/image

root.mainloop()

0

Fungsi sederhana yang membuat GIF:

import imageio
import pathlib
from datetime import datetime


def make_gif(image_directory: pathlib.Path, frames_per_second: float, **kwargs):
    """
    Makes a .gif which shows many images at a given frame rate.
    All images should be in order (don't know how this works) in the image directory

    Only tested with .png images but may work with others.

    :param image_directory:
    :type image_directory: pathlib.Path
    :param frames_per_second:
    :type frames_per_second: float
    :param kwargs: image_type='png' or other
    :return: nothing
    """
    assert isinstance(image_directory, pathlib.Path), "input must be a pathlib object"
    image_type = kwargs.get('type', 'png')

    timestampStr = datetime.now().strftime("%y%m%d_%H%M%S")
    gif_dir = image_directory.joinpath(timestampStr + "_GIF.gif")

    print('Started making GIF')
    print('Please wait... ')

    images = []
    for file_name in image_directory.glob('*.' + image_type):
        images.append(imageio.imread(image_directory.joinpath(file_name)))
    imageio.mimsave(gif_dir.as_posix(), images, fps=frames_per_second)

    print('Finished making GIF!')
    print('GIF can be found at: ' + gif_dir.as_posix())


def main():
    fps = 2
    png_dir = pathlib.Path('C:/temp/my_images')
    make_gif(png_dir, fps)

if __name__ == "__main__":
    main()

0

Saya mengerti Anda bertanya tentang mengonversi gambar ke gif; namun, jika format aslinya adalah MP4, Anda dapat menggunakan FFmpeg :

ffmpeg -i input.mp4 output.gif

-1

Benar-benar luar biasa ... Semua mengusulkan beberapa paket khusus untuk memainkan GIF animasi, saat ini dapat dilakukan dengan Tkinter dan modul PIL klasik!

Ini adalah metode animasi GIF saya sendiri (saya buat beberapa waktu lalu). Sangat sederhana:

from Tkinter import * 
from PIL import Image, ImageTk
from time import sleep

def stop(event):
  global play
  play = False
  exit() 

root = Tk()
root.bind("<Key>", stop) # Press any key to stop
GIFfile = {path_to_your_GIF_file}    
im = Image.open(GIFfile); img = ImageTk.PhotoImage(im)
delay = float(im.info['duration'])/1000; # Delay used in the GIF file 
lbl = Label(image=img); lbl.pack() # Create a label where to display images
play = True; frame = 0
while play:
  sleep(delay);
  frame += 1
  try:
    im.seek(frame); img = ImageTk.PhotoImage(im)
    lbl.config(image=img); root.update() # Show the new frame/image
  except EOFError:
    frame = 0 # Restart

root.mainloop()

Anda dapat mengatur cara Anda sendiri untuk menghentikan animasi. Beri tahu saya jika Anda ingin mendapatkan versi lengkap dengan tombol putar / jeda / keluar.

Catatan: Saya tidak yakin apakah frame berturut-turut dibaca dari memori atau dari file (disk). Dalam kasus kedua akan lebih efisien jika mereka membaca sekaligus dan disimpan ke dalam array (daftar). (Saya tidak begitu tertarik untuk mencari tahu! :)


1
Biasanya bukan ide yang baik untuk memanggil sleeputas utama GUI. Anda dapat menggunakan aftermetode ini untuk memanggil suatu fungsi secara berkala.
Bryan Oakley

Anda benar, tetapi ini bukan intinya sama sekali, bukan? Intinya adalah keseluruhan metode. Jadi, saya lebih suka mengharapkan reaksi terhadap itu!
Apostolos

1
Saya hanya mencoba memberikan saran tentang cara meningkatkan jawaban Anda.
Bryan Oakley

BTW, saya biasanya menggunakan tk.after()sendiri. Tapi di sini saya perlu membuat kode sesederhana mungkin. Siapa pun yang menggunakan metode animasi GIF ini dapat menerapkan fungsi penundaannya sendiri.
Apostolos

Akhirnya! Ya, ini memang poin yang sangat bagus. Saya di luar topik! Terima kasih, @Novel. (Menarik untuk melihat bagaimana orang lain melewatkan ini, misalnya Bryan, yang berbicara tentang metode waktu tunda!)
Apostolos
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.