Created
November 16, 2018 01:47
-
-
Save jaimemarijke/dadc0272f8e0fbe07e811777a42d5d8b to your computer and use it in GitHub Desktop.
PiCamera prototyping (raspi).ipynb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "code", | |
"execution_count": 18, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"from fractions import Fraction\n", | |
"import time\n", | |
"\n", | |
"import picamera\n", | |
"import picamera.array\n", | |
"import numpy as np\n", | |
"import tifffile" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def print_camera_settings(camera, context_msg='', requested_shutter_speed=None):\n", | |
" print('\\n----')\n", | |
" print(context_msg)\n", | |
" print('----')\n", | |
" print('sensor_mode:', camera.sensor_mode)\n", | |
" print('Requested shutter speed:', requested_shutter_speed)\n", | |
" print('shutter_speed:', camera.shutter_speed)\n", | |
" print('exposure_speed:', camera.exposure_speed) # Docs: \"returns the shutter speed currently being used by the camera.\"\n", | |
" print('iso:', camera.iso)\n", | |
" print('framerate:', camera.framerate)\n", | |
" print('analog_gain:', camera.analog_gain)\n", | |
" print('digital_gain:', camera.digital_gain)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Bit Depths\n", | |
"Our images are captured with 10-bits, but we want to save them in image formats with larger bit depths. By applying a scaling factor, we can make our saved images look roughly the correct brightness\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 45, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"CAPTURED_IMAGE_BIT_DEPTH = 2 ** 10\n", | |
"IMAGE_FORMAT_BIT_DEPTH = 2 ** 16\n", | |
"BIT_DEPTH_SCALE_FACTOR = int(IMAGE_FORMAT_BIT_DEPTH / CAPTURED_IMAGE_BIT_DEPTH)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Use PiBayerArray to save directly to TIFF\n", | |
"This example shows how to capture directly to a numpy array using the PiBayerArray stream.\n", | |
"\n", | |
"The downside is that (as far as I can tell) this means that EXIF data doesn't get recorded anywere" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 46, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"\n", | |
"----\n", | |
"Camera settings after capture\n", | |
"----\n", | |
"sensor_mode: 0\n", | |
"Requested shutter speed: 500000\n", | |
"shutter_speed: 499982\n", | |
"exposure_speed: 499982\n", | |
"iso: 400\n", | |
"framerate: 1\n", | |
"analog_gain: 1311/256\n", | |
"digital_gain: 1/64\n", | |
"\n", | |
"Done!\n" | |
] | |
} | |
], | |
"source": [ | |
"def picamera_capture_to_bayer_stream_to_tiff(iso=100, shutter_speed=int(0.01 * 1000 * 1000), compression_level=1):\n", | |
" filename_root = f'picamera_ss{shutter_speed}_iso{iso}_compress{compression_level}'\n", | |
" tiff_filename = f'{filename_root}.tiff'\n", | |
" \n", | |
" with picamera.PiCamera(framerate=1) as camera: \n", | |
" camera.awb_mode = 'off'\n", | |
" # I think this would use Pagnutti's numbers\n", | |
" # camera.awb_gains = (1.307,1.615) \n", | |
"\n", | |
" camera.iso = iso\n", | |
" camera.shutter_speed = shutter_speed\n", | |
"\n", | |
" with picamera.array.PiBayerArray(camera) as stream:\n", | |
" camera.capture(stream, 'jpeg', bayer=True) # To get bayer data, the image must be captured to the 'jpeg' format\n", | |
"\n", | |
" print_camera_settings(camera, 'Camera settings after capture', shutter_speed)\n", | |
"\n", | |
" tifffile.imsave(tiff_filename, stream.array.astype(np.uint16) * BIT_DEPTH_SCALE_FACTOR, compress=compression_level)\n", | |
"\n", | |
" camera.framerate = 1 # Hack to make camera close properly (https://github.com/waveform80/picamera/issues/528)\n", | |
" \n", | |
" print('\\nDone!')\n", | |
" \n", | |
"picamera_capture_to_bayer_stream_to_tiff(iso=400, shutter_speed=int(0.5 * 1000 * 1000))" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Save as JPEG+RAW, use PiBayerArray to extract RAW\n", | |
"This example shows how to capture to a JPEG+RAW file (just like raspistill!), then crack that file back open and extract the raw using PiBayerArray. (Based on this random person's gist I found)\n", | |
"\n", | |
"The advantage to this approach is that the EXIF data gets saved in the .jpeg, and then we can (theoretically)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 23, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"import PIL.Image\n", | |
"import PIL.ExifTags\n", | |
"\n", | |
"def _read_exif_tags(image_path):\n", | |
" '''\n", | |
" Uses (an \"experimental\" private function from) PIL to read EXIF tags from an image file.\n", | |
" Returns a dictionary of tag names to values\n", | |
" '''\n", | |
" PIL_image = PIL.Image.open(image_path)\n", | |
" EXIF_CODES_TO_NAMES = PIL.ExifTags.TAGS\n", | |
" # _getexif() returns a dictionary of {tag code: tag value}. Use PIL.ExifTags.TAGS dictionary of {tag code: tag name}\n", | |
" # to construct a more digestible dictionary of {tag name: tag value}\n", | |
" tags = {\n", | |
" EXIF_CODES_TO_NAMES[tag_code]: tag_value\n", | |
" for tag_code, tag_value in PIL_image._getexif().items()\n", | |
" if tag_code in EXIF_CODES_TO_NAMES\n", | |
" }\n", | |
" return tags" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 32, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"\n", | |
"----\n", | |
"Camera settings after capture\n", | |
"----\n", | |
"sensor_mode: 0\n", | |
"Requested shutter speed: 10000\n", | |
"shutter_speed: 9991\n", | |
"exposure_speed: 9991\n", | |
"iso: 100\n", | |
"framerate: 1\n", | |
"analog_gain: 331/128\n", | |
"digital_gain: 105/128\n", | |
"----\n", | |
"EXIF data:\n", | |
"----\n", | |
"ImageWidth: 720\n", | |
"ImageLength: 480\n", | |
"ResolutionUnit: 2\n", | |
"ExifOffset: 192\n", | |
"Make: RaspberryPi\n", | |
"Model: RP_imx219\n", | |
"DateTime: 2018:11:15 17:38:41\n", | |
"YCbCrPositioning: 1\n", | |
"XResolution: (72, 1)\n", | |
"YResolution: (72, 1)\n", | |
"ExifVersion: b'0220'\n", | |
"ComponentsConfiguration: b'\\x01\\x02\\x03\\x00'\n", | |
"ShutterSpeedValue: (6645155, 1000000)\n", | |
"DateTimeOriginal: 2018:11:15 17:38:41\n", | |
"DateTimeDigitized: 2018:11:15 17:38:41\n", | |
"ApertureValue: (20000, 10000)\n", | |
"BrightnessValue: (8, 100)\n", | |
"MaxApertureValue: (20000, 10000)\n", | |
"MeteringMode: 2\n", | |
"Flash: 0\n", | |
"FocalLength: (30390, 10000)\n", | |
"ColorSpace: 1\n", | |
"ExifImageWidth: 720\n", | |
"ExifInteroperabilityOffset: 906\n", | |
"ExifImageHeight: 480\n", | |
"ExposureTime: (9991, 1000000)\n", | |
"FNumber: (20000, 10000)\n", | |
"ExposureProgram: 3\n", | |
"ISOSpeedRatings: 100\n", | |
"ExposureMode: 0\n", | |
"FlashPixVersion: b'0100'\n", | |
"WhiteBalance: 0\n", | |
"MakerNote: b'ev=-1 mlux=-1 exp=9991 ag=469 focus=255 gain_r=0.000 gain_b=0.000 greenness=0 ccm=6022,-2314,394,-936,4728,310,300,-4324,8126,0,0,0 md=0 tg=2454 7480 oth=0 0 b=0 f=2454 7480 fi=0 ISP Build Date: Jun 7 2018, 15:36:07 VC_BUILD_ID_VERSION: 4800f08a139d6ca1c5ecbee345ea6682e2160881 (clean) VC_BUILD_ID_USER: dc4 VC_BUILD_ID_BRANCH: master '\n" | |
] | |
} | |
], | |
"source": [ | |
"# Cadged from https://gist.github.com/rwb27/c2ea3afb204a634f9f21a99bd8dd1541\n", | |
"def _extract_raw_from_jpeg(jpeg_filename):\n", | |
" ''' Make a fake camera and use it to set up the PiBayerArray stream, \n", | |
" then write the jpeg contents to it to get it to extract the raw bayer data\n", | |
" '''\n", | |
" class DummyCam(object):\n", | |
" full_resolution = (3280,2464)\n", | |
" resolution = full_resolution\n", | |
" revision = 'IMX219'\n", | |
" sensor_mode = 0\n", | |
" \n", | |
" with open(jpeg_filename, mode=\"rb\") as file:\n", | |
" jpeg_data = file.read()\n", | |
"\n", | |
" cam = DummyCam()\n", | |
" bayer_array = picamera.array.PiBayerArray(cam)\n", | |
" bayer_array.write(jpeg_data)\n", | |
" bayer_array.flush()\n", | |
" \n", | |
" return bayer_array.array\n", | |
"\n", | |
"def picamera_capture_as_jpeg_extract_to_tiff(iso=100, shutter_speed=int(0.01 * 1000 * 1000), compress_level=1):\n", | |
" filename_root = f'picamera_ss{shutter_speed}_iso{iso}_compress{compress_level}'\n", | |
" jpeg_filename = f'{filename_root}.jpeg'\n", | |
" tiff_filename = f'{filename_root}.tiff'\n", | |
" \n", | |
" # 1. First take an image and save it as a JPEG+RAW\n", | |
" with picamera.PiCamera(framerate=1) as camera: \n", | |
" camera.awb_mode = 'off'\n", | |
" # I think this would use Pagnutti's numbers\n", | |
" # camera.awb_gains = (1.307,1.615) \n", | |
"\n", | |
" camera.iso = iso\n", | |
" camera.shutter_speed = shutter_speed\n", | |
"\n", | |
" camera.capture(jpeg_filename, bayer=True)\n", | |
" \n", | |
" print_camera_settings(camera, 'Camera settings after capture', shutter_speed)\n", | |
" \n", | |
" camera.framerate = 1 # Hack to make camera close properly (https://github.com/waveform80/picamera/issues/528)\n", | |
" \n", | |
" # 2. Then crack open that JPEG, and read out the RAW and EXIF data, save to TIFF\n", | |
" raw_image = _extract_raw_from_jpeg(jpeg_filename)\n", | |
" exif = _read_exif_tags(jpeg_filename)\n", | |
" \n", | |
" tifffile.imsave(tiff_filename, raw_image.astype(np.uint16) * BIT_DEPTH_SCALE_FACTOR, compress=compress_level)\n", | |
" \n", | |
" # Unfortunately... I haven't found a way to write EXIF data to TIFF!\n", | |
" print('----\\nEXIF data:\\n----')\n", | |
" for tag, value in exif.items():\n", | |
" print(f'{tag}: {value}')\n", | |
"\n", | |
"picamera_capture_as_jpeg_extract_to_tiff()" | |
] | |
}, | |
{ | |
"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 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment