The Siemens Star is a radial starburst pattern commonly used to adjust internal focus settings in camera lens and for measuring camera resolution. This notebook produces a Siemens Star test chart in the form in [Scalable Vector Graphics (svg)[(https://en.wikipedia.org/wiki/Scalable_Vector_Graphics) that can be rendered by most modern web browsers. The idea for this notebook was motivated by this post on a forum at Digital Photography Review (DPReview).
This is a Jupyter notebook containing Python functions. An executable version of this notebook is available from the http://mybinder.org/ by clicking on the following button. There may be a longish pause as the notebook loads.
To use the notebook after it has been loaded in an executable environment:
!pip install svglib
Requirement already satisfied: svglib in /Users/jeff/anaconda3/lib/python3.6/site-packages (0.9.0) Requirement already satisfied: tinycss2>=0.6.0 in /Users/jeff/anaconda3/lib/python3.6/site-packages (from svglib) (0.6.1) Requirement already satisfied: lxml in /Users/jeff/anaconda3/lib/python3.6/site-packages (from svglib) (4.2.5) Requirement already satisfied: reportlab in /Users/jeff/anaconda3/lib/python3.6/site-packages (from svglib) (3.5.13) Requirement already satisfied: cssselect2>=0.2.0 in /Users/jeff/anaconda3/lib/python3.6/site-packages (from svglib) (0.2.1) Requirement already satisfied: webencodings>=0.4 in /Users/jeff/anaconda3/lib/python3.6/site-packages (from tinycss2>=0.6.0->svglib) (0.5.1) Requirement already satisfied: pillow>=4.0.0 in /Users/jeff/anaconda3/lib/python3.6/site-packages (from reportlab->svglib) (5.3.0)
The following cell defines functions that create SVG markup. Run this cell first!
from math import pi, sin, cos, sqrt
# page width
page_width = 900
# xrite passport rgb color table
# https://xritephoto.com/ph_product_overview.aspx?ID=820&Action=support&SupportID=5159
rgb = [
[115, 82, 68], # 1. dark skin
[194,150,130], # 2. light skin
[ 98,122,157], # 3. blue sky
[ 87,108, 67], # 4. folliage
[133,128,177], # 5. blue flower
[103,189,170], # 6. bluish green
[214,126, 44], # 7. orange
[ 80, 91,166], # 8. purplish blue
[193, 90, 99], # 9. moderate red
[ 94, 60,108], # 10. purple
[157,188, 64], # 11. yellow green
[224,163, 46], # 12. orange yellow
[ 56, 61,150], # 13. blue
[ 70,148, 73], # 14. green
[175, 54, 60], # 15. red
[231,199, 31], # 16. yellow
[187, 86,149], # 17. magenta
[ 8,133,161], # 18. cyan
[243,243,242], # 19. white (0.05*)
[200,200,200], # 20. neutral 8 (0.23*)
[160,160,160], # 21. neutral 6.5 (0.44*)
[122,122,121], # 22. neutral 5 (0.70*)
[ 85, 85, 85], # 23. neutral 3.5 (1.05*)
[ 52, 52, 52], # 24. black (1.50*)
]
def star(xc, yc, radius, cycles=72, colors=['black', 'white']):
"""Return an svg string containing of polygons in the form of a star."""
phi = pi/cycles/len(colors)
xy = [xc, yc, xc - radius*sin(phi), yc + radius*cos(phi), xc + radius*sin(phi), yc + radius*cos(phi)]
fmt = '<polygon points = "{:.3f},{:.3f} {:.3f},{:.3f} {:.3f},{:.3f}"'.format(*xy) \
+ ' style = "fill:{0}" transform = "rotate({1:.3f},' \
+ ' {:.3f}, {:.3f})"/>\n'.format(xc, yc)
return ''.join([fmt.format(colors[k], 360.0*(n + float(k)/len(colors))/cycles)
for n in range(0, int(cycles)) for k in range(0, len(colors))])
def lphcircles(xc, yc, radius, cycles, colors, bb_height):
lph = [10, 20, 50, 100, 200, 500, 1000, 2000, 5000]
r = [bb_height*cycles*len(colors)/(2*pi*lph) for lph in lph]
fmt = '<circle cx="{0:.3f}" cy="{1:.3f}" r="{2:.3f}" stroke="black" stroke-width="1" fill-opacity="0"/>\n' +\
'<text x="{3:.3f}" y="{4:.3f}" fill="red" font-weight="bold" text-align="center">{5:4.0f}</text>'
return '\n'.join([fmt.format(xc, yc, r, xc+r*cos(13*pi/8), yc + r*sin(13*pi/8), lph)
for (lph,r) in zip(lph,r) if 10 < r < radius]) + '\n'
def colorchecker(bw, bh):
return ''.join(['<rect width="{0}" height="{1}" '.format(bw, bh) +
' x="{0}" y="{1}"'.format(bw*((0.5+col) + 15*(col>1)), bh*(7+row)) +
' style="fill:rgb({0},{1},{2});stroke-width:2;stroke:black"/>\n'.format(*rgb[row + 6*col])
for col in range(0,4) for row in range (0,6)])
def SiemensStar(cycles=144, aspect=4/3, colors=['black','white'], colorcheck='True', cstars='True', lph='True'):
bb_width = page_width
bb_height = bb_width/aspect
r = bb_height*min(0.5, (sqrt(1+aspect**2))/4)
svg = '<svg xmlns="https://www.w3.org/2000/svg"' + \
' width="{0}" height="{1}" style="border:solid 1px" >\n'.format(bb_width, bb_height)
svg += star(bb_width/2, bb_height/2, r, cycles, colors)
svg += lphcircles(0.5*bb_width, 0.5*bb_height, r, cycles, colors, bb_height) if lph else ''
if cstars:
for x,y in [(x,y) for x in (0, bb_width) for y in (0, bb_height)]:
svg += star(x, y, r, cycles, colors)
svg += lphcircles(x, y, r, cycles, colors, bb_height) if lph else ''
svg += colorchecker(0.05*bb_width, 0.05*bb_height) if colorcheck else ''
return svg + '</svg>'
The following cell creates a simple resolution chart consisting of a central Siemens Star and additional elements. The central star consists of alternating radial spokes of color from a specified list colors
. If omitted, the default is black and white. The list may contain two or more colors. The list of colors is repeated a number of times as determined by the parameter cycles
.
A list of svg commands is generated, stored in a file star.svf, and displayed as an inline graphic within the notebook. To download the resulting file, right click on the link and choose the Save Link As ...
option.
from IPython.display import SVG
from svglib.svglib import svg2rlg
from reportlab.graphics import renderPDF, renderPM
svg = SiemensStar(cycles=144, colors = ['blue', 'yellow'])
with open('star.svg', 'w') as f:
f.write(svg)
chart = svg2rlg('star.svg')
renderPDF.drawToFile(chart, 'star.pdf')
renderPM.drawToFile(chart, 'star.png', fmt='PNG')
SVG(svg)
The following interactive cell creates a simple resolution chart consisting of a central Siemens Star and additional elements. Use the dropdown menus to set the colors of the radial spokes, and the cycles slider to set the number of spokes.
A list of svg commands is generated and stored in a file star.html. To download the resulting file, right click on the link and choose the Save Link As ...
option.
from IPython.display import SVG
from ipywidgets import interact, Dropdown, IntSlider, Checkbox
from IPython.display import display
svg_colors = ['black', 'white', 'red', 'blue', 'green', 'cyan', 'yellow', 'magenta']
def makestar(cycles, aspect, color1, color2, colorcheck, cstars, clines):
svg = SiemensStar(cycles, aspect=eval(aspect), colors=[color1,color2], colorcheck=colorcheck, cstars=cstars, lph=clines)
display(SVG(svg))
with open('star.html','w') as f:
f.write(svg)
interact(makestar,
cycles = IntSlider(min=12, max=600, value = 144, step=2, description='Color Cycles'), \
aspect = Dropdown(options=['16/9', '3/2', '4/3', '1/1'], value='4/3', description='Aspect Ratio'), \
color1 = Dropdown(options=svg_colors, value='black', description='Color A'), \
color2 = Dropdown(options=svg_colors, value='white', description='Color B'), \
colorcheck = Checkbox(value=True, description='Color Checker'), \
cstars = Checkbox(value=True, description='Corner Stars'), \
clines = Checkbox(value=True, description='LPH Lines'))
interactive(children=(IntSlider(value=144, description='Color Cycles', max=600, min=12, step=2), Dropdown(desc…
<function __main__.makestar(cycles, aspect, color1, color2, colorcheck, cstars, clines)>