Created
December 10, 2021 16:21
-
-
Save Sasszem/d3b6973fb4a1d30ae19a0cbcdcf5df97 to your computer and use it in GitHub Desktop.
Create "fake-preview" images (similar to this one: https://www.reddit.com/r/rickroll/comments/ls99p2/wheres_waldo/). Requires the Pillow package!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import struct | |
import zlib | |
from PIL import Image | |
import click | |
import io | |
GAMMA = 0.08 | |
DESAT_RATE = 0.4 | |
################################################################################ | |
# FIRST PHASE - GENERATING THE MIXED IMAGE (AND OVERBRIGTENING THE HIDDEN ONE) # | |
################################################################################ | |
def desaturate(col, rate): | |
r, g, b = col[0]/255, col[1]/255, col[2]/255 | |
r = 0.5 + (r-0.5)*rate | |
g = 0.5 + (g-0.5)*rate | |
b = 0.5 + (b-0.5)*rate | |
r = int(255*r) | |
g = int(255*g) | |
b = int(255*b) | |
return (r, g, b, 255) | |
def gamma_adjust(col, gamma): | |
"""Brightening of the image""" | |
r, g, b = col[0], col[1], col[2] | |
invgamma = 1/gamma | |
r = int(255*(r/255)**invgamma) | |
g = int(255*(g/255)**invgamma) | |
b = int(255*(b/255)**invgamma) | |
return (r, g, b, 255) | |
def make_combined_image(foreground, background, gamma, desat_rate): | |
with Image.open("inp_1_res.png") as inp1, Image.open("inp_2_res.png") as inp2: | |
n = Image.new("RGBA", inp1.size, (0, 0, 0, 255)) | |
w, h = n.size | |
for x in range(w): | |
for y in range(h): | |
if x % 2 == 0 and y % 2 == 0: | |
n.putpixel((x, y), gamma_adjust( | |
desaturate(inp1.getpixel((x, y)), 1), 0.5/gamma)) | |
else: | |
if x < inp2.size[0] and y < inp2.size[1]: | |
n.putpixel((x, y), gamma_adjust(desaturate( | |
inp2.getpixel((x, y)), desat_rate), 1)) | |
outfile = io.BytesIO() | |
n.save(outfile, "png") | |
outfile.seek(0) | |
return outfile | |
############################################################ | |
# SECOND PHASE - ADDING EXTRA IMAGE INFORMATION TO THE PNG # | |
############################################################ | |
def read_chunk(f): | |
"""Read a single PNG chunk from binary file""" | |
length = struct.unpack(">I", f.read(4))[0] | |
type = f.read(4).decode() | |
print(f"{length=}, {type=}") | |
data = f.read(length) | |
assert f.read(4) == zlib.crc32( | |
type.encode()+data).to_bytes(4, "big"), "CRC is not valid" | |
return type, data | |
def get_png_chunks(file): | |
chunks = [] | |
assert list(file.read(8)) == [137, 80, 78, 71, | |
13, 10, 26, 10], "PNG signature is not valid" | |
chunktype = "" | |
while chunktype != "IEND": | |
chunktype, data = read_chunk(file) | |
chunks.append((chunktype, data)) | |
return chunks | |
def write_png(chunks, file): | |
file.write(bytes([137, 80, 78, 71, 13, 10, 26, 10])) | |
for type, data in chunks: | |
file.write(struct.pack(">I", len(data))+type.encode()+data + | |
struct.pack(">I", zlib.crc32(type.encode()+data))) | |
@click.command() | |
@click.argument('hidden', type=click.Path(exists=True)) | |
@click.argument('fake', type=click.Path(exists=True)) | |
@click.argument('output', type=click.Path(exists=False)) | |
@click.option('-g', '--gamma', type=float, default=GAMMA) | |
@click.option('-d', '--desat', "desat_rate", type=float, default=DESAT_RATE) | |
def main(hidden, fake, output, gamma, desat_rate): | |
combined = make_combined_image(hidden, fake, gamma, desat_rate) | |
chunks = get_png_chunks(combined) | |
gamma_chunk = ("gAMA", int(100000*gamma).to_bytes(4, "big")) | |
chunks.insert(1, gamma_chunk) | |
with open(output, "wb") as f: | |
write_png(chunks, f) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment