Skip to content

Commit 9339f86

Browse files
committed
Add Python scripts by Glenn Newell for DSO image processing
1 parent e6a8318 commit 9339f86

30 files changed

Lines changed: 886 additions & 1 deletion

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
## stellarium-data
2-
Tools & script for generating data files for Stellarium
2+
Tools & scripts for generating data files for Stellarium
33

44
### Statistics
55
![Github All Releases](https://img.shields.io/github/downloads/Stellarium/stellarium-data/total.svg)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
These python scripts are the same for all three platforms (Windows, Mac OS, and Linux)
2+
3+
Assuming you are using the recommended Anaconda python, and that you installed it for "you", vs. all users,
4+
you should have an "anaconda3" or "Anaconda3" directory just under your home directory (the directory with your user name
5+
as the name).
6+
7+
These two Python scripts should be placed in that "anaconda3" directory.
8+
9+
In Windows you can copy and paste this location into a windows explorer address bar, to get there:
10+
11+
%USERPROFILE%\Anaconda3\
12+
13+
Place the two .py and .bat scripts in %USERPROFILE%\Anaconda3\
14+
15+
Mac OS: Place the two .py script files in the anaconda3 directory, which should be inside the directory with your username.
16+
17+
Linux: Place the two .py script files in the anaconda3 directory, which should be inside the directory with your username.
Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
Created on Sun Aug 25 18:19:14 2019
4+
5+
@author: Glenn C. Newell
6+
"""
7+
import warnings
8+
warnings.filterwarnings(action="ignore")
9+
from astroquery.astrometry_net import AstrometryNet
10+
import astropy.wcs as wcs
11+
import re
12+
import math
13+
from PIL import Image, ImageOps
14+
import os
15+
import sys
16+
17+
18+
19+
def flip_image(image_path, saved_location):
20+
"""
21+
Flip or mirror the image
22+
23+
@param image_path: The path to the image to edit
24+
@param saved_location: Path to save the cropped image
25+
"""
26+
image_obj = Image.open(image_path)
27+
rotated_image = image_obj.transpose(Image.FLIP_LEFT_RIGHT)
28+
rotated_image.save(saved_location)
29+
rotated_image.show()
30+
31+
ast = AstrometryNet()
32+
ast.api_key = 'XXXXXXXXXXXXXXXX'
33+
34+
imagename = sys.argv[1]
35+
#image.show()
36+
#imagename = "D:/Google Drive/Stellarium DSO image addition/buble_full_P-1.jpg"
37+
file, ext = os.path.splitext(imagename)
38+
image = Image.open(imagename)
39+
print('Unstretching image by raising blackpoint (for Plate Solving)')
40+
greyscale = image.copy().convert('L')
41+
42+
stars = ImageOps.colorize(greyscale, black="black", white="white", blackpoint=200, whitepoint=255)
43+
#stars.show()
44+
print('Saving:', file + '_stars.jpg' )
45+
stars.save(file + '_stars.jpg')
46+
print('Uploading:', file + '_stars.jpg' )
47+
48+
#wcs_header = ast.solve_from_image('D:\Google Drive\Stellarium DSO image addition\buble_full_P-1.jpg', force_image_upload=True)
49+
50+
try_again = True
51+
submission_id = None
52+
53+
while try_again:
54+
try:
55+
if not submission_id:
56+
#"C:\Users\gnewell\Google Drive\Stellarium DSO image addition\buble_full_P-1.jpg"
57+
#"D:/Google Drive/Stellarium DSO image addition/buble_full_P-1.jpg"
58+
wcs_header = ast.solve_from_image(file + '_stars.jpg' , force_image_upload=True, parity=2, scale_units='degwidth', scale_type='ul',
59+
scale_lower=0.1, scale_upper=12.0, solve_timeout=620, downsample_factor=2, submission_id=submission_id)
60+
else:
61+
wcs_header = ast.monitor_submission(submission_id,
62+
solve_timeout=600)
63+
except TimeoutError as e:
64+
submission_id = e.args[1]
65+
else:
66+
# got a result, so terminate
67+
try_again = False
68+
69+
if wcs_header:
70+
# Code to execute when solve succeeds
71+
#print(wcs_header)
72+
#w = wcs_header
73+
w = wcs.WCS(wcs_header)
74+
#hdu = hdulist[0]
75+
#hdr = hdulist[0].header
76+
#hdulist.close()
77+
78+
#print(repr(wcs_header))
79+
80+
print('\nImage Width x Height:',wcs_header['IMAGEW'],'x', wcs_header['IMAGEH'])
81+
for comment in wcs_header['comment']:
82+
#print(comment)
83+
if re.match('^scale:',comment):
84+
print(comment)
85+
l = []
86+
for t in comment.split():
87+
try:
88+
l.append(float(t))
89+
except ValueError:
90+
pass
91+
scale = l[-1]
92+
#print(scale)
93+
94+
cd11 = wcs_header['CD1_1']
95+
cd12 = wcs_header['CD1_2']
96+
cd21 = wcs_header['CD2_1']
97+
cd22 = wcs_header['CD2_2']
98+
if cd11 * cd22 - cd12 * cd21 > 0:
99+
parity = 1
100+
else:
101+
parity = -1
102+
103+
print('Image Parity: ',parity)
104+
orientation = math.degrees(math.atan2(cd21-cd12, cd11+cd22))
105+
print('Image Orientation: {0:.1f}'.format(180 + orientation),'East of North')
106+
107+
#print('Press Enter to exit')
108+
#input()
109+
110+
else:
111+
print('Failure to solve input image')
112+
# Code to execute when solve fails
113+
raise SystemExit
114+
imgresized = image.copy()
115+
rescale = 1.0
116+
if scale < 1.0:
117+
rescale = 0.5
118+
if scale < 0.5:
119+
rescale = 0.25
120+
size = int(rescale * int(wcs_header['IMAGEW'])), int(rescale * int(wcs_header['IMAGEH']))
121+
imgresized = image.resize(size)
122+
123+
imgflipped = imgresized.copy()
124+
if parity == -1:
125+
print('Flipping image to correct Parity')
126+
imgflipped.transpose(Image.FLIP_LEFT_RIGHT)
127+
#image2.show()
128+
ccw = 180.01 - orientation
129+
print('Rotating image',ccw, 'ccw so North is Up')
130+
imgrotated = imgflipped.rotate(ccw, expand=1)
131+
#imgrotated.show()
132+
width, height = imgrotated.size
133+
print('New size: ',width, height)
134+
if width >= height:
135+
ledgewidth = True
136+
ledgeheight = False
137+
else:
138+
ledgewidth = False
139+
ledgeheight = True
140+
scaledone = False
141+
print('Scaling long edge to 512, 1024, or 2048')
142+
if ledgewidth == True:
143+
if width > 2048:
144+
rescale2 = 2048 / width
145+
size = int(rescale2 * width), int(rescale2 * height)
146+
imgreresized = imgrotated.resize(size)
147+
scaledone = True
148+
if (scaledone == False) and (width > 1024):
149+
rescale2 = 1024 / width
150+
size = int(rescale2 * width), int(rescale2 * height)
151+
imgreresized = imgrotated.resize(size)
152+
scaledone = True
153+
if scaledone == False:
154+
rescale2 = 512 / width
155+
size = int(rescale2 * width), int(rescale2 * height)
156+
imgreresized = imgrotated.resize(size)
157+
scaledone = True
158+
if ledgeheight == True:
159+
if height > 2048:
160+
rescale2 = 2048 / height
161+
size = int(rescale2 * width), int(rescale2 * height)
162+
imgreresized = imgrotated.resize(size)
163+
scaledone = True
164+
if scaledone == False and height > 1024:
165+
rescale2 = 1024 / height
166+
size = int(rescale2 * width), int(rescale2 * height)
167+
imgreresized = imgrotated.resize(size)
168+
scaledone = True
169+
if scaledone == False:
170+
rescale2 = 512 / height
171+
size = int(rescale2 * width), int(rescale2 * height)
172+
imgreresized = imgrotated.resize(size)
173+
scaledone = True
174+
#imgreresized.show()
175+
width, height = imgreresized.size
176+
print('New size: ',width, height)
177+
print('Padding short edge to 512, 1024, or 2048')
178+
paddone = False
179+
delta_w = 0
180+
delta_h = 0
181+
if ledgewidth == True:
182+
if height > 1024:
183+
delta_h = 2048 - height
184+
paddone = True
185+
if paddone == False and height > 512:
186+
delta_h = 1024 - height
187+
paddone = True
188+
if paddone == False:
189+
delta_h = 512 - height
190+
if ledgeheight == True:
191+
if width > 1024:
192+
delta_w = 2048 - width
193+
paddone = True
194+
if paddone == False and width > 512:
195+
delta_w = 1024 - width
196+
paddone = True
197+
if paddone == False:
198+
delta_w = 512 - width
199+
padding = (delta_w//2, delta_h//2, delta_w-(delta_w//2), delta_h-(delta_h//2))
200+
finalimage = ImageOps.expand(imgreresized, padding)
201+
width, height = finalimage.size
202+
print('Final size: ',width, height)
203+
#finalimage.show()
204+
print('Saving final image:', file + '_for_Stellarium.png')
205+
finalimage.save(file + '_for_Stellarium.png')
206+
print('Unstretching image by raising blackpoint (for Plate Solving)')
207+
greyscale = finalimage.copy().convert('L')
208+
209+
stars = ImageOps.colorize(greyscale, black="black", white="white", blackpoint=200, whitepoint=255)
210+
#stars.show()
211+
print('Saving:', file + '_for_Stellarium_stars.jpg' )
212+
stars.save(file + '_for_Stellarium_stars.jpg')
213+
try_again = True
214+
submission_id = None
215+
newscale = scale / rescale / rescale2
216+
217+
newll = newscale * width /3600 / 1.2
218+
newul = newscale * width /3600 * 1.2
219+
print('Scale est:',newscale,'arcsecperpix (for Plate Solving) Lower Limit:', newll, 'Upper Limit:', newul, 'degwidth')
220+
print('Uploading:', file + '_for_Stellarium_stars.jpg')
221+
while try_again:
222+
try:
223+
if not submission_id:
224+
#wcs_header2 = ast.solve_from_image(file + '_for_Stellarium_stars.jpg', force_image_upload=True, parity=0, scale_units='arcsecperpix', scale_type='ev',
225+
# solve_timeout=620, scale_est=newscale, scale_err=20, downsample_factor=2, submission_id=submission_id)
226+
wcs_header2 = ast.solve_from_image(file + '_for_Stellarium_stars.jpg', force_image_upload=True, parity=1, scale_units='degwidth', scale_type='ul',
227+
solve_timeout=620, scale_lower=newll, scale_upper=newul, downsample_factor=2, submission_id=submission_id)
228+
else:
229+
wcs_header2 = ast.monitor_submission(submission_id,
230+
solve_timeout=600)
231+
except TimeoutError as e:
232+
submission_id = e.args[1]
233+
else:
234+
# got a result, so terminate
235+
try_again = False
236+
237+
if wcs_header2:
238+
w2 = wcs.WCS(wcs_header2)
239+
# Code to execute when solve succeeds
240+
wx1, wy1 = w2.all_pix2world(0., 0., 0)
241+
#print('0,0: {0} {1}'.format(wx1, wy1))
242+
243+
wx2, wy2= w2.all_pix2world(wcs_header2['IMAGEW']-1,0, 0)
244+
#print(wcs_header2['IMAGEW']-1,0,': {0} {1}'.format(wx2, wy2))
245+
246+
247+
wx3, wy3 = w2.all_pix2world(wcs_header2['IMAGEW']-1, wcs_header2['IMAGEH']-1, 0)
248+
#print(wcs_header2['IMAGEW']-1, wcs_header2['IMAGEH']-1,': {0} {1}'.format(wx3, wy3))
249+
250+
wx4, wy4 = w2.all_pix2world(0., wcs_header2['IMAGEH']-1, 0)
251+
#print(0, wcs_header2['IMAGEH']-1,': {0} {1}'.format(wx4, wy4))
252+
i_n = '\"' + os.path.basename(file + '_for_Stellarium.png') + '\",'
253+
print('\nSaving Entry for Stellarium textures.json in:',file + '_for_Stellarium.json')
254+
if os.path.exists(file + '_for_Stellarium.json'):
255+
os.remove(file + '_for_Stellarium.json')
256+
json = open(file + '_for_Stellarium.json', "a")
257+
print('\t{\n\t\t\"imageCredits\" : {\"Short\": \"Mine\", \"infoUrl\": \"\"},\n\t\t\"imageUrl\" :',i_n, file=json)
258+
print('\t\t\"worldCoords" : [[[{0:.4f}, {1:.4f}], [{2:.4f}, {3:.4f}], [{4:.4f}, {5:.4f}], [{6:.4f}, {7:.4f}]]],'.format(wx4, wy4, wx3, wy3, wx2, wy2, wx1, wy1), file=json)
259+
print('\t\t\"textureCoords\" : [[[0,0], [1,0], [1,1], [0,1]]],\n\t\t\"minResolution\" : 0.2,\n\t\t\"maxBrightness\" : 12.0\n\t},', file=json)
260+
json.close()
261+
else:
262+
print('Failure to solve final image')
263+
# Code to execute when solve fails
264+
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# -*- coding: utf-8 -*-
2+
from astropy.io import fits
3+
import astropy.wcs as wcs
4+
import sys
5+
import re
6+
import math
7+
8+
filename = sys.argv[-1]
9+
#filename = 'D:\Glenn\Downloads\wcs (2).fits'
10+
hdulist = fits.open(filename)
11+
w = wcs.WCS(hdulist[(0)].header, hdulist)
12+
hdu = hdulist[0]
13+
hdr = hdulist[0].header
14+
hdulist.close()
15+
16+
print(repr(w))
17+
18+
print('Image Width x Height:',hdu.header['IMAGEW'],'x', hdu.header['IMAGEH'])
19+
for comment in hdr['comment']:
20+
#print(comment)
21+
if re.match('^scale:',comment):
22+
print(comment)
23+
24+
cd11 = hdu.header['CD1_1']
25+
cd12 = hdu.header['CD1_2']
26+
cd21 = hdu.header['CD2_1']
27+
cd22 = hdu.header['CD2_2']
28+
if cd11 * cd22 - cd12 * cd21 > 0:
29+
parity = 1
30+
else:
31+
parity = -1
32+
33+
print('Image Parity: ',parity)
34+
orientation = math.degrees(math.atan2(cd21-cd12, cd11+cd22))
35+
print('Image Orientation: {0:.1f}'.format(180 + orientation),'East of North')
36+
37+
wx1, wy1 = w.all_pix2world(0., 0., 0)
38+
#print('0,0: {0} {1}'.format(wx1, wy1))
39+
40+
wx2, wy2= w.all_pix2world(hdu.header['IMAGEW']-1,0, 0)
41+
#print(hdu.header['IMAGEW']-1,0,': {0} {1}'.format(wx2, wy2))
42+
43+
44+
wx3, wy3 = w.all_pix2world(hdu.header['IMAGEW']-1, hdu.header['IMAGEH']-1, 0)
45+
#print(hdu.header['IMAGEW']-1, hdu.header['IMAGEH']-1,': {0} {1}'.format(wx3, wy3))
46+
47+
wx4, wy4 = w.all_pix2world(0., hdu.header['IMAGEH']-1, 0)
48+
#print(0, hdu.header['IMAGEH']-1,': {0} {1}'.format(wx4, wy4))
49+
print('\nworld coordinates of image corners for Stellarium:')
50+
print('\n"worldCoords" : [[[{0:.4f}, {1:.4f}], [{2:.4f}, {3:.4f}], [{4:.4f}, {5:.4f}], [{6:.4f}, {7:.4f}]]],'.format(wx4, wy4, wx3, wy3, wx2, wy2, wx1, wy1))
51+
#print('Press Enter to exit')
52+
#input()
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Assuming you are using some flavor of a Gnome desktop, you can use the files in this directory as drag and drop icons that will
2+
run the python scripts on your dropped files.
3+
4+
These two .desktop files should be placed on your desktop, or wherever you keep astro imaging tools. You will have to set the files to have
5+
execute permission (chmod 755 or by using your desktop properties/permissions GUI).
6+
7+
You can then drag and drop one or more images on the Stellarium_Nebulae_Image_Prep desktop icon, or one or more wcs.fits files
8+
on the WCS_corners desktop icon and the Python scripts will run for each dropped file.
9+
10+
One significant caveat is that filenames containing spaces don't seem to work. If by chance you can modify the desktop files to
11+
make it work (in a way that doesn't have paths specific to your particular case) please let me know!
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[Desktop Entry]
2+
Encoding=UTF-8
3+
Name=Stellarium_Nebulae_Image_Prep
4+
Comment=Execute the script with the file dropped
5+
Exec=gnome-terminal -e "sh -c '\$HOME/anaconda3/bin/python \$HOME/anaconda3/Stellarium_Nebulae_Image_Prep.py \"%u\"'"
6+
Icon=utilities-terminal
7+
Type=Application
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[Desktop Entry]
2+
Encoding=UTF-8
3+
Name=WCS_corners
4+
Comment=Execute the script with the file dropped
5+
Exec=gnome-terminal -e "sh -c '\$HOME/anaconda3/bin/python \$HOME/anaconda3/WCS_corners.py \"%u\"'"
6+
Icon=utilities-terminal
7+
Type=Application
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
These two .app files should be placed on your desktop, or wherever you keep astro imaging tools
2+
3+
You can then drag and drop one or more images on the Stellarium_Nebulae_Image_Prep app icon, or one or more wcs.fits files
4+
on the WCS_corners app icon, and the python scripts will run for each dropped file.
5+

0 commit comments

Comments
 (0)