Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ssp-software
data-access-tools
Commits
1c6674e4
Commit
1c6674e4
authored
Aug 24, 2021
by
Sogo Mineo
Browse files
add pdr3/maskViewer
parent
551f1509
Changes
2
Hide whitespace changes
Inline
Side-by-side
pdr3/maskViewer/README.md
0 → 100644
View file @
1c6674e4
maskViewer
============================================================
This is a mask viewer for products of hscPipe,
designed to work without the need for the LSST stack.
Requirements
------------------------------------------------------------
*
python >= 2.7 or python >= 3.6
*
astropy
*
numpy
*
ds9,
*xpaaccess*
,
*xpaset*
Usage
------------------------------------------------------------
### Example
maskViewer.py calexp-HSC-I-0-4,4.fits \
detected=tomato \
cr=purple \
bright_object=palegoldenrod
### Details
maskViewer.py FITS MASK=COLOR [MASK=COLOR ...]
#### FITS file path
You can specify the HDU to view by suffixing
`[...]`
to the image path.
For example,
maskViewer.py calexp-HSC-I-0-4,4.fits[3] ...
maskViewer.py calexp-HSC-I-0-4,4.fits[VARIANCE] ...
#### colors
Colors can be
*
`#rgb`
(e.g.
`#fff`
for white),
*
`#rrggbb`
(e.g.
`#000000`
for black),
*
or a CSS color name.
You can see available CSS color names by:
maskViewer.py --show colors
#### masks
You can see available masks by:
maskViewer.py FITS --show masks
You will, for instance, see:
MP_BAD
MP_BRIGHT_OBJECT
MP_CLIPPED
MP_CR
MP_CROSSTALK
MP_DETECTED
MP_DETECTED_NEGATIVE
MP_EDGE
To view
`MP_DETECTED`
, write it in the command line
with or without the prefix
`MP_`
. Mask names are
case insensitive. All of the following command lines
are valid:
*
`maskViewer.py FITS MP_DETECTED=white`
*
`maskViewer.py FITS DETECTED=white`
*
`maskViewer.py FITS detected=white`
Copyright
------------------------------------------------------------
This script is distributed under Creative Commons 0 (CC0).
To the extent possible under law, the person who associated CC0
with this work has waived all copyright and related or neighboring
rights to this work.
pdr3/maskViewer/maskViewer.py
0 → 100755
View file @
1c6674e4
#!/bin/sh
# This script is distributed under Creative Commons 0 (CC0).
# To the extent possible under law, the person who associated CC0
# with this work has waived all copyright and related or neighboring
# rights to this work.
""":"
if command -v python3 &> /dev/null
then
exec python3 "$0" "$@"
elif command -v python &> /dev/null
then
exec python "$0" "$@"
else
echo "Python interpreter not found." 1>&2
fi
exit 1
":"""
from
__future__
import
print_function
,
unicode_literals
try
:
import
astropy.io.fits
as
pyfits
except
ImportError
as
e
:
try
:
import
pyfits
except
ImportError
:
raise
e
import
numpy
import
argparse
import
io
import
re
import
subprocess
import
time
import
warnings
warnings
.
filterwarnings
(
"ignore"
,
module
=
"astropy.io.fits.card"
)
def
startup
():
parser
=
argparse
.
ArgumentParser
(
description
=
"""
Show a specific mask plane in a FITS file.
"""
)
parser
.
add_argument
(
"fits"
,
nargs
=
"?"
,
type
=
fits_load_image
,
help
=
"""
Input FITS file.
"""
)
parser
.
add_argument
(
"masks"
,
metavar
=
"mask[=color]"
,
nargs
=
"*"
,
type
=
maskcolor_parse
,
help
=
"""
Name of mask to show. `color` may be #rgb or #rrggbb or a color name.
"""
)
parser
.
add_argument
(
"--mask"
,
type
=
fits_load_mask
,
help
=
"""
Mask image alternative to what is contained in the input FITS file.
"""
)
parser
.
add_argument
(
"--transparency"
,
type
=
int
,
default
=
60
,
help
=
"""
Mask transparency (in [0, 100], default 60).
"""
)
parser
.
add_argument
(
"--no-photoshop"
,
action
=
"store_true"
,
help
=
"""
Don't do any color corrections to the image.
"""
)
parser
.
add_argument
(
"--show"
,
metavar
=
"(colors|masks)"
,
choices
=
[
"colors"
,
"masks"
],
help
=
"""
Show color names or mask names, and exit.
"""
)
args
=
parser
.
parse_args
()
if
args
.
show
==
"colors"
:
return
show_colors
()
if
args
.
fits
is
None
:
raise
RuntimeError
(
"Specify a FITS file."
)
if
args
.
mask
is
not
None
:
args
.
fits
=
(
args
.
fits
[
0
],
args
.
mask
)
if
args
.
fits
[
1
]
is
None
:
raise
RuntimeError
(
"Specify a mask by means of --mask="
)
if
args
.
show
==
"masks"
:
return
show_masks
(
args
.
fits
[
1
])
if
not
args
.
masks
:
raise
RuntimeError
(
"Specify masks to show."
)
return
main
(
args
.
fits
[
0
],
args
.
fits
[
1
],
args
.
masks
,
transparency
=
args
.
transparency
,
photoshop
=
not
args
.
no_photoshop
,
)
def
main
(
imagehdu
,
maskhdu
,
masks
,
transparency
=
60
,
photoshop
=
True
):
"""
@param imagehdu: a header-data unit of a FITS
@param maskhdu: a header-data unit of a FITS
@param masks (List[Tuple[str, ColorSpec]]): list of mask-name and its color
"""
maskheader
=
maskhdu
.
header
masks
=
[(
maskheader
[
name
],
color
)
for
name
,
color
in
masks
]
transparency
=
max
(
0
,
min
(
int
(
transparency
),
100
))
ds9
=
Ds9
()
ds9
.
set_fits
(
fits_to_bytes
(
imagehdu
))
for
maskbit
,
color
in
masks
:
data
=
((
maskhdu
.
data
&
(
1
<<
maskbit
))
!=
0
).
astype
(
numpy
.
uint8
)
ds9
.
set_mask
(
fits_to_bytes
(
pyfits
.
PrimaryHDU
(
data
=
data
)),
str
(
color
))
ds9
.
xpaset
([
"mask"
,
"transparency"
,
str
(
transparency
)])
if
photoshop
:
ds9
.
xpaset
([
"zscale"
])
def
show_colors
():
for
name
,
value
in
sorted
(
ColorSpec
.
csscolors
.
items
()):
print
(
name
,
":"
,
value
)
def
show_masks
(
hdu
):
masks
=
sorted
(
key
for
key
in
hdu
.
header
.
keys
()
if
key
.
startswith
(
"MP_"
))
for
mask
in
masks
:
print
(
mask
)
def
fits_load_image
(
path
):
"""
@param path (str): Path to a FITS file.
@return The image HDU and the mask HDU in the FITS file.
"""
m
=
re
.
match
(
r
"^(.*)\[([^/\]\\]+)\]$"
,
path
)
if
m
:
path
,
index
=
m
.
groups
()
try
:
index
=
int
(
index
)
except
ValueError
:
pass
else
:
index
=
None
hdus
=
pyfits
.
open
(
path
,
uint
=
True
)
if
len
(
hdus
)
==
1
and
(
index
is
None
):
return
(
hdus
[
0
],
None
)
if
index
is
None
:
index
=
"IMAGE"
image
=
None
mask
=
None
for
i
,
hdu
in
enumerate
(
hdus
):
header
=
hdu
.
header
exttype
=
header
.
get
(
"EXTTYPE"
)
or
header
.
get
(
"EXTNAME"
)
if
i
==
index
or
exttype
==
index
:
image
=
hdu
elif
exttype
==
"MASK"
:
mask
=
fits_ensure_int_image
(
hdu
)
if
image
:
return
(
image
,
mask
)
raise
RuntimeError
(
"FITS file doesn't contain an image hdu: "
+
path
)
def
fits_load_mask
(
path
):
"""
@param path (str): Path to a FITS file.
@return The mask HDU in the FITS file.
"""
m
=
re
.
match
(
r
"^(.*)\[([^/\]\\]+)\]$"
,
path
)
if
m
:
path
,
index
=
m
.
groups
()
try
:
index
=
int
(
index
)
except
ValueError
:
pass
else
:
index
=
None
hdus
=
pyfits
.
open
(
path
,
uint
=
True
)
if
len
(
hdus
)
==
1
and
(
index
is
None
):
return
hdus
[
0
]
if
index
is
None
:
index
=
"MASK"
mask
=
None
for
i
,
hdu
in
enumerate
(
hdus
):
header
=
hdu
.
header
exttype
=
header
.
get
(
"EXTTYPE"
)
or
header
.
get
(
"EXTNAME"
)
if
i
==
index
or
exttype
==
index
:
mask
=
fits_ensure_int_image
(
hdu
)
break
if
mask
:
return
mask
raise
RuntimeError
(
"FITS file doesn't contain a mask hdu: "
+
path
)
def
fits_ensure_int_image
(
hdu
):
"""
Ensure that hdu is an integer image HDU.
It may be a bug of astropy but an integer image HDU is sometimes
converted to float one even if its header doesn't contain BZERO or BSCALE.
"""
if
hdu
.
data
.
dtype
.
kind
in
"iu"
:
# Already integer
return
hdu
if
(
hdu
.
data
.
dtype
.
kind
==
"f"
and
numpy
.
all
(
numpy
.
floor
(
hdu
.
data
)
==
hdu
.
data
)
):
leftmost_bit
=
max
(
bitplace
for
key
,
bitplace
in
hdu
.
header
.
items
()
if
key
.
startswith
(
"MP_"
)
and
isinstance
(
bitplace
,
int
)
)
if
leftmost_bit
<
8
:
dtype
=
numpy
.
uint8
elif
leftmost_bit
<
16
:
dtype
=
numpy
.
uint16
elif
leftmost_bit
<
32
:
dtype
=
numpy
.
uint32
else
:
dtype
=
numpy
.
uint64
return
type
(
hdu
)(
data
=
hdu
.
data
.
astype
(
dtype
),
header
=
hdu
.
header
)
raise
RuntimeError
(
"Mask data is not integer."
)
def
fits_to_bytes
(
hdus
):
memfile
=
io
.
BytesIO
()
hdus
.
writeto
(
memfile
)
return
memfile
.
getvalue
()
class
Ds9
(
object
):
__slots__
=
(
"__pipe"
,
)
def
__init__
(
self
):
self
.
__pipe
=
None
self
.
launch
()
def
launch
(
self
):
n_tries
=
3
waittime
=
10
if
self
.
__check_xpaaccess
():
return
for
i_try
in
range
(
n_tries
):
if
(
self
.
__pipe
is
not
None
)
and
(
self
.
__pipe
.
poll
()
is
not
None
):
self
.
__pipe
.
wait
()
self
.
__pipe
=
None
if
self
.
__pipe
is
None
:
self
.
__pipe
=
subprocess
.
Popen
([
"ds9"
])
for
_
in
range
(
waittime
):
if
self
.
__check_xpaaccess
():
return
time
.
sleep
(
1
)
raise
RuntimeError
(
"Failed to launch ds9 ."
)
def
set_fits
(
self
,
data
):
"""
@param data (bytes): FITS file
"""
self
.
launch
()
try
:
self
.
xpaset
([
"fits"
],
data
)
except
Exception
:
self
.
xpaset
([
"fits"
,
"new"
],
data
)
def
set_mask
(
self
,
data
,
color
):
"""
@param data (bytes): FITS file
@param color ("#rrggbb")
"""
self
.
launch
()
self
.
xpaset
([
"mask"
,
"color"
,
color
])
self
.
xpaset
([
"fits"
,
"mask"
],
data
)
@
staticmethod
def
__check_xpaaccess
():
p
=
subprocess
.
Popen
([
"xpaaccess"
,
"ds9"
],
stdout
=
subprocess
.
PIPE
)
try
:
msg
=
p
.
stdout
.
read
().
decode
(
"utf-8"
)
finally
:
p
.
wait
()
return
msg
.
strip
()
==
"yes"
@
staticmethod
def
xpaset
(
command
,
data
=
b
""
):
p
=
subprocess
.
Popen
(
[
"xpaset"
,
"ds9"
]
+
command
,
stdin
=
subprocess
.
PIPE
,
stdout
=
subprocess
.
PIPE
,
stderr
=
subprocess
.
PIPE
,
)
try
:
out
,
err
=
[
x
.
decode
(
"utf-8"
)
for
x
in
p
.
communicate
(
data
)]
finally
:
p
.
wait
()
if
err
.
startswith
(
"XPA$ERROR"
):
raise
RuntimeError
(
err
)
def
maskcolor_parse
(
s
):
"""
@param s (str): "maskname=color"
@return (Tuple[str, ColorSpec])
"""
s
=
s
.
split
(
"="
,
1
)
if
len
(
s
)
==
1
:
return
maskname_normalize
(
s
[
0
]),
ColorSpec
(
"tomato"
)
if
len
(
s
)
==
2
:
return
maskname_normalize
(
s
[
0
]),
ColorSpec
(
s
[
1
])
def
maskname_normalize
(
s
):
"""
@param s (str): Mask name with or without the prefix "MP_". Case insensitive.
@return (str): "MP_..."
"""
s
=
s
.
upper
()
if
s
.
startswith
(
"MP_"
):
return
s
else
:
return
"MP_"
+
s
class
ColorSpec
(
object
):
__slots__
=
(
"r"
,
"g"
,
"b"
)
# [0, 255]
def
__init__
(
self
,
spec
):
"""
@param spec (str):
* "#rgb", "#rgb,a"
* "#rrggbb", "#rrggbb"
* "name", "name"
"""
spec
=
ColorSpec
.
csscolors
.
get
(
spec
,
spec
)
m
=
re
.
match
(
"#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$"
,
spec
)
if
m
:
r
,
g
,
b
=
m
.
groups
()
self
.
r
=
int
(
r
,
16
)
self
.
g
=
int
(
g
,
16
)
self
.
b
=
int
(
b
,
16
)
return
m
=
re
.
match
(
"#([0-9a-f])([0-9a-f])([0-9a-f])$"
,
color
)
if
m
:
r
,
g
,
b
=
m
.
groups
()
self
.
r
=
int
(
r
+
r
,
16
)
self
.
g
=
int
(
g
+
g
,
16
)
self
.
b
=
int
(
b
+
b
,
16
)
return
def
__str__
(
self
):
return
"#{self.r:02x}{self.g:02x}{self.b:02x}"
.
format
(
self
=
self
)
csscolors
=
{
"black"
:
"#000000"
,
"silver"
:
"#c0c0c0"
,
"gray"
:
"#808080"
,
"white"
:
"#ffffff"
,
"maroon"
:
"#800000"
,
"red"
:
"#ff0000"
,
"purple"
:
"#800080"
,
"fuchsia"
:
"#ff00ff"
,
"green"
:
"#008000"
,
"lime"
:
"#00ff00"
,
"olive"
:
"#808000"
,
"yellow"
:
"#ffff00"
,
"navy"
:
"#000080"
,
"blue"
:
"#0000ff"
,
"teal"
:
"#008080"
,
"aqua"
:
"#00ffff"
,
"orange"
:
"#ffa500"
,
"aliceblue"
:
"#f0f8ff"
,
"antiquewhite"
:
"#faebd7"
,
"aquamarine"
:
"#7fffd4"
,
"azure"
:
"#f0ffff"
,
"beige"
:
"#f5f5dc"
,
"bisque"
:
"#ffe4c4"
,
"blanchedalmond"
:
"#ffebcd"
,
"blueviolet"
:
"#8a2be2"
,
"brown"
:
"#a52a2a"
,
"burlywood"
:
"#deb887"
,
"cadetblue"
:
"#5f9ea0"
,
"chartreuse"
:
"#7fff00"
,
"chocolate"
:
"#d2691e"
,
"coral"
:
"#ff7f50"
,
"cornflowerblue"
:
"#6495ed"
,
"cornsilk"
:
"#fff8dc"
,
"crimson"
:
"#dc143c"
,
"cyan"
:
"#00ffff"
,
"darkblue"
:
"#00008b"
,
"darkcyan"
:
"#008b8b"
,
"darkgoldenrod"
:
"#b8860b"
,
"darkgray"
:
"#a9a9a9"
,
"darkgreen"
:
"#006400"
,
"darkgrey"
:
"#a9a9a9"
,
"darkkhaki"
:
"#bdb76b"
,
"darkmagenta"
:
"#8b008b"
,
"darkolivegreen"
:
"#556b2f"
,
"darkorange"
:
"#ff8c00"
,
"darkorchid"
:
"#9932cc"
,
"darkred"
:
"#8b0000"
,
"darksalmon"
:
"#e9967a"
,
"darkseagreen"
:
"#8fbc8f"
,
"darkslateblue"
:
"#483d8b"
,
"darkslategray"
:
"#2f4f4f"
,
"darkslategrey"
:
"#2f4f4f"
,
"darkturquoise"
:
"#00ced1"
,
"darkviolet"
:
"#9400d3"
,
"deeppink"
:
"#ff1493"
,
"deepskyblue"
:
"#00bfff"
,
"dimgray"
:
"#696969"
,
"dimgrey"
:
"#696969"
,
"dodgerblue"
:
"#1e90ff"
,
"firebrick"
:
"#b22222"
,
"floralwhite"
:
"#fffaf0"
,
"forestgreen"
:
"#228b22"
,
"gainsboro"
:
"#dcdcdc"
,
"ghostwhite"
:
"#f8f8ff"
,
"gold"
:
"#ffd700"
,
"goldenrod"
:
"#daa520"
,
"greenyellow"
:
"#adff2f"
,
"grey"
:
"#808080"
,
"honeydew"
:
"#f0fff0"
,
"hotpink"
:
"#ff69b4"
,
"indianred"
:
"#cd5c5c"
,
"indigo"
:
"#4b0082"
,
"ivory"
:
"#fffff0"
,
"khaki"
:
"#f0e68c"
,
"lavender"
:
"#e6e6fa"
,
"lavenderblush"
:
"#fff0f5"
,
"lawngreen"
:
"#7cfc00"
,
"lemonchiffon"
:
"#fffacd"
,
"lightblue"
:
"#add8e6"
,
"lightcoral"
:
"#f08080"
,
"lightcyan"
:
"#e0ffff"
,
"lightgoldenrodyellow"
:
"#fafad2"
,
"lightgray"
:
"#d3d3d3"
,
"lightgreen"
:
"#90ee90"
,
"lightgrey"
:
"#d3d3d3"
,
"lightpink"
:
"#ffb6c1"
,
"lightsalmon"
:
"#ffa07a"
,
"lightseagreen"
:
"#20b2aa"
,
"lightskyblue"
:
"#87cefa"
,
"lightslategray"
:
"#778899"
,
"lightslategrey"
:
"#778899"
,
"lightsteelblue"
:
"#b0c4de"
,
"lightyellow"
:
"#ffffe0"
,
"limegreen"
:
"#32cd32"
,
"linen"
:
"#faf0e6"
,
"magenta"
:
"#ff00ff"
,
"mediumaquamarine"
:
"#66cdaa"
,
"mediumblue"
:
"#0000cd"
,
"mediumorchid"
:
"#ba55d3"
,
"mediumpurple"
:
"#9370db"
,
"mediumseagreen"
:
"#3cb371"
,
"mediumslateblue"
:
"#7b68ee"
,
"mediumspringgreen"
:
"#00fa9a"
,
"mediumturquoise"
:
"#48d1cc"
,
"mediumvioletred"
:
"#c71585"
,
"midnightblue"
:
"#191970"
,
"mintcream"
:
"#f5fffa"
,
"mistyrose"
:
"#ffe4e1"
,
"moccasin"
:
"#ffe4b5"
,
"navajowhite"
:
"#ffdead"
,
"oldlace"
:
"#fdf5e6"
,
"olivedrab"
:
"#6b8e23"
,
"orangered"
:
"#ff4500"
,
"orchid"
:
"#da70d6"
,
"palegoldenrod"
:
"#eee8aa"
,
"palegreen"
:
"#98fb98"
,
"paleturquoise"
:
"#afeeee"
,
"palevioletred"
:
"#db7093"
,
"papayawhip"
:
"#ffefd5"
,
"peachpuff"
:
"#ffdab9"
,
"peru"
:
"#cd853f"
,
"pink"
:
"#ffc0cb"
,
"plum"
:
"#dda0dd"
,
"powderblue"
:
"#b0e0e6"
,
"rosybrown"
:
"#bc8f8f"
,
"royalblue"
:
"#4169e1"
,
"saddlebrown"
:
"#8b4513"
,
"salmon"
:
"#fa8072"
,
"sandybrown"
:
"#f4a460"
,
"seagreen"
:
"#2e8b57"
,
"seashell"
:
"#fff5ee"
,
"sienna"
:
"#a0522d"
,
"skyblue"
:
"#87ceeb"
,
"slateblue"
:
"#6a5acd"
,
"slategray"
:
"#708090"
,
"slategrey"
:
"#708090"
,
"snow"
:
"#fffafa"
,
"springgreen"
:
"#00ff7f"
,
"steelblue"
:
"#4682b4"
,
"tan"
:
"#d2b48c"
,
"thistle"
:
"#d8bfd8"
,
"tomato"
:
"#ff6347"
,
"turquoise"
:
"#40e0d0"
,
"violet"
:
"#ee82ee"
,
"wheat"
:
"#f5deb3"
,
"whitesmoke"
:
"#f5f5f5"
,
"yellowgreen"
:
"#9acd32"
,
"rebeccapurple"
:
"#663399"
,
}
if
__name__
==
"__main__"
:
startup
()
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment