{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# requires python 3.6+ because I use format string literals\n", "\n", "# get this library here: https://github.com/ZhuangLab/storm-control\n", "from storm_control.sc_hardware.hamamatsu import hamamatsu_camera as hc\n", "\n", "# install with pip install pyqtgraph\n", "import pyqtgraph as pq\n", "from pyqtgraph.Qt import QtGui\n", "\n", "# stdlib\n", "import time\n", "from threading import Thread\n", "from queue import LifoQueue\n", "\n", "# this lets pyqtgraph without blocking execution in the notebook\n", "%gui qt" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# There are 2 camera classes, hc.HamamatsuCamera and hc.HamamatsuCameraMR, I'm not sure what the differences are yet \n", "# hc.HamamatsuCameraMR seems to have fewer ops? \n", "# I have 2 cameras, so I put them in a tuple\n", "cams = hc.HamamatsuCameraMR(camera_id=0), hc.HamamatsuCameraMR(camera_id=1)\n", "\n", "# Full frame at 100 FPS doesn't work, but that's a problem with pyqtgraph. 2x2 binning works.\n", "binning = 2\n", "\n", "# Set camera properties. I don't know what all of these mean yet. \n", "for cam in cams:\n", " cam_x, cam_y = 2048, 2048\n", " cam.setPropertyValue(\"defect_correct_mode\", \"OFF\")\n", " cam.setPropertyValue(\"exposure_time\", 0.01)\n", " cam.setPropertyValue(\"subarray_hsize\", cam_x)\n", " cam.setPropertyValue(\"subarray_vsize\", cam_y)\n", " cam.setPropertyValue(\"binning\", f\"{binning}x{binning}\")\n", " cam.setPropertyValue(\"readout_speed\", 2)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Create windows for displaying images using pyqtgraph \n", "disps = [pq.image() for cam in cams]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# We store images in a last-in-first-out queue\n", "queues = [LifoQueue(maxsize=10 ** 5) for cam in cams]\n", "\n", "# Currently I start / stop acquisition based on the state of this boolean global. \n", "running = True\n", "\n", "# A function that takes frames from a camera and puts them in a queue\n", "def grabber(cam, queue):\n", " global running\n", " cam.startAcquisition()\n", " while running:\n", " frames, dims = cam.getFrames()\n", " for f in frames:\n", " queue.put(f.getData().reshape(*dims)) \n", "\n", " cam.stopAcquisition() \n", "\n", "# A function that takes frames from the queue and displays them \n", "def drawer(disp, queue, sleep_time=.032):\n", " global running\n", " \n", " while running: \n", " im = queue.get() // binning\n", " # Optional: mess with im to reduce its size and reduce the load on pyqtgraph\n", " disp.imageItem.updateImage(im)\n", " QtGui.QApplication.processEvents()\n", " # need to give pyqtgraph some time to process the image before we give it another one\n", " time.sleep(sleep_time)\n", " queue.task_done()\n", "\n", "\n", "# give our functions to threads with the right arguments \n", "frame_threads = [Thread(target=grabber, args=args) for args in zip(cams, queues)]\n", "draw_threads = [Thread(target=drawer, args=args) for args in zip(disps, queues)]\n", "\n", "# start the threads\n", "for t in frame_threads: t.start()\n", "for d in draw_threads: d.start()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# run this cell to stop acquisition / display\n", "running = False" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.6.5" } }, "nbformat": 4, "nbformat_minor": 2 }