{ "cells": [ { "cell_type": "markdown", "id": "af12be1c", "metadata": {}, "source": [ "# Output" ] }, { "cell_type": "markdown", "id": "8de61e70", "metadata": {}, "source": [ "We now have values for each pixel in the image, but these values are not yet in the correct range.\n", "At the end of the day, we want them to be between 0 and 255.\n", "\n", "To scale them correctly, we use the \"space and marker\" part of the frame:\n", "\n", "```{figure} img/apt_frame_format.webp\n", "---\n", "name: fig:frame_format_output.en\n", "---\n", "The APT frame format again.\n", "Sync A (39 pixels) is followed by a space of 47 black pixels.\n", "Similarly, sync B (39 pixel) in the second half is followed by a space of 47 white pixels.\n", "```\n", "\n", "The space following sync A has black pixels (except for the minute markers), and the space following sync b has white pixels.\n", "We just ignore the minute markers :)\n", "\n", "We can scale the pixel values into the correct range like this:\n", "- We calculate the black level $v_b$ by computing the average (or [median](https://en.wikipedia.org/wiki/Median)) value of all pixels in the first space.\n", "- We do the same with the second space to get the white level $v_w$.\n", "- We now map every pixel value $v$ to $(v - v_b) / (v_w - v_b)$.\n", "- This has moved all values between $v_b$ and $v_w$ into the range between 0 and 1.\n", "- All values outside this range are clamped to 0 or 1 respectively.\n", "- Finally, we have to multiply each value with 255." ] }, { "cell_type": "markdown", "id": "436e60e1", "metadata": {}, "source": [ "(pgm-format)=\n", "## PGM format\n", "\n", "```{note}\n", "Feel free to use a different format, or an image library like [Pillow](https://python-pillow.org/) or [SDL](https://www.libsdl.org/).\n", "```\n", "\n", "To write the final image into a file, we can use this very simple file format:\n", "\n", "```{admonition} Format\n", "The first line contains the string `P2`.\n", "\n", "The second line contains two positive integers $w$ and $h$, describing the width $(w)$ and height $(h)$ of the image $(w = 2080, h \\approx 1400)$.\n", "\n", "The third line contains the positive integer $v_{max} = 255$.\n", "\n", "Afterwards, $h$ lines follow, containing $w$ integer pixel values $v$ $(0 \\leq v \\leq v_{max})$.\n", "```" ] }, { "cell_type": "markdown", "id": "1ab1434e", "metadata": {}, "source": [ "### Example\n", "\n", "```\n", "P2\n", "6 7\n", "255\n", " 0 6 12 18 24 30\n", " 6 240 235 230 30 36\n", "12 235 24 30 220 42\n", "18 230 235 220 42 48\n", "24 225 36 42 210 54\n", "30 220 215 210 54 60\n", "36 42 48 54 60 66\n", "```" ] }, { "cell_type": "markdown", "id": "c2c9a6b9", "metadata": {}, "source": [ "```{note}\n", "The PGM format only supports grayscale images, but there is also e.g. the PPM format for colored images.\n", "You can read more about it [here](https://en.wikipedia.org/wiki/Netpbm).\n", "```" ] }, { "cell_type": "markdown", "id": "d16219b2", "metadata": {}, "source": [ "After saving the pixel values in such a file with a `.pgm` extension, we can look at them using a graphics program like [GIMP](https://www.gimp.org/)." ] } ], "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.11.13" } }, "nbformat": 4, "nbformat_minor": 5 }