{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Apply image filters" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Problem\n", "\n", "You need to apply filters to hundreds of images—blur, sharpen, edge detection, and other enhancements." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Solution\n", "\n", "**What's in this recipe:**\n", "\n", "- Apply common image filters\n", "- Test filters before applying\n", "- Process multiple images in batch\n", "\n", "You apply image filters (blur, sharpen, edge detection) to images in your table using custom UDFs that wrap Pillow's `ImageFilter` module (relies on PIL/Pillow). This gives you control over filter parameters.\n", "\n", "You can iterate on transformations before adding them to your table. Use `.select()` with `.collect()` to preview results on sample images—nothing is stored in your table. If you want to collect only the first few rows, use `.head(n)` instead of `.collect()`. Once you're satisfied, use `.add_computed_column()` to apply the filter to all images in your table.\n", "\n", "For more on this workflow, see [Get fast feedback on transformations](https://docs.pixeltable.com/howto/cookbooks/core/dev-iterative-workflow)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Setup" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "execution": { "iopub.execute_input": "2025-12-12T02:38:51.537787Z", "iopub.status.busy": "2025-12-12T02:38:51.537508Z", "iopub.status.idle": "2025-12-12T02:38:54.020884Z", "shell.execute_reply": "2025-12-12T02:38:54.020425Z" } }, "outputs": [], "source": [ "%pip install -qU pixeltable" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2025-12-12T02:38:54.037993Z", "iopub.status.busy": "2025-12-12T02:38:54.037851Z", "iopub.status.idle": "2025-12-12T02:38:55.160125Z", "shell.execute_reply": "2025-12-12T02:38:55.159644Z" } }, "outputs": [], "source": [ "import pixeltable as pxt\n", "from PIL import ImageFilter" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Load images" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2025-12-12T02:38:55.162499Z", "iopub.status.busy": "2025-12-12T02:38:55.162288Z", "iopub.status.idle": "2025-12-12T02:38:55.353441Z", "shell.execute_reply": "2025-12-12T02:38:55.352925Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata\n", "Created directory 'image_demo'.\n" ] }, { "data": { "text/plain": [ "" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Create a fresh directory (drop existing if present)\n", "pxt.drop_dir('image_demo', force=True)\n", "pxt.create_dir('image_demo')" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2025-12-12T02:38:55.355997Z", "iopub.status.busy": "2025-12-12T02:38:55.355777Z", "iopub.status.idle": "2025-12-12T02:38:55.411544Z", "shell.execute_reply": "2025-12-12T02:38:55.411165Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Created table 'filters'.\n" ] } ], "source": [ "t = pxt.create_table('image_demo/filters', {'image': pxt.Image})" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2025-12-12T02:38:55.413601Z", "iopub.status.busy": "2025-12-12T02:38:55.413461Z", "iopub.status.idle": "2025-12-12T02:38:56.743587Z", "shell.execute_reply": "2025-12-12T02:38:56.742929Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\r", "Inserting rows into `filters`: 0 rows [00:00, ? rows/s]" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\r", "Inserting rows into `filters`: 3 rows [00:00, 538.79 rows/s]" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "Inserted 3 rows with 0 errors.\n" ] }, { "data": { "text/plain": [ "3 rows inserted, 6 values computed." ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "t.insert(\n", " [\n", " {\n", " 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000090.jpg'\n", " },\n", " {\n", " 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000106.jpg'\n", " },\n", " {\n", " 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000285.jpg'\n", " },\n", " ]\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Iterate: apply filters to a few images first" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2025-12-12T02:38:56.746350Z", "iopub.status.busy": "2025-12-12T02:38:56.745898Z", "iopub.status.idle": "2025-12-12T02:38:56.749213Z", "shell.execute_reply": "2025-12-12T02:38:56.748768Z" } }, "outputs": [], "source": [ "@pxt.udf\n", "def apply_blur(img: pxt.Image) -> pxt.Image:\n", " \"\"\"Apply blur filter.\"\"\"\n", " return img.filter(ImageFilter.BLUR)\n", "\n", "\n", "@pxt.udf\n", "def apply_sharpen(img: pxt.Image) -> pxt.Image:\n", " \"\"\"Apply sharpen filter.\"\"\"\n", " return img.filter(ImageFilter.SHARPEN)\n", "\n", "\n", "@pxt.udf\n", "def apply_find_edges(img: pxt.Image) -> pxt.Image:\n", " \"\"\"Apply edge detection filter.\"\"\"\n", " return img.filter(ImageFilter.FIND_EDGES)\n", "\n", "\n", "@pxt.udf\n", "def apply_edge_enhance(img: pxt.Image) -> pxt.Image:\n", " \"\"\"Apply edge enhancement filter.\"\"\"\n", " return img.filter(ImageFilter.EDGE_ENHANCE)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2025-12-12T02:38:56.751056Z", "iopub.status.busy": "2025-12-12T02:38:56.750930Z", "iopub.status.idle": "2025-12-12T02:38:56.878856Z", "shell.execute_reply": "2025-12-12T02:38:56.872479Z" } }, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
imageapply_blurapply_sharpen
\n", " \n", "
\n", " \n", "
\n", " \n", "
" ], "text/plain": [ " image \\\n", "0 \n", " \n", " \n", " image\n", " blurred\n", " sharpened\n", " \n", " \n", " \n", " \n", "
\n", " \n", "
\n", "
\n", " \n", "
\n", "
\n", " \n", "
\n", " \n", " \n", "
\n", " \n", "
\n", "
\n", " \n", "
\n", "
\n", " \n", "
\n", " \n", " \n", "
\n", " \n", "
\n", "
\n", " \n", "
\n", "
\n", " \n", "
\n", " \n", " \n", "" ], "text/plain": [ " image \\\n", "0 \n", " \n", " \n", " image\n", " edges\n", " edge_enhanced\n", " \n", " \n", " \n", " \n", "
\n", " \n", "
\n", "
\n", " \n", "
\n", "
\n", " \n", "
\n", " \n", " \n", "
\n", " \n", "
\n", "
\n", " \n", "
\n", "
\n", " \n", "
\n", " \n", " \n", "
\n", " \n", "
\n", "
\n", " \n", "
\n", "
\n", " \n", "
\n", " \n", " \n", "" ], "text/plain": [ " image \\\n", "0