Skip to content

Commit

Permalink
Update to 0.0.8
Browse files Browse the repository at this point in the history
  • Loading branch information
TotallyNotChase committed Feb 24, 2020
1 parent afc425b commit 15a1e05
Show file tree
Hide file tree
Showing 5 changed files with 304 additions and 53 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,13 @@
* 0.0.7.1 -> Fix a script breaking bug

* 0.0.7.2 -> Fix missing frames argument

* 0.0.8 -> **MAJOR CHANGES**

Add support for `Image` object input, now you can directly pass an `Image` object to the library!

Add support for glitching GIFs to GIFs, That's right! You can now input a GIF to turn it into a **GLITCHED GIF**!

Cleanup code

Add more docs
44 changes: 32 additions & 12 deletions glitch_this/commandline.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from time import time
from glitch_this import ImageGlitcher

def is_uptodate(version):
def islatest(version):
from urllib import request
import json
# Check pypi for the latest version number\
Expand All @@ -19,7 +19,27 @@ def is_uptodate(version):

return version == latest_version

def glitch_script(src_img_path, glitch_level, scan_lines, color, gif, frames, duration):
def glitch_gif(src_img_path, glitch_level, scan_lines, color, duration):
t0 = time()
# Fetching image attributes
img_path, img_file = os.path.split(Path(src_img_path))
img_filename, img_fileex = img_file.rsplit('.', 1)
full_path = os.path.join(img_path, 'glitched_{}.gif'.format(img_filename))

# Glitching begins here
glitcher = ImageGlitcher()
glitch_imgs = glitcher.glitch_gif(src_img_path, glitch_level, scan_lines=scan_lines, color_offset=color)
glitch_imgs[0].save(full_path,
format='GIF',
append_images=glitch_imgs[1:],
save_all=True,
duration=duration,
loop=0)
t1 = time()
print('Glitched GIF saved in "{}"\nDuration = {}'.format(full_path, duration))
print('Time taken: ' + str(t1 - t0))

def glitch_image(src_img_path, glitch_level, scan_lines, color, gif, frames, duration):
t0 = time()
# Fetching image attributes
img_path, img_file = os.path.split(Path(src_img_path))
Expand Down Expand Up @@ -58,28 +78,28 @@ def main():
argparser.add_argument('-s', '--scan', dest='scan_lines', action='store_true',
help='Whether or not to add scan lines effect, defaults to False')
argparser.add_argument('-g', '--gif', dest='gif', action='store_true',
help='Include if you want a GIF instead of static image')
help='Include if you want a GIF instead of static image\nNOTE: Does nothing if input image is GIF, i.e when using `-ig`')
argparser.add_argument('-f', '--frames', dest='frames', type=int, default=23,
help='How many frames to include in GIF, defaults to 23')
help='How many frames to include in GIF, defaults to 23\nNOTE: Does nothing if input image is GIF, i.e when using `-ig`')
argparser.add_argument('-d', '--duration', dest='duration', type=int, default=200,
help='How long to display each frame (in centiseconds), defaults to 200')
argparser.add_argument('-ig', '--inputgif', dest='input_gif', action='store_true',
help='If input image is GIF, use for glitching GIFs to GIFs!\
Defaults to False\nNOTE: This is a slow process')
args = argparser.parse_args()

# Sanity checking the inputs
if not 1 <= args.glitch_level <= 10:
raise ValueError('glitch_amount parameter must be in range 1 to 10, inclusive')
if not os.path.exists(args.src_img_path):
raise FileNotFoundError('No image found at given path')
if not args.frames > 1:
raise ValueError('Frames must be greather than 1')
if not args.duration > 0:
raise ValueError('Duration must be greather than 0')

# Call the actual script
glitch_script(args.src_img_path, args.glitch_level, args.scan_lines, args.color, args.gif, args.frames, args.duration)
if not args.input_gif:
glitch_image(args.src_img_path, args.glitch_level, args.scan_lines, args.color, args.gif, args.frames, args.duration)
else:
glitch_gif(args.src_img_path, args.glitch_level, args.scan_lines, args.color, args.duration)

# Let the user know if new version is available
if not is_uptodate(ImageGlitcher.__version__):
if not islatest(ImageGlitcher.__version__):
print('A new version of "glitch-this" is available. Please consider upgrading via `pip install --upgrade glitch-this`')

if __name__=='__main__':
Expand Down
177 changes: 153 additions & 24 deletions glitch_this/glitch_this.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import os, shutil
import numpy as np
from random import randint
from PIL import Image
from PIL import Image, ImageSequence

class ImageGlitcher:
# Handles Image/GIF Glitching Operations

__version__ = '0.0.7.2'
__version__ = '0.0.8'

def __init__(self):
# Setting up global variables needed for glitching
Expand All @@ -18,55 +18,184 @@ def __init__(self):
self.inputarr = None
self.outputarr = None

def glitch_image(self, src_img_path, glitch_amount, color_offset=False, scan_lines=False, gif=False, frames=23):
# Getting PATH of temp folders
self.lib_path = os.path.split(os.path.abspath(__file__))[0]
self.gif_dirpath = os.path.join(self.lib_path, 'Glitched GIF')

def isgif(self, img):
# Returns true if input image is a GIF and/or animated
if isinstance(img, str):
return img.endswith('.gif')
index = 0
for frame in ImageSequence.Iterator(img):
# More than one frames means image is animated
index += 1
if index >= 2:
return True
return False

def open_image(self, img_path):
# Returns an Image object
# Will throw exception if img_path doesn't point to Image
if img_path.endswith('.gif'):
# Do not convert GIF file
return Image.open(img_path)
elif img_path.endswith('.png'):
# Convert the Image to RGBA if it's png
return Image.open(img_path).convert('RGBA')
else:
# Otherwise convert it to RGB
return Image.open(img_path).convert('RGB')

def fetch_image(self, src_img, gif_allowed):
"""
The following code resolves whether input was a path or an Image
Then returns an Image object
Raises an exception if `img` param is not an Image
"""
if isinstance(src_img, str) and (gif_allowed or not src_img.endswith('.gif')):
"""
An str object was passed
If GIF is not allowed and the Image path is a GIF
the function will raise an Exception
If GIF is allowed, any Image path is good to go
"""
# Sanity Check if the path exists
if not os.path.exists(src_img):
raise FileNotFoundError('No image found at given path')
try:
# Open the image at given path
img = self.open_image(src_img)
except:
# File is not an Image
raise Exception('Wrong format')
elif isinstance(src_img, Image.Image) and (gif_allowed or not self.isgif(src_img)):
"""
An Image object was passed
If GIF is not allowed and the Image object is a GIF
the function will raise an Exception
If GIF is allowed, any Image object is good to go
"""
if src_img.format == 'GIF':
# Do not convert GIF file
return src_img
elif src_img.format == 'PNG':
# Convert the Image to RGBA if it's png
img = src_img.convert('RGBA')
else:
# Otherwise convert it to RGB
img = src_img.convert('RGB')
else:
# File is not an Image
# OR it's a GIF but GIF is not allowed

# Raise the GENERIC exception here
raise Exception('Wrong format')
return img

def glitch_image(self, src_img, glitch_amount, color_offset=False, scan_lines=False, gif=False, frames=23):
"""
Sets up values needed for glitching the image
Returns created Image object
Returns created Image object if gif=False
Returns a list of PngImage objects if gif=True
PARAMETERS:-
src_img: Either the path to input Image or an Image object itself
glitch_amount: Level of glitch intensity, [1, 10] (inclusive)
color_offset: Specify True if color_offset effect should be applied
scan_lines: Specify True if scan_lines effect should be applied
gif: True if output should be ready to be saved as GIF
frames: How many glitched frames should be generated for GIF
"""
# Sanity checking the inputs
if not 1 <= glitch_amount <= 10:
raise ValueError('glitch_amount parameter must be in range 1 to 10, inclusive')
if not os.path.exists(src_img_path):
raise FileNotFoundError('No image found at given path')

try:
if src_img_path.endswith('.png'):
src_img = Image.open(src_img_path).convert('RGBA')
else:
src_img = Image.open(src_img_path).convert('RGB')
# Get Image, whether input was an str path or Image object
# GIF input is NOT allowed in this method
img = self.fetch_image(src_img, False)
except:
raise Exception('File format not supported - must be an image file')
# Throw DETAILED exception here (Traceback will be present from previous exceptions)
raise Exception('File format not supported - must be a non-animated image file')

# Fetching image attributes
self.pixel_tuple_len = len(src_img.getbands())
self.img_width, self.img_height = src_img.size
self.img_mode = src_img.mode
self.pixel_tuple_len = len(img.getbands())
self.img_width, self.img_height = img.size
self.img_mode = img.mode

# Assigning the 3D arrays with pixel data
self.inputarr = np.asarray(src_img)
self.outputarr = np.array(src_img)
self.inputarr = np.asarray(img)
self.outputarr = np.array(img)

# Glitching begins here
if not gif:
# Return glitched image
return self.get_glitched_img(glitch_amount, color_offset, scan_lines)
return self.get_glitched_img(glitch_amount, color_offset, scan_lines)

# Return glitched GIF
# Set up directory for storing glitched images
lib_path = os.path.split(os.path.abspath(__file__))[0]
gif_dirpath = os.path.join(lib_path, 'Glitched GIF')
if os.path.isdir(gif_dirpath):
shutil.rmtree(gif_dirpath)
os.mkdir(gif_dirpath)
if os.path.isdir(self.gif_dirpath):
shutil.rmtree(self.gif_dirpath)
os.mkdir(self.gif_dirpath)

glitched_imgs = []
for i in range(frames):
glitched_img = self.get_glitched_img(glitch_amount, color_offset, scan_lines)
file_path = os.path.join(gif_dirpath, 'glitched_{}.png'.format(str(i)))
file_path = os.path.join(self.gif_dirpath, 'glitched_{}.png'.format(str(i)))
glitched_img.save(file_path)
glitched_imgs.append(Image.open(file_path))
return glitched_imgs

def glitch_gif(self, src_gif, glitch_amount, color_offset=False, scan_lines=False):
"""
Glitch each frame of input GIF
Returns a list of PngImage objects if gif=True
NOTE: This is a time consuming process, especially for large GIFs
with many frames
PARAMETERS:-
src_img: Either the path to input Image or an Image object itself
glitch_amount: Level of glitch intensity, [1, 10] (inclusive)
color_offset: Specify True if color_offset effect should be applied
scan_lines: Specify True if scan_lines effect should be applied
"""
# Sanity checking the inputs
if not 1 <= glitch_amount <= 10:
raise ValueError('glitch_amount parameter must be in range 1 to 10, inclusive')
if not self.isgif(src_gif):
raise Exception('Input image must be a path to a GIF or be a GIF Image object')

try:
# Get Image, whether input was an str path or Image object
# GIF input is allowed in this method
gif = self.fetch_image(src_gif, True)
except:
# Throw DETAILED exception here (Traceback will be present from previous exceptions)
raise Exception('File format not supported - must be an image file')

# Set up directory for storing glitched images
if os.path.isdir(self.gif_dirpath):
shutil.rmtree(self.gif_dirpath)
os.mkdir(self.gif_dirpath)

i = 0
glitched_imgs = []
for frame in ImageSequence.Iterator(gif):
file_path = os.path.join(self.gif_dirpath, 'frame.png')
frame.save(file_path)
glitched_img = self.glitch_image(file_path, glitch_amount, color_offset, scan_lines)
file_path = os.path.join(self.gif_dirpath, 'glitched_{}.png'.format(str(i)))
glitched_img.save(file_path)
glitched_imgs.append(Image.open(file_path))
i += 1
return glitched_imgs

def get_glitched_img(self, glitch_amount, color_offset, scan_lines):
"""
Glitches the image located at given path
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setup(
name='glitch_this',
version='0.0.7.2',
version='0.0.8',
author='TotallyNotChase',
author_email='44284917+TotallyNotChase@users.noreply.github.com',
description='A package to glitch images',
Expand Down
Loading

0 comments on commit 15a1e05

Please sign in to comment.