{ "cells": [ { "cell_type": "markdown", "source": [ "# Smart Stack Processing\r\n", "\r\n", "Once a smart stack procedure has been completed on the OFM, the scan folder should contain a 'data.json' file, and a directory for each x-y position captured. Within each of these directories, there should be a 'data.json' file and a stack of 9 jpeg images. The image from each stack to use in large scans will have automatically been tagged with \"central_image\".\r\n", "\r\n", "If the scan isn't completed, the last folder will not contain these images, but the data file will contain a report to help you debug the cause of the failure.\r\n", "\r\n", "The scan folder will need to be moved to the device this script is run on, using SCP or another similar method." ], "metadata": {} }, { "cell_type": "code", "execution_count": 6, "source": [ "#Begin with standard imports for processing paths, json metadata and copying files\r\n", "\r\n", "import os\r\n", "import json\r\n", "import piexif\r\n", "from shutil import copy2\r\n", "import logging" ], "outputs": [], "metadata": {} }, { "cell_type": "code", "execution_count": 7, "source": [ "def pull_usercomment_dict(filepath):\r\n", " \"\"\"\r\n", " Reads UserComment Exif data from a file, and returns the contained bytes as a dictionary.\r\n", " Args:\r\n", " filepath: Path to the Exif-containing file\r\n", " \"\"\"\r\n", " try:\r\n", " exif_dict = piexif.load(filepath)\r\n", " except piexif._exceptions.InvalidImageDataError:\r\n", " logging.warning(\"Invalid data at {}. Skipping.\".format(filepath))\r\n", " return None\r\n", " if \"Exif\" in exif_dict and piexif.ExifIFD.UserComment in exif_dict[\"Exif\"]:\r\n", " try:\r\n", " return json.loads(exif_dict[\"Exif\"][piexif.ExifIFD.UserComment].decode())\r\n", " except json.decoder.JSONDecodeError:\r\n", " logging.error(\r\n", " f\"Capture {filepath} has old, corrupt, or missing OpenFlexure metadata. Unable to reload to server.\"\r\n", " )\r\n", " else:\r\n", " return None" ], "outputs": [], "metadata": {} }, { "cell_type": "code", "execution_count": 8, "source": [ "# Requires the link to the folder\r\n", "\r\n", "scan_dir = r'..\\tile_example'\r\n", "\r\n", "# Creates a new folder in the directory called 'use'\r\n", "\r\n", "use_dir = os.path.join(scan_dir, 'use')\r\n", "if not os.path.exists(use_dir):\r\n", " os.mkdir(use_dir)\r\n", "\r\n", "# Gets a list of the folders in the main directory, ignoring the newly made 'use' folder.\r\n", "# Any other folders in the directory may cause the process to fail\r\n", "\r\n", "coords = [ f.path for f in os.scandir(scan_dir) if f.is_dir() and 'use' not in str(f) ]\r\n", "\r\n", "# For each folder, search through the images for one containing a 'central_image' tag, and copy it to the 'use' folder\r\n", "\r\n", "for stack in coords:\r\n", " location = os.path.basename(stack)\r\n", " images = [ os.path.join(stack, f) for f in os.listdir(stack) if f.endswith('jpeg')]\r\n", " for image in images:\r\n", " if 'central_image' in pull_usercomment_dict(image)['image']['tags']:\r\n", " copy2(image, os.path.join(use_dir, location + '_' + os.path.split(image)[1])) " ], "outputs": [], "metadata": {} }, { "cell_type": "code", "execution_count": 9, "source": [ "# Gets a list of all the images in the 'use' dir, and sorts them by modified date\r\n", "# (on Windows, this is actually the time the images were captured, not when they were pasted.\r\n", "# Other Operating Systems might need a different approach)\r\n", "\r\n", "images = [ os.path.join(use_dir, f) for f in os.listdir(use_dir) if f.endswith('jpeg')]\r\n", "\r\n", "images.sort(key=os.path.getmtime)\r\n", "\r\n", "# In order of capture date, enumerates the images, starting with '00.jpeg'.\r\n", "# If over 100 images are taken, must be modified with an additional zfill,\r\n", "# ie, so there is space to go from '000.jpeg' to '101.jpeg'.\r\n", "\r\n", "for i in range(len(images)):\r\n", " os.rename(images[i], os.path.join(use_dir, str(i).zfill(2) + '.jpeg'))" ], "outputs": [], "metadata": {} }, { "cell_type": "markdown", "source": [ "There should now be a new folder, 'use', containing the images captured in ascending order. The stitching plug-in in the [Fiji distribution of ImageJ](https://imagej.net/software/fiji/downloads) can now be used to tile the images. The first index will be 00 and the format {ii}.jpeg (or 000 and {iii}.jpeg for larger scans). The approximate overlap and order of captures will depend on the scan you ran." ], "metadata": {} } ], "metadata": { "interpreter": { "hash": "aa28e4f14a7ff8b2d1fa149f2498635dcaf698094ea98853a01965ff2586af7e" }, "kernelspec": { "name": "python3", "display_name": "Python 3.8.2 64-bit ('.venv': poetry)" }, "language_info": { "name": "python", "version": "3.8.2", "mimetype": "text/x-python", "codemirror_mode": { "name": "ipython", "version": 3 }, "pygments_lexer": "ipython3", "nbconvert_exporter": "python", "file_extension": ".py" }, "orig_nbformat": 2 }, "nbformat": 4, "nbformat_minor": 2 }