{ "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": "2025-11-05T13:54:51.040604Z", "iopub.status.busy": "2025-11-05T13:54:51.040492Z", "iopub.status.idle": "2025-11-05T13:54:51.044195Z", "shell.execute_reply": "2025-11-05T13:54:51.043760Z" } }, "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": "2025-11-05T13:54:51.045074Z", "iopub.status.busy": "2025-11-05T13:54:51.044997Z", "iopub.status.idle": "2025-11-05T13:54:51.046700Z", "shell.execute_reply": "2025-11-05T13:54:51.046531Z" } }, "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": "2025-11-05T13:54:51.060447Z", "iopub.status.busy": "2025-11-05T13:54:51.060369Z", "iopub.status.idle": "2025-11-05T13:54:51.062512Z", "shell.execute_reply": "2025-11-05T13:54:51.062334Z" } }, "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": "2025-11-05T13:54:51.063321Z", "iopub.status.busy": "2025-11-05T13:54:51.063252Z", "iopub.status.idle": "2025-11-05T13:54:51.064627Z", "shell.execute_reply": "2025-11-05T13:54:51.064455Z" } }, "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": "2025-11-05T13:54:51.065299Z", "iopub.status.busy": "2025-11-05T13:54:51.065230Z", "iopub.status.idle": "2025-11-05T13:54:51.066474Z", "shell.execute_reply": "2025-11-05T13:54:51.066301Z" } }, "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": "2025-11-05T13:54:51.067338Z", "iopub.status.busy": "2025-11-05T13:54:51.067271Z", "iopub.status.idle": "2025-11-05T13:54:56.292553Z", "shell.execute_reply": "2025-11-05T13:54:56.291927Z" } }, "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": "2025-11-05T13:54:56.294310Z", "iopub.status.busy": "2025-11-05T13:54:56.294151Z", "iopub.status.idle": "2025-11-05T13:54:56.797323Z", "shell.execute_reply": "2025-11-05T13:54:56.796940Z" } }, "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": "2025-11-05T13:54:56.798601Z", "iopub.status.busy": "2025-11-05T13:54:56.798506Z", "iopub.status.idle": "2025-11-05T13:54:57.260457Z", "shell.execute_reply": "2025-11-05T13:54:57.260244Z" } }, "outputs": [ { "data": { "text/html": [ " \n", " " ], "text/plain": [ "