{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "d77feed4",
      "metadata": {
        "id": "d77feed4"
      },
      "outputs": [],
      "source": [
        "def translation_matrix(tx, ty):\n",
        "    return np.array([\n",
        "        [1, 0, tx],\n",
        "        [0, 1, ty],\n",
        "        [0, 0, 1]\n",
        "    ], dtype=float)\n",
        "\n",
        "\n",
        "print(translation_matrix(30, 20))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "cf96eaaf",
      "metadata": {
        "id": "cf96eaaf"
      },
      "outputs": [],
      "source": [
        "def scaling_matrix(sx, sy):\n",
        "    return np.array([\n",
        "        [sx, 0,  0],\n",
        "        [0,  sy, 0],\n",
        "        [0,  0,  1]\n",
        "    ], dtype=float)\n",
        "\n",
        "\n",
        "print(scaling_matrix(1.5, 1.5))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "eec643ed",
      "metadata": {
        "id": "eec643ed"
      },
      "outputs": [],
      "source": [
        "def rotation_matrix(angle_degrees):\n",
        "    theta = np.deg2rad(angle_degrees)\n",
        "\n",
        "    return np.array([\n",
        "        [np.cos(theta), -np.sin(theta), 0],\n",
        "        [np.sin(theta),  np.cos(theta), 0],\n",
        "        [0,              0,             1]\n",
        "    ], dtype=float)\n",
        "\n",
        "\n",
        "print(rotation_matrix(30))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "2016d97d",
      "metadata": {
        "id": "2016d97d"
      },
      "outputs": [],
      "source": [
        "def shear_matrix(shx=0, shy=0):\n",
        "    return np.array([\n",
        "        [1,   shx, 0],\n",
        "        [shy, 1,   0],\n",
        "        [0,   0,   1]\n",
        "    ], dtype=float)\n",
        "\n",
        "\n",
        "print(shear_matrix(shx=0.3))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "3c65a88c",
      "metadata": {
        "id": "3c65a88c"
      },
      "outputs": [],
      "source": [
        "def affine_transform(image, matrix, interpolation=\"nearest\",\n",
        "                     output_shape=None):\n",
        "\n",
        "    input_height, input_width = image.shape\n",
        "\n",
        "    if output_shape is None:\n",
        "        output_height = input_height\n",
        "        output_width = input_width\n",
        "    else:\n",
        "        output_height, output_width = output_shape\n",
        "\n",
        "    output = np.zeros(\n",
        "        (output_height, output_width),\n",
        "        dtype=np.uint8\n",
        "    )\n",
        "\n",
        "    inverse_matrix = np.linalg.inv(matrix)\n",
        "\n",
        "    for y_out in range(output_height):\n",
        "        for x_out in range(output_width):\n",
        "\n",
        "            output_coordinate = np.array(\n",
        "                [x_out, y_out, 1],\n",
        "                dtype=float\n",
        "            )\n",
        "\n",
        "            source_coordinate = (\n",
        "                inverse_matrix @ output_coordinate\n",
        "            )\n",
        "\n",
        "            x_source = source_coordinate[0]\n",
        "            y_source = source_coordinate[1]\n",
        "\n",
        "            if interpolation == \"nearest\":\n",
        "                value = nearest_neighbor(\n",
        "                    image,\n",
        "                    x_source,\n",
        "                    y_source\n",
        "                )\n",
        "\n",
        "            elif interpolation == \"bilinear\":\n",
        "                value = bilinear_interpolation(\n",
        "                    image,\n",
        "                    x_source,\n",
        "                    y_source\n",
        "                )\n",
        "\n",
        "            elif interpolation == \"bicubic\":\n",
        "                value = bicubic_interpolation(\n",
        "                    image,\n",
        "                    x_source,\n",
        "                    y_source\n",
        "                )\n",
        "\n",
        "            else:\n",
        "                raise ValueError(\n",
        "                    \"Choose nearest, bilinear or bicubic\"\n",
        "                )\n",
        "\n",
        "            output[y_out, x_out] = int(value)\n",
        "\n",
        "    return output"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "6befb34a",
      "metadata": {
        "id": "6befb34a"
      },
      "outputs": [],
      "source": [
        "T = translation_matrix(30, 20)\n",
        "\n",
        "rectangle_transformed = affine_transform(\n",
        "    rectangle,\n",
        "    T,\n",
        "    interpolation=\"nearest\"\n",
        ")\n",
        "\n",
        "fig, axes = plt.subplots(1, 2, figsize=(10, 5))\n",
        "\n",
        "axes[0].imshow(rectangle, cmap=\"gray\", vmin=0, vmax=255)\n",
        "axes[0].set_title(\"Original\")\n",
        "\n",
        "axes[1].imshow(\n",
        "    rectangle_transformed,\n",
        "    cmap=\"gray\",\n",
        "    vmin=0,\n",
        "    vmax=255\n",
        ")\n",
        "axes[1].set_title(\"Affine Translation\")\n",
        "\n",
        "for ax in axes:\n",
        "    ax.axis(\"off\")\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "736854ce",
      "metadata": {
        "id": "736854ce"
      },
      "outputs": [],
      "source": [
        "def rotation_about_center(width, height, angle_degrees):\n",
        "\n",
        "    cx = width / 2\n",
        "    cy = height / 2\n",
        "\n",
        "    move_to_origin = translation_matrix(-cx, -cy)\n",
        "    rotate = rotation_matrix(angle_degrees)\n",
        "    move_back = translation_matrix(cx, cy)\n",
        "\n",
        "    return move_back @ rotate @ move_to_origin\n",
        "\n",
        "\n",
        "R = rotation_about_center(\n",
        "    width=200,\n",
        "    height=200,\n",
        "    angle_degrees=30\n",
        ")\n",
        "\n",
        "rotated_triangle = affine_transform(\n",
        "    triangle,\n",
        "    R,\n",
        "    interpolation=\"bilinear\"\n",
        ")\n",
        "\n",
        "fig, axes = plt.subplots(1, 2, figsize=(10, 5))\n",
        "\n",
        "axes[0].imshow(triangle, cmap=\"gray\", vmin=0, vmax=255)\n",
        "axes[0].set_title(\"Original Triangle\")\n",
        "\n",
        "axes[1].imshow(\n",
        "    rotated_triangle,\n",
        "    cmap=\"gray\",\n",
        "    vmin=0,\n",
        "    vmax=255\n",
        ")\n",
        "axes[1].set_title(\"Rotated Triangle\")\n",
        "\n",
        "for ax in axes:\n",
        "    ax.axis(\"off\")\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "2441e0ac",
      "metadata": {
        "id": "2441e0ac"
      },
      "outputs": [],
      "source": [
        "def scaling_about_center(width, height, sx, sy):\n",
        "\n",
        "    cx = width / 2\n",
        "    cy = height / 2\n",
        "\n",
        "    move_to_origin = translation_matrix(-cx, -cy)\n",
        "    scale = scaling_matrix(sx, sy)\n",
        "    move_back = translation_matrix(cx, cy)\n",
        "\n",
        "    return move_back @ scale @ move_to_origin\n",
        "\n",
        "\n",
        "S = scaling_about_center(\n",
        "    width=200,\n",
        "    height=200,\n",
        "    sx=1.3,\n",
        "    sy=1.3\n",
        ")\n",
        "\n",
        "scaled_circle = affine_transform(\n",
        "    circle,\n",
        "    S,\n",
        "    interpolation=\"bilinear\"\n",
        ")\n",
        "\n",
        "fig, axes = plt.subplots(1, 2, figsize=(10, 5))\n",
        "\n",
        "axes[0].imshow(circle, cmap=\"gray\", vmin=0, vmax=255)\n",
        "axes[0].set_title(\"Original Circle\")\n",
        "\n",
        "axes[1].imshow(\n",
        "    scaled_circle,\n",
        "    cmap=\"gray\",\n",
        "    vmin=0,\n",
        "    vmax=255\n",
        ")\n",
        "axes[1].set_title(\"Scaled Circle\")\n",
        "\n",
        "for ax in axes:\n",
        "    ax.axis(\"off\")\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "e5538f5e",
      "metadata": {
        "id": "e5538f5e"
      },
      "outputs": [],
      "source": [
        "H = shear_matrix(shx=0.4)\n",
        "\n",
        "sheared_rectangle = affine_transform(\n",
        "    rectangle,\n",
        "    H,\n",
        "    interpolation=\"nearest\"\n",
        ")\n",
        "\n",
        "fig, axes = plt.subplots(1, 2, figsize=(10, 5))\n",
        "\n",
        "axes[0].imshow(rectangle, cmap=\"gray\", vmin=0, vmax=255)\n",
        "axes[0].set_title(\"Original Rectangle\")\n",
        "\n",
        "axes[1].imshow(\n",
        "    sheared_rectangle,\n",
        "    cmap=\"gray\",\n",
        "    vmin=0,\n",
        "    vmax=255\n",
        ")\n",
        "axes[1].set_title(\"Sheared Rectangle\")\n",
        "\n",
        "for ax in axes:\n",
        "    ax.axis(\"off\")\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "c85e1b83",
      "metadata": {
        "id": "c85e1b83"
      },
      "outputs": [],
      "source": [
        "height, width = triangle.shape\n",
        "\n",
        "T = translation_matrix(15, 10)\n",
        "R = rotation_about_center(width, height, 25)\n",
        "S = scaling_about_center(width, height, 1.1, 1.1)\n",
        "\n",
        "combined_matrix = T @ R @ S\n",
        "\n",
        "combined_triangle = affine_transform(\n",
        "    triangle,\n",
        "    combined_matrix,\n",
        "    interpolation=\"bilinear\"\n",
        ")\n",
        "\n",
        "fig, axes = plt.subplots(1, 2, figsize=(10, 5))\n",
        "\n",
        "axes[0].imshow(triangle, cmap=\"gray\", vmin=0, vmax=255)\n",
        "axes[0].set_title(\"Original\")\n",
        "\n",
        "axes[1].imshow(\n",
        "    combined_triangle,\n",
        "    cmap=\"gray\",\n",
        "    vmin=0,\n",
        "    vmax=255\n",
        ")\n",
        "axes[1].set_title(\"Translation + Rotation + Scaling\")\n",
        "\n",
        "for ax in axes:\n",
        "    ax.axis(\"off\")\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "08b64425",
      "metadata": {
        "id": "08b64425"
      },
      "outputs": [],
      "source": [
        "import urllib.request\n",
        "from PIL import Image\n",
        "\n",
        "image_url = (\n",
        "    \"https://raw.githubusercontent.com/\"\n",
        "    \"scikit-image/scikit-image/main/\"\n",
        "    \"skimage/data/chelsea.png\"\n",
        ")\n",
        "\n",
        "urllib.request.urlretrieve(image_url, \"sample_image.png\")\n",
        "\n",
        "real_image = Image.open(\"sample_image.png\").convert(\"L\")\n",
        "\n",
        "real_image = real_image.resize((160, 120))\n",
        "\n",
        "real_image = np.array(real_image)\n",
        "\n",
        "print(\"Image shape:\", real_image.shape)\n",
        "\n",
        "plt.figure(figsize=(6, 5))\n",
        "plt.imshow(real_image, cmap=\"gray\")\n",
        "plt.title(\"Real Grayscale Image\")\n",
        "plt.axis(\"off\")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "cb0d97df",
      "metadata": {
        "id": "cb0d97df"
      },
      "outputs": [],
      "source": [
        "real_h, real_w = real_image.shape\n",
        "\n",
        "R_real = rotation_about_center(\n",
        "    real_w,\n",
        "    real_h,\n",
        "    20\n",
        ")\n",
        "\n",
        "real_rotated_nearest = affine_transform(\n",
        "    real_image,\n",
        "    R_real,\n",
        "    interpolation=\"nearest\"\n",
        ")\n",
        "\n",
        "real_rotated_bilinear = affine_transform(\n",
        "    real_image,\n",
        "    R_real,\n",
        "    interpolation=\"bilinear\"\n",
        ")\n",
        "\n",
        "fig, axes = plt.subplots(1, 3, figsize=(15, 5))\n",
        "\n",
        "axes[0].imshow(real_image, cmap=\"gray\")\n",
        "axes[0].set_title(\"Original\")\n",
        "\n",
        "axes[1].imshow(real_rotated_nearest, cmap=\"gray\")\n",
        "axes[1].set_title(\"Rotation: Nearest\")\n",
        "\n",
        "axes[2].imshow(real_rotated_bilinear, cmap=\"gray\")\n",
        "axes[2].set_title(\"Rotation: Bilinear\")\n",
        "\n",
        "for ax in axes:\n",
        "    ax.axis(\"off\")\n",
        "\n",
        "plt.show()"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "name": "python",
      "version": "3.x"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}