# -*- 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