Created
February 3, 2023 19:30
-
-
Save flxai/497ce622ad6c28cc08da007fdd91ba6c to your computer and use it in GitHub Desktop.
Recreate Salavon's piece 'Every Playboy Centerfold, The 1970s (normalized)' from 2002
This file contains hidden or 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
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"id": "90f14a7f-5f5a-4c92-bc5d-2e779aecd7bf", | |
"metadata": {}, | |
"source": [ | |
"# Recreate Salavon's piece 'Every Playboy Centerfold, The 1970s (normalized)' from 2002\n", | |
"This notebook recreates [Salavon's piece](http://salavon.com/work/everyplayboycenterfolddecades/image/216/), although not faithfully.\n", | |
"Please also see the slides of the talk that goes along with it:\n", | |
"\n", | |
"[https://s.flx.ai/2022/digital-art-forgery/](https://s.flx.ai/2022/digital-art-forgery/)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "b69f4596-fb6b-4a7c-a329-7dac1ab26b3f", | |
"metadata": {}, | |
"source": [ | |
"## Imports" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "4799c2da-4c24-4cb1-abe9-2e2271d1dcf4", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"import regex as re\n", | |
"import pylab as plt\n", | |
"import pandas as pd\n", | |
"\n", | |
"from glob import glob\n", | |
"from IPython import display\n", | |
"from pathlib import Path\n", | |
"from PIL import Image\n", | |
"\n", | |
"from tqdm.auto import tqdm" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "6758c2c0-187e-4105-847e-4a0d79d2952e", | |
"metadata": {}, | |
"source": [ | |
"## Settings" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "6f9bdd68-ae9f-4cd3-90af-bbf9a400f1c8", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Download centerfolds first, e.g. from:\n", | |
"# https://www.1377x.to/torrent/2758469/Playboy-Centerfolds-Ultra-High-Quality-The-Full-1953-2015-Year/" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "6f9bdd68-ae9f-4cd3-90af-bbf9a400f1c8", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"data_dir = 'data/centerfolds'" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "4ad567f9-f4d7-4006-818a-acb169c76028", | |
"metadata": {}, | |
"source": [ | |
"## Extract metadata" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "0914be0a-d041-43d4-bc0f-dacecc1a7b6d", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# img_files = sorted(glob(f'{data_dir}/**/*.jpg'))\n", | |
"img_files = sorted(glob(f'{data_dir}/*197?*/*.jpg'))\n", | |
"# img_files = sorted(glob(f'{data_dir}/*198?*/*.jpg'))\n", | |
"albums = list(set([\n", | |
" Path(c).parent.stem\n", | |
" for c in img_files\n", | |
"]))\n", | |
"print(f\"Found {len(img_files)} number of images. This relates to {len(albums)} albums.\")" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "1f598997-725a-494a-8eee-23a41993fae2", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"images = []\n", | |
"widths = []\n", | |
"heights = []\n", | |
"for img_file in tqdm(img_files):\n", | |
" with Image.open(img_file) as im:\n", | |
" width, height = im.size\n", | |
" widths.append(width)\n", | |
" heights.append(height)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "1ad6f340-e69f-4886-a9d9-f0bce3144350", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"regex_year = re.compile(r'([0-9]{4})')\n", | |
"regex_month = re.compile(r'([0-9]{2})_')\n", | |
"data = {\n", | |
" 'file_name': [Path(f).stem for f in img_files],\n", | |
" 'file_path': [Path(f) for f in img_files],\n", | |
" 'width': widths,\n", | |
" 'height': heights,\n", | |
" # 'album': [Path(f).parent.stem for f in img_files],\n", | |
" 'year': [re.findall(regex_year, Path(f).parent.stem)[0] for f in img_files],\n", | |
" 'month': [re.findall(regex_month, Path(f).stem)[0] for f in img_files],\n", | |
"}\n", | |
"df = pd.DataFrame(columns=data.keys(), data=data)\n", | |
"model_names = df['file_name'].str.extract(r'([A-Z][a-z]+)[ _]?([A-Z][a-z]+)(?:_CF)?$')\n", | |
"df['model_name'] = model_names[0]\n", | |
"df['model_surname'] = model_names[1]\n", | |
"df = df.drop('file_name', axis=1)\n", | |
"# Select only portrait formats\n", | |
"len_df = len(df)\n", | |
"df = df[df.width < df.height]\n", | |
"print(f'Removed {len_df - len(df)} images in landscape format.')\n", | |
"# Reindex\n", | |
"df = df.reset_index()\n", | |
"# Sort columns \n", | |
"df = df[['year', 'month', 'model_name', 'model_surname', 'width', 'height', 'file_path']]\n", | |
"df" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "4691bf09-06f7-4d3d-beff-e30c94c86e52", | |
"metadata": {}, | |
"source": [ | |
"## Select images by resolution" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "d1dc4246-675b-477c-887f-89cb40eedf62", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"import tabulate\n", | |
"\n", | |
"from IPython.display import display, HTML, Markdown" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "4845fda5-adb7-4085-9672-35481c91d4e7", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"min_w = df.width.min()\n", | |
"min_h = df.height.min()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "bcb79942-6fc1-4e3b-af4f-604023a86d2f", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"bin_count = 50\n", | |
"mode_w = df.width.mode()[0]\n", | |
"mode_h = df.height.mode()[0]" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "96f4e9ab-bc59-4e39-b447-4da419b68fee", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"len_min_or = len(df[(df.height >= min_h) | (df.width >= min_w)])\n", | |
"len_min_and = len(df[(df.height >= min_h) & (df.width >= min_w)])\n", | |
"len_mode_or = len(df[(df.height >= mode_h) | (df.width >= mode_w)])\n", | |
"len_mode_and = len(df[(df.height >= mode_h) & (df.width >= mode_w)])" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "e89c9944-7bc6-461b-addc-56708f1dd521", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"table_headers = [\"Selector\", \"Gate\", \"Width\", \"Height\", \"Selected\", \"Total\"]\n", | |
"table_data = [\n", | |
" [\"Min\", \"OR\", f\"{min_w}\", f\"{min_h}\", f\"{len_min_or}\", f\"{len(df)}\"],\n", | |
" [\"Min\", \"AND\", f\"{min_w}\", f\"{min_h}\", f\"{len_min_and}\", f\"{len(df)}\"],\n", | |
" [\"Mode\", \"OR\", f\"{mode_w}\", f\"{mode_h}\", f\"{len_mode_or}\", f\"{len(df)}\"],\n", | |
" [\"Mode\", \"AND\", f\"{mode_w}\", f\"{mode_h}\", f\"{len_mode_and}\", f\"{len(df)}\"],\n", | |
"]\n", | |
"table = tabulate.tabulate(table_data, headers=table_headers, tablefmt='html')\n", | |
"table" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "a648d767-f109-44e6-8d05-3258b13bee12", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"fig, axs = plt.subplots(1, 2)\n", | |
"axs[0].hist(df.width, bins=bin_count)\n", | |
"axs[0].set_title(\"Width\")\n", | |
"axs[0].axvline(mode_w, label=f\"Mode @ {mode_w}\", color='C1')\n", | |
"axs[0].axvline(min_w, label=f\"Min @ {min_w}\", color='C2')\n", | |
"axs[0].legend()\n", | |
"axs[1].hist(df.height, bins=bin_count)\n", | |
"axs[1].set_title(\"Height\")\n", | |
"axs[1].axvline(mode_h, label=f\"Mode @ {mode_h}\", color='C1')\n", | |
"axs[1].axvline(min_h, label=f\"Min @ {min_h}\", color='C2')\n", | |
"axs[1].legend()\n", | |
"plt.suptitle(\"Occurences of image sizes per axis\")\n", | |
"\n", | |
"display(HTML(table))\n", | |
"plt.show()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "5e9a62eb-4a25-4f88-a5cd-68b707e79e73", | |
"metadata": {}, | |
"source": [ | |
"### Get common image ratio" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "a0fdbd19-0889-42b7-9f68-1965ad1c5ca9", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"from fractions import Fraction" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "24ebb133-2c46-48a2-a983-0b842153242c", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"mode_ratio = Fraction(mode_w, mode_h)\n", | |
"ratio_w, ratio_h = mode_ratio.numerator, mode_ratio.denominator" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "e9902fbe-1064-485b-89e4-08bf5dba7ab3", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"display(Markdown(f\"(ℹ) Images are commonly shot in {ratio_w}:{ratio_h} format.\"))" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"id": "6e298daa-59f9-4c8f-b25a-866bc01328dd", | |
"metadata": {}, | |
"source": [ | |
"## Pre-process images" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "f7219bbc-e3a3-4d2c-a146-252e03be2782", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def load_image_size(row, mode_w, mode_h, ratio_w, ratio_h):\n", | |
" ratio = ratio_w / ratio_h\n", | |
" fn = Path(row.file_path).stem\n", | |
" im = Image.open(row.file_path)\n", | |
" w, h = im.size\n", | |
" r = w / h\n", | |
" \n", | |
" # Rescale if size does not fit\n", | |
" if w != mode_w or h != mode_h:\n", | |
" assert ratio != r\n", | |
" # Select side depending on ratio\n", | |
" if ratio > r:\n", | |
" # Image taller, scale width\n", | |
" new_h = int(mode_w / r)\n", | |
" im = im.resize((mode_w, new_h))\n", | |
" w, h = im.size\n", | |
" # Crop height\n", | |
" crop_h = h - mode_h\n", | |
" crop_top = int(crop_h // 2)\n", | |
" crop_bottom = crop_top if crop_h % 2 == 0 else crop_top + 1\n", | |
" im = im.crop((0, crop_top, w, h - crop_bottom))\n", | |
" elif ratio < r:\n", | |
" # Image wider, scale height\n", | |
" new_w = int(mode_h * r)\n", | |
" im = im.resize((new_w, mode_h))\n", | |
" w, h = im.size\n", | |
" # Crop width\n", | |
" crop_w = w - mode_w\n", | |
" crop_left = int(crop_w // 2)\n", | |
" crop_right = crop_left if crop_w % 2 == 0 else crop_left + 1\n", | |
" im = im.crop((crop_left, 0, w - crop_right, h))\n", | |
" else:\n", | |
" print(f\"Rescaling using same ratio... yet untested\") \n", | |
" im = im.resize((mode_w, mode_h))\n", | |
" return im" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "0d113435-bfcf-4c80-90f0-0f5c1876008f", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"ims = {}\n", | |
"for idx, row in tqdm(df.iterrows(), total=len(df)):\n", | |
" im = load_image_size(row, mode_w, mode_h, ratio_w, ratio_h)\n", | |
" ims[row.file_path] = im" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "4361b5a2-4608-418a-8b2c-ca057bf3c830", | |
"metadata": {}, | |
"outputs": [], | |
"source": [] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "daab86a1-b8ca-4c62-9bbe-5e707853a3dd", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"mode_w, min_w" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "b060b332-15dd-424e-bc75-8bb226215356", | |
"metadata": {}, | |
"outputs": [], | |
"source": [] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "4a6c9a16-726e-4cae-b8b4-63c7e6b0b393", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"new_width, new_height = (w / 3, h / 2)\n", | |
"\n", | |
"left = (width - new_width) / 2\n", | |
"top = (height - new_height) / 2\n", | |
"right = (width + new_width) / 2\n", | |
"bottom = (height + new_height) / 2\n", | |
"\n", | |
"# Crop the center of the image\n", | |
"im = im.crop((left, top, right, bottom))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "d283fee3-06ca-4b37-91e5-f80be2f5b023", | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"im.show()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"id": "34462965-948d-4acf-ac98-da4f67502b6e", | |
"metadata": {}, | |
"outputs": [], | |
"source": [] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 3 (ipykernel)", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.9.6" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 5 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment