Skip to content

Instantly share code, notes, and snippets.

@indranilsinharoy
Last active December 20, 2015 13:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save indranilsinharoy/6142992 to your computer and use it in GitHub Desktop.
Save indranilsinharoy/6142992 to your computer and use it in GitHub Desktop.
# -*- coding: utf-8 -*-
"""
File: simpleLensScaling.py
Purpose : Calculate two-point resolution using Rayleigh criterion and
study the effect of lens scaling on Rayleigh resolution.
Assumption: First order optical analysis, specifically they are:
1. The system is diffraction limited (aberrations are not considered)
2. The aperture is circular
3. A simple single lens system is considered, thus
a. the entrance pupil diameter = exit pupil diameter = aperture diameter
b. apply simple lens equation to find distance and magnification relationships
Created on Thu Aug 01 14:18:27 2013
Author: Indranil Sinharoy
License : MIT License
"""
from __future__ import division, print_function
from math import atan, pi
class camera(object):
"""a camera class"""
def __init__(self, lens=None, sensor=None):
self.lens = lens
self.sensor = sensor
self.fov_width = -1.0
self.fov_height = -1.0
def setObjDist(self, distance):
"""set the distance of the camera from the object plane in mm"""
self.lens.setObjDist(distance)
def calculateFOV(self):
"""returns the field-of-view of the camera"""
if self.lens and self.sensor:
img_w = self.sensor.width
img_h = self.sensor.height
img_z = self.lens.getImgDist()
self.fov_width = 2.0*atan((img_w/2.0)/img_z)*180.0/pi
self.fov_height = 2.0*atan((img_h/2.0)/img_z)*180.0/pi
return (self.fov_width, self.fov_height)
else:
print("Can't calculate FOV. Attach a lens and sensor to the camera")
return
class lens(object):
"""lens class, described by focal length, `f` mm, and aperture diameter, `D` mm.
The aperture is assumed to be circular.
"""
def __init__(self, f, D):
self.f = f # focal length in mm
self.D = D # aperture diameter in mm
self.Fnum = f/D # F/#
self.z_o = None # object distance in mm
self.latMag = None # lateral magnification
self.effFnum = None # effective F/#
def setObjDist(self,z_o):
"""sets the object distance z_o (mm)"""
self.z_o = z_o # set an object distance
self.z_i = self.getImgDist(z_o) # image distance for the set object distance
self.latMag = self.z_i/self.z_o # calculate lateral magnification
self.effFnum = self.z_i/self.D # calculate effective F/#
def getImgDist(self, zo=None):
"""returns the image distance for a particular object distance `zo` (which
might be different from `z_o`, the set object distance). If `zo=None`, then
the returned image distance is for the set object distance. If the object
distance has not been set, then the object distance is assumed to be infinity
and the image distance is equal to the focal length.
"""
if zo:
zi= self.f*zo/(zo-self.f)
elif not zo and self.z_o:
zi= self.f*self.z_o/(self.z_o-self.f)
elif not zo and not self.z_o:
zi = self.f
return zi
def getMinResolvSepImgPlane(self,lamda=550e-6):
"""returns the minimum resolvable separation of the geometrical image points
The default wavelength is 550 nm or 550e-6 mm
"""
deltaSi = 1.22*lamda*self.effFnum
return deltaSi
def getMinResolvSepObjPlane(self,lamda=550e-6):
"""returns the minimum resolvable separation of the geometrical image points
The default wavelength is 550 nm or 550e-6 mm
"""
deltaSo = self.getMinResolvSepImgPlane(lamda)/self.latMag
return deltaSo
def getLatRayleighResImgPlane(self,lamda=550e-6):
"""returns the lateral resolution in the image plane"""
deltaRi = 1.0/self.getMinResolvSepImgPlane(lamda)
return deltaRi
def getLatRayleighResObjPlane(self,lamda=550e-6):
"""returns the lateral resolution in the object plane"""
deltaRo = 1.0/self.getMinResolvSepObjPlane(lamda)
return deltaRo
def scaleLens(self,factor=0.5):
"""returns a scaled lens object"""
return lens(self.f*factor, self.D*factor)
class sensor(object):
"""a very simple sensor class"""
def __init__(self, width=36.0, height=24.0):
self.width = width
self.height = height
def scaleSensor(self, factor=0.5):
"""scale the dimensions of the sensor by the given factor and return
a new sensor object"""
return sensor(self.width*factor, self.height*factor)
if __name__ == "__main__":
print("Study the effect of Lens Scaling on Diffraction limited resolution:")
# Test the camera class
object_distance = 2000.0 # Object distance = 2 meters
lensA = lens(50.0, 25.0) # lensA, focal length=50mm, aperture diameter = 25 mm
sensorA = sensor(36.0, 24.0)
cameraA = camera(lensA, sensorA)
cameraA.setObjDist(object_distance)
print("Lens A: f = %1.4g, D = %1.4g, F/# = %1.4g, eff F/# = %1.4g" %
(lensA.f, lensA.D, lensA.Fnum, lensA.effFnum))
print("Lens A: minimum resolvable separation on image plane = %1.5g microns" %
(lensA.getMinResolvSepImgPlane()*1000.0))
print("Lens A: minimum resolvable separation on the object plane = %1.5g microns" %
(lensA.getMinResolvSepObjPlane()*1000.0))
print("Camera A: field of view (degrees): horizontal = %1.4g, vertical = %1.4g" %
(cameraA.calculateFOV()[0], cameraA.calculateFOV()[1]))
print("\nCreate a scaled camera B")
# scale lens by a factor of 1/10 and place the scaled lens at the same distance
lensB = lensA.scaleLens(1.0/10)
lensB.setObjDist(object_distance)
sensorB = sensorA.scaleSensor(1.0/10)
cameraB = camera(lensB, sensorB)
print("Lens B: f = %1.4g, D = %1.4g, F/# = %1.4g, eff F/# = %1.4g" %
(lensB.f, lensB.D, lensB.Fnum, lensB.effFnum))
print("Lens B: minimum resolvable separation on image plane = %1.5g microns" %
(lensB.getMinResolvSepImgPlane()*1000.0))
print("Lens B: minimum resolvable separation on the object plane = %1.5g microns" %
(lensB.getMinResolvSepObjPlane()*1000.0))
print("Camera B: field of view (degrees): horizontal = %1.4g, vertical = %1.4g" %
(cameraB.calculateFOV()[0], cameraB.calculateFOV()[1]))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment