{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Mosaic Art\n", "\n", "We would like to use images as mosaic pieces to build a bigger image.\n", "\n", "Data originally comes from [here](https://www.kaggle.com/thedownhill/art-images-drawings-painting-sculpture-engraving)." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "execution": { "iopub.execute_input": "2024-04-17T07:38:47.101204Z", "iopub.status.busy": "2024-04-17T07:38:47.101000Z", "iopub.status.idle": "2024-04-17T07:38:47.415427Z", "shell.execute_reply": "2024-04-17T07:38:47.415108Z" } }, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd\n", "\n", "from lets_plot import *" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2024-04-17T07:38:47.417119Z", "iopub.status.busy": "2024-04-17T07:38:47.416982Z", "iopub.status.idle": "2024-04-17T07:38:47.419198Z", "shell.execute_reply": "2024-04-17T07:38:47.419021Z" } }, "outputs": [ { "data": { "text/html": [ "\n", "
\n", " \n", " " ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "LetsPlot.setup_html()" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2024-04-17T07:38:47.432396Z", "iopub.status.busy": "2024-04-17T07:38:47.432297Z", "iopub.status.idle": "2024-04-17T07:38:47.434623Z", "shell.execute_reply": "2024-04-17T07:38:47.434444Z" } }, "outputs": [], "source": [ "def get_img_or_none_by_index(i):\n", " import io\n", " from os.path import isfile, join\n", " import requests\n", " from PIL import Image\n", "\n", " TIMEOUT = 5\n", " IMAGES_PATH = \"images/sculpture\"\n", " IMAGES_URL = \"https://github.com/JetBrains/lets-plot-docs/raw/master/source/examples/demo/images/sculpture\"\n", "\n", " filepath = join(IMAGES_PATH, \"{0}.jpg\".format(i))\n", " if isfile(filepath):\n", " return Image.open(filepath)\n", " filepath = join(IMAGES_PATH, \"{0}.jpeg\".format(i))\n", " if isfile(filepath):\n", " return Image.open(filepath)\n", " r = requests.get(\"{0}/{1}.jpg\".format(IMAGES_URL, i), timeout=TIMEOUT)\n", " if r.status_code == requests.codes.ok:\n", " return Image.open(io.BytesIO(r.content))\n", " r = requests.get(\"{0}/{1}.jpeg\".format(IMAGES_URL, i), timeout=TIMEOUT)\n", " if r.status_code == requests.codes.ok:\n", " return Image.open(io.BytesIO(r.content))\n", " return None" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2024-04-17T07:38:47.435643Z", "iopub.status.busy": "2024-04-17T07:38:47.435498Z", "iopub.status.idle": "2024-04-17T07:38:47.436995Z", "shell.execute_reply": "2024-04-17T07:38:47.436819Z" } }, "outputs": [], "source": [ "def get_grayscale_img_or_none(img):\n", " if img is None:\n", " return None\n", " if img.mode not in ['RGB', 'L']:\n", " return None\n", " return img.convert('L')" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2024-04-17T07:38:47.437848Z", "iopub.status.busy": "2024-04-17T07:38:47.437773Z", "iopub.status.idle": "2024-04-17T07:38:47.439116Z", "shell.execute_reply": "2024-04-17T07:38:47.438943Z" } }, "outputs": [], "source": [ "WIDTH, HEIGHT = 20, 20\n", "IMAGES_COUNT = 1_732\n", "TARGET_IMAGE = 1_086" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2024-04-17T07:38:47.439960Z", "iopub.status.busy": "2024-04-17T07:38:47.439881Z", "iopub.status.idle": "2024-04-17T07:38:52.196355Z", "shell.execute_reply": "2024-04-17T07:38:52.195995Z" } }, "outputs": [], "source": [ "data = []\n", "for i in range(IMAGES_COUNT):\n", " img = get_grayscale_img_or_none(get_img_or_none_by_index(i))\n", " if img is None:\n", " continue\n", " img = img.resize((WIDTH, HEIGHT))\n", " image_data = np.asarray(img).reshape(WIDTH * HEIGHT)\n", " image_agg_data = np.array([np.round(image_data.mean()), np.round(image_data.std())])\n", " data.append(np.concatenate((image_agg_data, image_data), axis=0).astype(int))" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2024-04-17T07:38:52.197757Z", "iopub.status.busy": "2024-04-17T07:38:52.197665Z", "iopub.status.idle": "2024-04-17T07:38:52.618680Z", "shell.execute_reply": "2024-04-17T07:38:52.618428Z" } }, "outputs": [], "source": [ "df = pd.DataFrame(data)\n", "df = df.rename(columns={0: 'color', 1: 'std'})\n", "df = df.sort_values(by=['color', 'std']).reset_index(drop=True)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2024-04-17T07:38:52.619986Z", "iopub.status.busy": "2024-04-17T07:38:52.619817Z", "iopub.status.idle": "2024-04-17T07:38:52.993096Z", "shell.execute_reply": "2024-04-17T07:38:52.992788Z" } }, "outputs": [ { "data": { "text/html": [ "
\n", " " ], "text/plain": [ "" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ggplot() + \\\n", " geom_bar(aes(x='color', fill='color'), data=df, sampling=sampling_pick(n=256), size=0) + \\\n", " scale_fill_gradient(low='black', high='white') + \\\n", " ggtitle('Gray Color Distribution')" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "execution": { "iopub.execute_input": "2024-04-17T07:38:52.994271Z", "iopub.status.busy": "2024-04-17T07:38:52.994193Z", "iopub.status.idle": "2024-04-17T07:38:53.034363Z", "shell.execute_reply": "2024-04-17T07:38:53.034065Z" } }, "outputs": [], "source": [ "for missing_color in set(range(256)) - set(df.color.unique()):\n", " df = pd.concat([df, pd.DataFrame.from_records([dict(color=missing_color)])])\n", "df = df.sort_values(by='color').reset_index(drop=True)\n", "df = df.ffill().bfill()" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "execution": { "iopub.execute_input": "2024-04-17T07:38:53.035662Z", "iopub.status.busy": "2024-04-17T07:38:53.035566Z", "iopub.status.idle": "2024-04-17T07:38:54.062463Z", "shell.execute_reply": "2024-04-17T07:38:54.062203Z" } }, "outputs": [], "source": [ "img = get_grayscale_img_or_none(get_img_or_none_by_index(TARGET_IMAGE))\n", "img = img.resize((80, 80))\n", "rows = ()\n", "for row in np.asarray(img):\n", " cols = ()\n", " for color in row:\n", " subimg = df[df.color == color].iloc[0, 2:].to_numpy().reshape(WIDTH, HEIGHT)\n", " cols = cols + (subimg,)\n", " rows = rows + (np.hstack(cols),)\n", "image_data = np.vstack(rows)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "execution": { "iopub.execute_input": "2024-04-17T07:38:54.063763Z", "iopub.status.busy": "2024-04-17T07:38:54.063687Z", "iopub.status.idle": "2024-04-17T07:38:54.179176Z", "shell.execute_reply": "2024-04-17T07:38:54.178909Z" } }, "outputs": [ { "data": { "text/html": [ "
\n", " " ], "text/plain": [ "" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ggplot() + \\\n", " geom_imshow(image_data=image_data) + \\\n", " ggsize(800, 800) + \\\n", " ggtitle('Mosaic Image') + \\\n", " theme_void() + theme(legend_position='none')" ] } ], "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.10.13" } }, "nbformat": 4, "nbformat_minor": 4 }