Skip to content

Instantly share code, notes, and snippets.

@jaimemarijke
Created November 16, 2018 01:47
Show Gist options
  • Save jaimemarijke/dadc0272f8e0fbe07e811777a42d5d8b to your computer and use it in GitHub Desktop.
Save jaimemarijke/dadc0272f8e0fbe07e811777a42d5d8b to your computer and use it in GitHub Desktop.
PiCamera prototyping (raspi).ipynb
Display the source blob
Display the rendered blob
Raw
{
"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