""" Program: Photron Camera MRAW Reader Date: 9 August 2018 Author: Ivo Peters (copied by LM) - Further Details - Ivo Peters - i.r.peters@soton.ac.uk Created on: 2016-10-26 Last revision: 2016-10-27 Version: 0.2 - Other Sources - Chunks of code taken from CINE library - Dustin Kleckner - dkleckner@uchicago.edu Chunks of code taken from pyMRAW.py - Jaka Javh - jaka.javh@fs.uni-lj.si From the .cih file - Makes a dictionary from the categories and data found in Camera Information Header file. From the .mraw file - Opens a movie file in bits and reads it as an image in matrix form, one entry for the shade of each pixel. """ """ - What is a cih file? The acronym `cih` stands for Camera Information Header. - What information is contained in it? It contains information about the camera name, date and time of filming, frame rate, and more. """ import numpy as np # Process the data in chunks. This keeps it in the L2 catch of the processor, increasing speed for large arrays by ~50%. # Chunk size should be divisible by 3, 4, and 5. It seems to be near-optimal (for speed). CHUNK_SIZE = 6 * 10 ** 5 def read_cih(filename): # Create a dictionary that will contain all the movie properties defined in the .cih file. cih = dict() # Read the cih (Camera Information Header). with open(filename, 'r') as file: # Work through the file line by line. for line in file: if line == '\n': # End of cif file (there's one blank line before the word 'END'). break # Split the line in property (key) and value (item) pairs; line_sp is a list with two entries. line_sp = line.replace('\n', '').split(' : ') # Check if line_sp indeed contains a property name and a value. if len(line_sp) == 2: key, item = line_sp # All keys and items are strings at first. Try to turn the value (item) into a number, float or integer: try: if '.' in item: cih[key] = float(item) else: cih[key] = int(item) # Otherwise the value is a string: except: cih[key] = item # The function returns the dictionary. return cih def twelve2sixteen(a): b = np.zeros(a.size // 3 * 2, dtype='u2') # The type u2 is a 16-bit unsigned integer. # Divide a into chunks containing some number of tuples with 3 values from a. for j in range(0, len(a), CHUNK_SIZE): (a0, a1, a2) = [a[j + i:j + CHUNK_SIZE:3].astype('u2') for i in range(3)] k = j // 3 * 2 k2 = k + CHUNK_SIZE // 3 * 2 b[k + 0:k2:2] = ((a0 & 0xFF) << 4) + ((a1 & 0xF0) >> 4) b[k + 1:k2:2] = ((a1 & 0x0F) << 8) + ((a2 & 0xFF) >> 0) return b # Create a new class 'mraw' of objects, subclass to the parent class 'object'. class mraw(object): def __init__(self, fn): cih = read_cih(fn) self.cih = cih # Gets the whole dictionary. self.width = cih['Image Width'] self.height = cih['Image Height'] self.image_count = cih['Total Frame'] self.bit_depth = cih['EffectiveBit Depth'] self.frame_rate = float(cih['Record Rate(fps)']) # Turn the frame rate into a float to avoid problems # with creating unwanted integers by accident when using this property. self.trigger_out_delay = float(cih['Signal Delay Trigger Out Width(nsec)']) * 1e-9 # Calculate the size of an image in bytes: # Number of pixels * number of bits per pixel / 8 (might be "n" bytes plus a nibble or so). # The `//` is floor division. After division, it returns the integer with no remainder. self.imageSize = self.width * self.height * self.bit_depth // 8 self.fileName = fn[:-4] + '.mraw' self.f = open(self.fileName, 'rb') def __len__(self): return self.image_count def __getitem__(self, key): if type(key) == slice: return map(self.get_frame, range(self.image_count)[key]) return self.get_frame(key) def get_fps(self): return self.frame_rate def get_frame(self, number): # Calculate the position in bytes where the image is: # It is the number of the frame multiplied by the image size of each frame in bytes. bytePosition = number * self.imageSize # Seek this position in the file. self.f.seek(bytePosition) # Read a certain number of bytes. image = np.frombuffer(self.f.read(self.imageSize), np.uint8) image = twelve2sixteen(image) # Break the image into byte chunks. # Make the image into a matrix. The height of the image is the number of rows the matrix has; its width is the # number of columns. image = np.reshape(image, (self.height, self.width)) return image def close(self): self.f.close()