├── .gitignore ├── LICENSE ├── README ├── adaptive_ggm.py ├── gaussian_gradient_magnitude.py ├── ggm_combine ├── README ├── curvecontrol.py ├── ggm_combine.yml ├── interactive.py └── qt.py └── hideregions2 ├── Makefile ├── README ├── dm ├── Makefile ├── block.cc ├── block.hh ├── coord.cc ├── coord.hh ├── dataset.cc ├── dataset.hh ├── descriptor.cc ├── descriptor.hh ├── dm.hh ├── exception.hh ├── general.cc ├── general.hh ├── image.cc ├── image.hh ├── memimage.cc ├── memimage.hh └── test.cc └── hideregions2.cc /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | test.out 3 | *~ 4 | *.a 5 | hideregions2/hideregions2 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This repository contains useful utilities for Gaussian gradient 2 | filtering of astronomical FITS images. 3 | 4 | The programs are described in 5 | 6 | A very deep Chandra view of metals, sloshing and feedback in the 7 | Centaurus cluster of galaxies, J.S. Sanders, A.C. Fabian, G.B. Taylor, 8 | H.R. Russell, K.M. Blundell, R.E.A. Canning, J. Hlavacek-Larrondo, 9 | S.A. Walker, C.K. Grimes, 2016, MNRAS, 457, 82 10 | http://adsabs.harvard.edu/abs/2016MNRAS.457...82S 11 | 12 | and 13 | 14 | Detecting edges in the X-ray surface brightness of galaxy clusters, 15 | J.S. Sanders, A.C. Fabian, H.R. Russell, S.A. Walker, K.M. Blundell, 16 | 2016, MNRAS, accepted, http://arxiv.org/abs/1605.02911 17 | 18 | and 19 | 20 | Studying the merging cluster Abell 3266 with eROSITA 21 | J.S. Sanders et al. 22 | 2021, A&A submitted, https://arxiv.org/abs/2106.14534 23 | 24 | All software is Copyright Jeremy Sanders and released under the GNU 25 | GPLv2+. 26 | 27 | gaussian_gradient_magnitude.py 28 | ------------------------------ 29 | 30 | The main program is gaussian_gradient_magnitude.py. This utility 31 | applies the gaussian gradient magnitude filter to an input fits image, 32 | using a particular scale, sigma, in pixels. 33 | 34 | Requirements: 35 | - Python 3.x, astropy, scipy, numpy 36 | 37 | To run, type 38 | 39 | $ ./gaussian_gradient_magnitude.py in.fits out.fits sigma 40 | 41 | Where sigma is a floating point value >0 42 | 43 | hideregions2 44 | ------------ 45 | 46 | This is a tool to cosmetically hide point sources in fits images. It 47 | fills point sources with random values from the surrounding pixel 48 | region. See hideregions2/README for details. 49 | 50 | ggm_combine 51 | ----------- 52 | 53 | This is an interactive tool to combine fits images filtered on 54 | different scales. See ggm_combine/README for details. 55 | 56 | adaptive_ggm.py 57 | --------------- 58 | 59 | This is an automated script to compute the adaptive GGM (used in 60 | Sanders et al. 2021). 61 | 62 | The program relies on the installation of the contour binning package 63 | 64 | https://github.com/jeremysanders/contbin 65 | 66 | Which should be available in the current PATH or can be pointed to 67 | using the --contbin-dir=DIR option. contbin needs to be up to date to 68 | include the latest accumulate_counts program. 69 | 70 | The program takes as input a counts image to compute the smoothing 71 | scale. It then smooths this counts image (or an optional input image 72 | given by --image=FILENAME) using these smoothing scales. The optional 73 | input image is to allow background subtraction or other 74 | manipulation. The gradient is computed from the log of the smoothed 75 | image (or linear image by using --log=False). Please note that if the 76 | input image has negative values then taking the log will generate nan 77 | outputs. 78 | 79 | The program can also take an optional mask image. This should contain 80 | 0 values for regions to be masked out and 1 for included 81 | regions. There is a special value of -2 which causes a pixel to be 82 | excluded in the input but present in the output (the smoothing ignores 83 | this input pixel but calculates the smoothed image from the 84 | surrounding pixels), which is good for cosmetically hiding point 85 | sources. 86 | 87 | The smoothing signal to noise ratio (specified with --sn=VALUE) is the 88 | main input parameter. It is used to calculate the smoothing scale from 89 | the input counts image. At present the smoothing radius contains at 90 | least sn-squared counts. There is an intermediate generated file 91 | containing the smoothing scale (--scale=FILENAME) which contains the 92 | radius-squared in pixels of the smoothing scale (the radius is used as 93 | the Gaussian sigma when smoothing). There is a second intermediate 94 | file which is the smoothed image (specified using 95 | --smoothed=FILENAME). 96 | 97 | An example usage: 98 | 99 | $ ./adaptive_ggm.py --sn=32 cts.fits grad.fits 100 | 101 | The full set of arguments include: 102 | 103 | $ ./adaptive_ggm.py --help 104 | usage: adaptive_ggm.py [-h] [--image IMAGE] [--sn SN] [--log LOG] [--mask MASK] [--scale SCALE] [--smoothed SMOOTHED] 105 | [--threads THREADS] [--contbin-dir CONTBIN_DIR] 106 | counts output 107 | 108 | Adapive Gaussian gradient magnitude 109 | 110 | positional arguments: 111 | counts counts image filename 112 | output output image filename 113 | 114 | optional arguments: 115 | -h, --help show this help message and exit 116 | --image IMAGE optional image filename to apply smoothing to (uses counts if not set) (default: None) 117 | --sn SN Smoothing S/N ratio (default: 32) 118 | --log LOG Calculate log after smoothing (default: True) 119 | --mask MASK Mask image (optional) (default: None) 120 | --scale SCALE Intermediate scale map filename (default: scale.fits) 121 | --smoothed SMOOTHED Intermediate smoothed image filename (default: smoothed.fits) 122 | --threads THREADS Number of threads to use when smoothing (default: 4) 123 | --contbin-dir CONTBIN_DIR 124 | Override location of contour binning code (default: None) 125 | -------------------------------------------------------------------------------- /adaptive_ggm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os.path 4 | import argparse 5 | import subprocess 6 | 7 | from astropy.io import fits 8 | import numpy as np 9 | 10 | def _escapeArgs(args): 11 | """Escape arguments for printing.""" 12 | out = [] 13 | for a in args: 14 | a = a.replace("'", r"\'") 15 | wrap = False 16 | for c in ' \t[]<>|()$"#!*?~&;': 17 | if c in a: 18 | wrap = True 19 | break 20 | if wrap: 21 | a = "'%s'" % a 22 | out.append(a) 23 | return ' '.join(out) 24 | 25 | def call(*args, **argsv): 26 | """Shortcut for running external programs.""" 27 | if not argsv.get('silent', False): 28 | print("+", _escapeArgs(args)) 29 | subprocess.check_call(args) 30 | 31 | def calcGradient(inimg, log): 32 | """Calculate gradient magnitude of input image.""" 33 | 34 | if log: 35 | inimg = np.log10(inimg) 36 | 37 | gy = inimg[1:,:]-inimg[:-1,:] 38 | gx = inimg[:,1:]-inimg[:,:-1] 39 | gyc = 0.5*(gy[1:,:]+gy[:-1,:]) 40 | gxc = 0.5*(gx[:,1:]+gx[:,:-1]) 41 | 42 | gyc = np.pad(gyc, ((1,1),(0,0))) 43 | gxc = np.pad(gxc, ((0,0),(1,1))) 44 | grad = np.sqrt(gxc**2 + gyc**2) 45 | 46 | return grad 47 | 48 | def main(): 49 | 50 | parser = argparse.ArgumentParser( 51 | description="Adapive Gaussian gradient magnitude", 52 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 53 | parser.add_argument('counts', help='counts image filename') 54 | 55 | parser.add_argument('--image', help='optional image filename to apply smoothing to (uses counts if not set)') 56 | parser.add_argument('--sn', type=float, default=32, help='Smoothing S/N ratio') 57 | parser.add_argument('--log', type=bool, default=True, help='Calculate log after smoothing') 58 | parser.add_argument('--mask', help='Mask image (optional)') 59 | parser.add_argument('--scale', help='Intermediate scale map filename', default='scale.fits') 60 | parser.add_argument('--smoothed', help='Intermediate smoothed image filename', default='smoothed.fits') 61 | parser.add_argument('--threads', type=int, default=4, help='Number of threads to use when smoothing') 62 | parser.add_argument('--contbin-dir', help='Override location of contour binning code') 63 | 64 | parser.add_argument('output', help='output image filename') 65 | args = parser.parse_args() 66 | 67 | # where to find program 68 | program = 'accumulate_counts' 69 | if args.contbin_dir: 70 | program = os.path.join(args.contbin_dir, program) 71 | 72 | # first calculate scale map 73 | print('* Calculating scale map') 74 | cargs = [ 75 | args.counts, 76 | '--scale=%s' % args.scale, 77 | '--sn=%g' % args.sn, 78 | '--threads=%i' % args.threads, 79 | ] 80 | if args.mask: 81 | cargs.append('--mask=%s' % args.mask) 82 | 83 | call(program, *cargs) 84 | 85 | # now smooth input image (using input image of counts image) 86 | print('* Smoothing input image') 87 | inimage = args.image or args.counts 88 | 89 | cargs = [ 90 | inimage, 91 | '--apply', 92 | '--scale=%s' % args.scale, 93 | '--applied=%s' % args.smoothed, 94 | '--threads=%i' % args.threads, 95 | '--gaussian', 96 | ] 97 | if args.mask: 98 | cargs.append('--mask=%s' % args.mask) 99 | 100 | call(program, *cargs) 101 | 102 | # get gradient 103 | print('* Calculating gradient') 104 | smf = fits.open(args.smoothed, 'readonly') 105 | smimg = smf[0].data 106 | grad = calcGradient(smimg, args.log) 107 | smf.close() 108 | 109 | # write to output filename (based on input image) 110 | inf = fits.open(inimage, 'readonly') 111 | inf[0].data = grad 112 | print('* Writing gradient image', args.output) 113 | inf.writeto(args.output, overwrite=True) 114 | 115 | if __name__ == '__main__': 116 | main() 117 | -------------------------------------------------------------------------------- /gaussian_gradient_magnitude.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import os 5 | import argparse 6 | 7 | from astropy.io import fits 8 | import scipy.ndimage 9 | import numpy as N 10 | 11 | def run(infile, outfile, scale): 12 | f = fits.open(infile) 13 | img = f[0].data 14 | 15 | # convert to float if required 16 | if issubclass(img.dtype.type, N.integer): 17 | img = img.astype(N.float32) 18 | 19 | proc = scipy.ndimage.gaussian_gradient_magnitude(img, scale) 20 | 21 | f[0].data = proc 22 | 23 | try: 24 | os.unlink(outfile) 25 | except OSError: 26 | pass 27 | 28 | f.writeto(outfile) 29 | 30 | def main(): 31 | parser = argparse.ArgumentParser( 32 | description="Gaussian gradient magnitude") 33 | parser.add_argument('inimage', help='input image') 34 | parser.add_argument('outimage', help='output image') 35 | parser.add_argument('scale', type=float, help='gaussian sigma (pixels)') 36 | args = parser.parse_args() 37 | 38 | run(args.inimage, args.outimage, args.scale) 39 | 40 | if __name__ == '__main__': 41 | main() 42 | -------------------------------------------------------------------------------- /ggm_combine/README: -------------------------------------------------------------------------------- 1 | ggm_combine: Copyright Jeremy Sanders, released under the GPLv2+ 2 | 3 | ggm_combine is a GUI tool for combining together GGM-filtered images 4 | with different scales. It presents a control for varying the scaling 5 | with radius for each input image. A checkbox for each image also 6 | allows scales to be switched on and off. 7 | 8 | Requirements: Python 2.7, PyQt4, numpy, pyfits, PyYaml, ds9 9 | 10 | The current implementation is VERY BASIC and USER-UNFRIENDLY, so 11 | please feel free to send improvements to the code. 12 | 13 | The user has to create a series of fits files with the GGM-filtered 14 | images which should be combined (e.g. using scales of 15 | sigma=1,2,4,8,16...) 16 | 17 | The user should then copy the template ggm_combine.yml. The file 18 | should be edited to set: 19 | 20 | - The central pixel (to measure radii from), based from 0 21 | - An optional chop range to speed up the interactive modification 22 | (note this can be enabled/disabled as necessary) 23 | - Output filename, written to while the program is used 24 | - List of input filenames, and scaling values 25 | 26 | To use the program 27 | 28 | # start an instance of ds9 29 | ds9 & 30 | 31 | # run the interactive GUI 32 | ./interactive.py input.yml & 33 | 34 | As the scaling radii and scales are modified, the output values are 35 | written to out-pars.yml. Rename this to something safe (and the output 36 | fits file) if you want to keep a particular set of parameters. 37 | 38 | To modify the number of radial points, the yml file has to be edited. 39 | 40 | Note that WCS is lost in the output file! 41 | -------------------------------------------------------------------------------- /ggm_combine/curvecontrol.py: -------------------------------------------------------------------------------- 1 | from __future__ import division, print_function 2 | 3 | import itertools 4 | import qt 5 | import numpy as N 6 | 7 | class MarkerItem(qt.QGraphicsEllipseItem): 8 | def __init__(self, parent, x, y): 9 | qt.QGraphicsEllipseItem.__init__(self, qt.QRectF(-5, -5, 10, 10)) 10 | self.setPos(x, y) 11 | self.setFlag(qt.QGraphicsItem.ItemIsMovable) 12 | self.parent = parent 13 | 14 | def mouseReleaseEvent(self, event): 15 | qt.QGraphicsEllipseItem.mouseReleaseEvent(self, event) 16 | self.parent.updatePosns() 17 | 18 | def posn(self): 19 | return self.pos().x(), self.pos().y() 20 | 21 | height = 80 22 | width = 400 23 | 24 | class CurveScene(qt.QGraphicsScene): 25 | 26 | changed = qt.pyqtSignal(object) 27 | 28 | def __init__(self, vals, *args): 29 | qt.QGraphicsScene.__init__(self) 30 | 31 | self.pathitem = qt.QGraphicsPathItem() 32 | self.addItem(self.pathitem) 33 | self.pathitem.setZValue(-1) 34 | 35 | self.ellipses = [] 36 | 37 | #for i in xrange(10): 38 | # ell = MarkerItem(self, i*40, 40) 39 | # self.addItem(ell) 40 | # self.ellipses.append(ell) 41 | 42 | maxx = N.max(vals[0]) 43 | maxy = N.max(vals[1]) 44 | self.maxx = maxx 45 | 46 | for x, y in zip(*vals): 47 | e = MarkerItem(self, N.sqrt(x/maxx)*width, height-y/maxy*height) 48 | self.addItem(e) 49 | self.ellipses.append(e) 50 | 51 | self.addLine( qt.QLineF(qt.QPointF(0,0), qt.QPointF(width, 0)) ) 52 | self.addLine( qt.QLineF(qt.QPointF(0,height), qt.QPointF(width,height)) ) 53 | 54 | self.updatePosns() 55 | 56 | def updatePosns(self): 57 | xvals = [] 58 | yvals = [] 59 | 60 | for e in self.ellipses: 61 | x, y = e.posn() 62 | xvals.append( (x/width)**2*self.maxx ) 63 | yvals.append( (height-y)/height ) 64 | 65 | xvals = N.array(xvals) 66 | yvals = N.array(yvals) 67 | 68 | # draw line 69 | xpts = N.arange(0, width) 70 | xv = (xpts/width)**2*self.maxx 71 | yv = N.interp(xv, xvals, yvals) 72 | ypts = height-yv*height 73 | 74 | path = qt.QPainterPath() 75 | path.moveTo(qt.QPointF(xpts[0], ypts[0])) 76 | for x, y in zip(xpts[1:], ypts[1:]): 77 | path.lineTo(qt.QPointF(x,y)) 78 | self.pathitem.setPath(path) 79 | 80 | # send results to main program 81 | self.changed.emit((xvals, yvals)) 82 | 83 | class CurveView(qt.QGraphicsView): 84 | 85 | changed = qt.pyqtSignal(object) 86 | 87 | def __init__(self, vals, *args): 88 | qt.QGraphicsView.__init__(self, *args) 89 | self.scene = CurveScene(vals, self) 90 | self.setScene(self.scene) 91 | 92 | self.scene.changed.connect(self.changed) 93 | -------------------------------------------------------------------------------- /ggm_combine/ggm_combine.yml: -------------------------------------------------------------------------------- 1 | image: 2 | # central pixel (from 0) to calculate radii from 3 | centre: [1659, 1449] 4 | chop: 5 | # to speed up combination, choose a pixel range below to chop the 6 | # input images, and set enable to True 7 | range: [980, 930, 2210, 1840] 8 | enable: False 9 | 10 | # output filename to write combined image 11 | outfilename: filtered.fits 12 | 13 | # these are the input scales to combine (any number are allowed) 14 | 15 | # each entry is 16 | # image filename (each needs to be the same size) 17 | # list of radii in pixels (increase as necessary) 18 | # weight factors for each radius position (same number as for radii) 19 | 20 | data: 21 | - filename: expcorr_total_nopts_ggm1.fits.gz 22 | weightrad: [0,20,40,80,160,320,640,1280] 23 | weightvals: [1,1,0,0,0,0,0,0] 24 | - filename: expcorr_total_nopts_ggm2.fits.gz 25 | weightrad: [0,20,40,80,160,320,640,1280] 26 | weightvals: [2,2,2,0,0,0,0,0] 27 | - filename: expcorr_total_nopts_ggm4.fits.gz 28 | weightrad: [0,20,40,80,160,320,640,1280] 29 | weightvals: [0,4,4,4,0,0,0,0] 30 | - filename: expcorr_total_nopts_ggm8.fits.gz 31 | weightrad: [0,20,40,80,160,320,640,1280] 32 | weightvals: [0,0,8,8,8,0,0,0] 33 | - filename: expcorr_total_nopts_ggm16.fits.gz 34 | weightrad: [0,20,40,80,160,320,640,1280] 35 | weightvals: [0,0,0,10,10,10,0,0] 36 | - filename: expcorr_total_nopts_ggm32.fits.gz 37 | weightrad: [0,20,40,80,160,320,640,1280] 38 | weightvals: [0,0,0,0,10,10,10,10] 39 | -------------------------------------------------------------------------------- /ggm_combine/interactive.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from __future__ import division, print_function 4 | 5 | import sys 6 | import os 7 | import glob 8 | from random import choice 9 | from string import ascii_uppercase 10 | from math import floor 11 | 12 | import qt 13 | import yaml 14 | from astropy.io import fits 15 | from astropy.wcs import WCS 16 | from astropy.nddata import Cutout2D 17 | import numpy as N 18 | 19 | import curvecontrol 20 | 21 | def ds9xpa(img, hdr, filename=None): 22 | """Send data to ds9 via xpa.""" 23 | if filename is None: 24 | tempfn = '/tmp/temp_xpa_%i.fits' % os.getpid() 25 | else: 26 | tempfn = filename 27 | try: 28 | os.unlink(filename) 29 | except OSError: 30 | pass 31 | 32 | hdu = fits.PrimaryHDU(header=hdr) 33 | hdu.data = img 34 | 35 | hdus = fits.HDUList([hdu]) 36 | hdus.writeto(tempfn) 37 | 38 | os.system('xpaset ds9 fits < %s' % tempfn) 39 | 40 | if filename is None: 41 | os.unlink(tempfn) 42 | 43 | class ImageContainer: 44 | def __init__(self, pars): 45 | 46 | self.pars = dict(pars) 47 | self.images = [] 48 | self.scales = [] 49 | self.radii = [] 50 | self.weights = [] 51 | 52 | if 'chop' in pars['image'] and pars['image']['chop']['enable']: 53 | chop = pars['image']['chop']['range'] 54 | else: 55 | chop = None 56 | 57 | xc, yc = pars['image']['centre'] 58 | for i, d in enumerate(pars['data']): 59 | print('Loading', d['filename']) 60 | with fits.open(d['filename']) as f: 61 | image = f[0].data 62 | # build a wcs object the hacky way 63 | if i == 0: 64 | hdr = f[0].header 65 | w = WCS(naxis=2) 66 | w.wcs.crval = [hdr['CRVAL1'], hdr['CRVAL2']] 67 | w.wcs.cdelt = [hdr['CDELT1'], hdr['CDELT2']] 68 | w.wcs.crpix = [hdr['CRPIX1'], hdr['CRPIX2']] 69 | w.wcs.ctype = [hdr['CTYPE1'], hdr['CTYPE2']] 70 | if chop: 71 | dy = chop[3]-chop[1] 72 | dx = chop[2]-chop[0] 73 | x0 = floor((chop[0] + chop[2])/2) 74 | y0 = floor((chop[1] + chop[3])/2) 75 | image_cutout = Cutout2D(image, (x0, y0), (dy, dx), wcs=w) 76 | image = image_cutout.data 77 | if i == 0: 78 | new_w = image_cutout.wcs 79 | hdr = new_w.to_header() 80 | self.images.append(image) 81 | 82 | radii = N.array(d['weightrad'], dtype=N.float64) 83 | self.radii.append(radii) 84 | weightvals = N.array(d['weightvals'], dtype=N.float64) 85 | maxval = round(N.max(weightvals), 2) 86 | self.scales.append(maxval) 87 | self.weights.append(weightvals/maxval) 88 | # set the header, which will be used later when writing the file 89 | some_random_str = ''.join(choice(ascii_uppercase) for i in range(10)) 90 | with open('tmp' + some_random_str, 'w') as f: 91 | hdr.totextfile(f, clobber=False) 92 | 93 | if chop: 94 | xc -= chop[0] 95 | yc -= chop[1] 96 | 97 | self.radiiimg = N.fromfunction( 98 | lambda y, x: N.sqrt((x-xc)**2+(y-yc)**2), 99 | self.images[0].shape) 100 | 101 | def writeOutputPars(self, filename): 102 | 103 | # update values in parameters 104 | for i, (r, w, s) in enumerate(zip(self.radii, self.weights, self.scales)): 105 | self.pars['data'][i]['weightrad'] = r.tolist() 106 | self.pars['data'][i]['weightvals'] = (w*s).tolist() 107 | 108 | print('pars', repr(self.pars)) 109 | 110 | # write to output 111 | with open(filename, 'w') as fout: 112 | fout.write( yaml.dump(self.pars) ) 113 | 114 | def filterAdd(self): 115 | """Combine input images using interpolation.""" 116 | 117 | out = N.zeros(self.images[0].shape) 118 | for image, radii, weights, scale in zip( 119 | self.images, self.radii, self.weights, self.scales): 120 | if scale > 0: 121 | print(id(image)) 122 | print('radii', radii) 123 | print('weights', weights) 124 | print('scale', scale) 125 | 126 | interpol = N.interp(self.radiiimg, radii, weights*scale) 127 | out += interpol*image 128 | 129 | maxval = N.max(out[N.isfinite(out)]) 130 | out = out / maxval 131 | return out 132 | 133 | class Window(qt.QWidget): 134 | def __init__(self, infile): 135 | qt.QWidget.__init__(self) 136 | layout = qt.QGridLayout() 137 | 138 | # load parameters 139 | with open(infile) as f: 140 | self.pars = pars = yaml.load(f) 141 | 142 | self.images = ImageContainer(pars) 143 | 144 | def getOnChanged(idx): 145 | def func(vals): 146 | self.images.radii[idx] = vals[0] 147 | self.images.weights[idx] = vals[1] 148 | self.redraw() 149 | return func 150 | 151 | def getEditChanged(cntrl, idx): 152 | def func(): 153 | text = cntrl.text() 154 | try: 155 | self.images.scales[idx] = float(text) 156 | except ValueError: 157 | cntrl.setText(str(self.images.scales[idx])) 158 | return 159 | self.redraw() 160 | return func 161 | 162 | def getCheck(idx, cntrl): 163 | def func(): 164 | self.images.scales[idx] = -self.images.scales[idx] 165 | cntrl.setText(str(self.images.scales[idx])) 166 | self.redraw() 167 | return func 168 | 169 | for i in range(len(self.images.images)): 170 | 171 | radii = self.images.radii[i] 172 | weights = self.images.weights[i] 173 | print('r', radii) 174 | print('w', weights) 175 | 176 | cntrl = curvecontrol.CurveView([radii, weights]) 177 | cntrl.changed.connect( getOnChanged(i) ) 178 | layout.addWidget(cntrl, i, 0) 179 | 180 | l = qt.QLineEdit() 181 | l.setText(str(self.images.scales[i])) 182 | l.editingFinished.connect( getEditChanged(l, i) ) 183 | layout.addWidget(l, i, 1) 184 | 185 | c = qt.QCheckBox() 186 | c.clicked.connect( getCheck(i, l) ) 187 | layout.addWidget(c, i, 2) 188 | 189 | self.setLayout(layout) 190 | self.redraw() 191 | 192 | def redraw(self): 193 | img = self.images.filterAdd() 194 | tmp_file = max(glob.iglob('tmp*'), key=os.path.getctime) 195 | print('Temporary header file: %s.' % tmp_file) 196 | hdr = fits.Header.fromtextfile(tmp_file) 197 | ds9xpa(img, hdr, filename=self.pars['image']['outfilename']) 198 | self.images.writeOutputPars('out-pars.yml') 199 | 200 | def main(): 201 | filename = sys.argv[1] 202 | 203 | app = qt.QApplication(sys.argv) 204 | win = Window(filename) 205 | win.show() 206 | app.exec_() 207 | 208 | if __name__ == '__main__': 209 | main() 210 | -------------------------------------------------------------------------------- /ggm_combine/qt.py: -------------------------------------------------------------------------------- 1 | """A convenience module to import both the used Qt symbols from.""" 2 | 3 | import sip 4 | sip.setapi('QDate', 2) 5 | sip.setapi('QDateTime', 2) 6 | sip.setapi('QString', 2) 7 | sip.setapi('QTextStream', 2) 8 | sip.setapi('QTime', 2) 9 | sip.setapi('QUrl', 2) 10 | sip.setapi('QVariant', 2) 11 | 12 | from PyQt4.QtCore import * 13 | from PyQt4.QtGui import * 14 | from PyQt4.uic import loadUi 15 | -------------------------------------------------------------------------------- /hideregions2/Makefile: -------------------------------------------------------------------------------- 1 | CXX=g++ 2 | CC=g++ 3 | 4 | CXXFLAGS=-g -Wall -O2 5 | 6 | ALL_CXXFLAGS = -I. -I${ASCDS_LIB}/../include $(CXXFLAGS) 7 | 8 | .cc.o: 9 | $(CXX) -c $(CPPFLAGS) $(ALL_CXXFLAGS) $< 10 | 11 | all: hideregions2 12 | 13 | clean: 14 | rm -f hideregions2 *.o 15 | @${MAKE} -C dm clean 16 | 17 | dm/libdmxx.a: 18 | @${MAKE} -C dm 19 | 20 | hideregions2: hideregions2.o dm/libdmxx.a 21 | $(CXX) -o hideregions2 hideregions2.o -Ldm -ldmxx -L$(ASCDS_LIB) -lregion -lascdm -Wl,-rpath $(ASCDS_LIB) -Wl,-rpath $(ASCDS_LIB)/../ots/lib 22 | -------------------------------------------------------------------------------- /hideregions2/README: -------------------------------------------------------------------------------- 1 | hideregions2: Tool to fill in point sources with random values from 2 | the surrounding pixels. It's only tested for Chandra images. 3 | 4 | Copyright Jeremy Sanders, released under the GPLv2+. 5 | 6 | hideregions2 is a C++ code which links against the CIAO region 7 | libraries. It uses a C++ wrapper library to CIAO (dmxx). 8 | 9 | Requirements for building: 10 | - Chandra CIAO environment running 11 | - C++ compiler (only gcc g++ tested) 12 | - Boost library 13 | 14 | To build inspect the Makefile (and dm/Makefile) and run 15 | 16 | # make all 17 | 18 | If all went well, you'll have a hideregions2 executable. 19 | 20 | To use, run 21 | 22 | # hideregions2 in.fits points.reg out.fits 23 | 24 | Note that points.reg must be a CIAO region file in PHYSICAL 25 | COORDINATES. Only circle and ellipse regions are supported! 26 | 27 | WCS data are lost in the output file! 28 | 29 | -------------------------------------------------------------------------------- /hideregions2/dm/Makefile: -------------------------------------------------------------------------------- 1 | CXX=g++ 2 | CXXFLAGS = -g -Wall -I$(ASCDS_LIB)/../include/ -O2 3 | 4 | objects = dataset.o general.o descriptor.o image.o block.o memimage.o coord.o 5 | 6 | all: libdmxx.a test.out 7 | 8 | clean: 9 | rm -f *.o libdmxx.a 10 | 11 | dataset.o: dataset.hh image.hh block.hh 12 | general.o: general.hh 13 | descriptor.o: descriptor.hh 14 | image.o: image.hh descriptor.hh block.hh memimage.hh 15 | block.o: block.hh 16 | memimage.o: memimage.hh 17 | coord.o: coord.hh 18 | 19 | libdmxx.a: $(objects) 20 | ar -rcs libdmxx.a $(objects) 21 | 22 | test.out : test.cc libdmxx.a 23 | $(CXX) -o test.out test.cc $(CXXFLAGS) -L. -ldmxx -L$(ASCDS_LIB) \ 24 | -lascdm -lregion 25 | -------------------------------------------------------------------------------- /hideregions2/dm/block.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "block.hh" 3 | 4 | dm::block::~block() 5 | { 6 | if ( m_block != 0 ) 7 | dmBlockClose(m_block); 8 | } 9 | 10 | bool dm::block::read_key(const std::string& name, double* ret) 11 | { 12 | assert( m_block != 0 ); 13 | 14 | dmDescriptor* desc = dmKeyRead_d(m_block, const_cast(name.c_str()), 15 | ret); 16 | 17 | return( desc != 0 ); 18 | } 19 | -------------------------------------------------------------------------------- /hideregions2/dm/block.hh: -------------------------------------------------------------------------------- 1 | #ifndef DM_BLOCK_HH 2 | #define DM_BLOCK_HH 3 | 4 | #include 5 | #include 6 | 7 | namespace dm { 8 | 9 | class block 10 | { 11 | public: 12 | virtual ~block(); 13 | 14 | // read a double key with name, return false if not found 15 | bool read_key(const std::string& name, double* ret); 16 | 17 | protected: 18 | block(dmBlock *init) { m_block = init; } 19 | 20 | friend class dataset; 21 | 22 | private: 23 | block(const block& other); // disallow copy usage 24 | block& operator=(const block& other); // disallow = 25 | 26 | protected: 27 | dmBlock* m_block; 28 | }; 29 | 30 | 31 | } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /hideregions2/dm/coord.cc: -------------------------------------------------------------------------------- 1 | #include "coord.hh" 2 | 3 | dm::coord::coord(dmDescriptor *descr) 4 | : _descr(descr) 5 | { 6 | } 7 | 8 | dm::coord::~coord() 9 | { 10 | } 11 | -------------------------------------------------------------------------------- /hideregions2/dm/coord.hh: -------------------------------------------------------------------------------- 1 | #ifndef DM_COORD_HH 2 | #define DM_COORD_HH 3 | 4 | #include 5 | 6 | namespace dm 7 | { 8 | 9 | class coord 10 | { 11 | public: 12 | protected: 13 | coord(dmDescriptor* descr); 14 | ~coord(); 15 | 16 | private: 17 | dmDescriptor* _descr; 18 | }; 19 | 20 | 21 | } 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /hideregions2/dm/dataset.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "dataset.hh" 5 | #include "exception.hh" 6 | 7 | ////////////////////////////////////////////////////////////////// 8 | // Constructors / destructors 9 | 10 | dm::dataset::dataset(const std::string& filename, open_mode mode) 11 | : m_delete_on_finish(false) 12 | { 13 | strlike bfr(filename); 14 | 15 | switch( mode ) { 16 | case open: 17 | m_dataset = dmDatasetOpen( bfr() ); 18 | if( m_dataset == 0 ) { 19 | except_unable_to_open e; 20 | e.set_descr(std::string("Unable to open file ") + filename); 21 | throw e; 22 | } 23 | break; 24 | case openrw: 25 | m_dataset = dmDatasetOpenUpdate( bfr() ); 26 | if( m_dataset == 0 ) { 27 | except_unable_to_open e; 28 | e.set_descr(std::string("Unable to open file ") + filename + 29 | " (r/w access)"); 30 | throw e; 31 | } 32 | break; 33 | case create_over: 34 | unlink(filename.c_str()); 35 | case create: 36 | m_dataset = dmDatasetCreate( bfr() ); 37 | if( m_dataset == 0 ) { 38 | except_unable_to_create e; 39 | e.set_descr(std::string("Unable to create file ") + filename); 40 | throw e; 41 | } 42 | break; 43 | } 44 | } 45 | 46 | dm::dataset::~dataset() 47 | { 48 | if( m_delete_on_finish ) 49 | dmDatasetDelete( m_dataset ); 50 | else 51 | dmDatasetClose( m_dataset ); 52 | } 53 | 54 | 55 | /////////////////////////////////////////////////////////////// 56 | // Block functions 57 | 58 | int dm::dataset::get_current_block() 59 | { 60 | return dmDatasetGetCurrentBlockNo(m_dataset); 61 | } 62 | 63 | int dm::dataset::get_no_blocks() 64 | { 65 | return dmDatasetGetNoBlocks(m_dataset); 66 | } 67 | 68 | dm::block* dm::dataset::get_block(int blockno) 69 | { 70 | return new block( _get_block(blockno) ); 71 | } 72 | 73 | std::string dm::dataset::get_block_name(int blockno) 74 | { 75 | char buffer[256]; 76 | dmErrCode ret = dmDatasetGetBlockName(m_dataset, blockno, buffer, 255); 77 | if( ret != dmSUCCESS ) { 78 | except_invalid_param e; 79 | e.set_descr(std::string("Invalid block number ") + 80 | to_str(blockno)); 81 | throw e; 82 | } 83 | return buffer; 84 | } 85 | 86 | dmBlockType dm::dataset::get_block_type(int blockno) 87 | { 88 | return dmDatasetGetBlockType(m_dataset, blockno); 89 | } 90 | 91 | dm::block* dm::dataset::copy_block(const std::string& name, 92 | const dm::block* parent, 93 | bool copydata) 94 | { 95 | strlike buffer(name); 96 | 97 | dmBlock* b = dmBlockCreateCopy(m_dataset, buffer(), parent->m_block, 98 | copydata); 99 | 100 | if( b == 0 ) { 101 | except_copy_fail e; 102 | e.set_descr("Unable to copy block"); 103 | throw e; 104 | } 105 | 106 | return new block(b); 107 | } 108 | 109 | ////////////////////////////////////////////////////////////// 110 | // Image functions 111 | 112 | dm::image* dm::dataset::create_image(const std::string& name, 113 | dmDataType datatype, 114 | const std::vector& axes_lengths) 115 | { 116 | strlike buffer(name); 117 | 118 | const unsigned noaxes = axes_lengths.size(); 119 | long* lengths = new long[noaxes]; 120 | for(unsigned i=0; i 5 | #include 6 | #include 7 | 8 | #include "general.hh" 9 | #include "block.hh" 10 | #include "image.hh" 11 | 12 | namespace dm 13 | { 14 | 15 | class dataset 16 | { 17 | public: 18 | // open the dataset (or create) 19 | dataset(const std::string& filename, open_mode mode = open); 20 | // close the dataset 21 | ~dataset(); 22 | 23 | // BLOCK FUNCTIONS 24 | ////////////////// 25 | // get current block no. 26 | int get_current_block(); 27 | // get number of blocks 28 | int get_no_blocks(); 29 | // move to block 30 | block* get_block(int blockno); 31 | // get the name of the numbered block 32 | std::string get_block_name(int blockno); 33 | // get type of the block 34 | dmBlockType get_block_type(int blockno); 35 | // copy block 36 | block* copy_block(const std::string& name, const block* parent, 37 | bool copydata); 38 | 39 | // IMAGE FUNCTIONS 40 | ////////////////// 41 | image* create_image(const std::string& name, 42 | dmDataType datatype, 43 | const std::vector& axes_lengths); 44 | image* create_image(const std::string& name, 45 | dmDataType datatype, 46 | unsigned nlen, const int* lengths); 47 | image* create_image(const std::string& name, 48 | dmDataType datatype, int xw, int yw); 49 | image* get_image(int block_no = 1); 50 | 51 | // delete the dataset when we close (default false) 52 | void delete_when_finished(bool b=true) 53 | { m_delete_on_finish = b; } 54 | 55 | public: 56 | // static functions 57 | static void delete_on_disk(const std::string& filename); 58 | 59 | private: 60 | dataset(const dataset& other); // disallow copy 61 | dataset& operator=(const dataset& other); // disallow = 62 | 63 | dmBlock* _get_block(int blockno); 64 | 65 | private: 66 | dmDataset* m_dataset; 67 | bool m_delete_on_finish; 68 | }; 69 | 70 | 71 | 72 | } 73 | 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /hideregions2/dm/descriptor.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "descriptor.hh" 4 | #include "general.hh" 5 | #include "exception.hh" 6 | 7 | dm::descriptor::descriptor(dmDescriptor* d) 8 | { 9 | m_descriptor = d; 10 | m_dtype = dmGetDataType(d); 11 | } 12 | 13 | template void 14 | dm::descriptor::set_pixel(const pix_vec& pos, T val) 15 | { 16 | arraycopy a( pos ); 17 | 18 | int r; 19 | switch( get_data_type() ) { 20 | case dmSHORT: 21 | r = dmImageDataSetPixel_s(m_descriptor, a(), short(val) ); break; 22 | case dmLONG: 23 | r = dmImageDataSetPixel_l(m_descriptor, a(), long(val) ); break; 24 | case dmFLOAT: 25 | r = dmImageDataSetPixel_f(m_descriptor, a(), float(val) ); break; 26 | case dmDOUBLE: 27 | r = dmImageDataSetPixel_d(m_descriptor, a(), double(val) ); break; 28 | case dmBYTE: 29 | r = dmImageDataSetPixel_ub(m_descriptor, a(), (unsigned char)(val) ); break; 30 | case dmUSHORT: 31 | r = dmImageDataSetPixel_us(m_descriptor, a(), (unsigned short)(val) ); break; 32 | case dmULONG: 33 | r = dmImageDataSetPixel_ul(m_descriptor, a(), (unsigned long)(val) ); break; 34 | default: 35 | except_unknown e; 36 | e.set_descr("Invalid data type in dm::descriptor::set_pixel()"); 37 | throw e; 38 | } 39 | 40 | if( r == dmFAILURE ) { 41 | except_invalid_param e; 42 | e.set_descr("Invalid return from dmImageDataSetPixel_x"); 43 | throw e; 44 | } 45 | } 46 | 47 | template void 48 | dm::descriptor::get_pixel(const pix_vec& pos, T* val) 49 | { 50 | arraycopy a( pos ); 51 | 52 | const long dim = pos.size(); 53 | 54 | switch( get_data_type() ) { 55 | case dmSHORT: 56 | *val = T( dmImageDataGetPixel_s(m_descriptor, a(), dim ) ); break; 57 | case dmLONG: 58 | *val = T( dmImageDataGetPixel_l(m_descriptor, a(), dim ) ); break; 59 | case dmFLOAT: 60 | *val = T( dmImageDataGetPixel_f(m_descriptor, a(), dim ) ); break; 61 | case dmDOUBLE: 62 | *val = T( dmImageDataGetPixel_d(m_descriptor, a(), dim ) ); break; 63 | case dmBYTE: 64 | *val = T( dmImageDataGetPixel_ub(m_descriptor, a(), dim ) ); break; 65 | case dmUSHORT: 66 | *val = T( dmImageDataGetPixel_us(m_descriptor, a(), dim ) ); break; 67 | case dmULONG: 68 | *val = T( dmImageDataGetPixel_ul(m_descriptor, a(), dim ) ); break; 69 | default: 70 | except_unknown e; 71 | e.set_descr("Invalid data type in dm::descriptor::get_pixel()"); 72 | throw e; 73 | } 74 | } 75 | 76 | namespace dm{ 77 | // copies T2[] to T1 (size items) 78 | template void _translate_array(T1* a, T2* b, unsigned size) 79 | { 80 | for(unsigned i=0; i void 115 | dm::descriptor::get_subarray(const pix_vec& lowerbounds, 116 | const pix_vec& upperbounds, 117 | T** val) 118 | { 119 | const int size = get_total_size(lowerbounds, upperbounds); 120 | arraycopy l( lowerbounds), u ( upperbounds ); 121 | *val = new T[size]; 122 | 123 | int r; 124 | switch( get_data_type() ) { 125 | case dmSHORT: DM_DESCRIPTOR_GETSUBARRAY(short, _s); 126 | case dmLONG: DM_DESCRIPTOR_GETSUBARRAY(long, _l); 127 | case dmFLOAT: DM_DESCRIPTOR_GETSUBARRAY(float, _f); 128 | case dmDOUBLE: DM_DESCRIPTOR_GETSUBARRAY(double, _d); 129 | case dmBYTE: DM_DESCRIPTOR_GETSUBARRAY(unsigned char, _ub); 130 | case dmUSHORT: DM_DESCRIPTOR_GETSUBARRAY(unsigned short, _us); 131 | case dmULONG: DM_DESCRIPTOR_GETSUBARRAY(unsigned long, _ul); 132 | default: 133 | except_unknown e; 134 | e.set_descr("Invalid data type in dm::descriptor::get_subarray()"); 135 | throw e; 136 | } 137 | 138 | if( r != dmSUCCESS ) { 139 | except_invalid_param e; 140 | e.set_descr("Invalid return from dmImageDataGetSubArray_x"); 141 | throw e; 142 | } 143 | } 144 | 145 | #undef DM_DESCRIPTOR_GETSUBARRAY 146 | 147 | #define DM_DESCRIPTOR_SETSUBARRAY(TYPE, EXTEN) \ 148 | {\ 149 | TYPE *x = new TYPE[size];\ 150 | _translate_array(x, val, size);\ 151 | r = dmImageDataSetSubArray ## EXTEN (m_descriptor, l(), u(), x);\ 152 | delete[] x; break;\ 153 | } 154 | 155 | template void 156 | dm::descriptor::set_subarray(const pix_vec& lowerbounds, 157 | const pix_vec& upperbounds, 158 | const T* val) 159 | { 160 | const int size = get_total_size(lowerbounds, upperbounds); 161 | arraycopy l( lowerbounds), u ( upperbounds ); 162 | 163 | int r; 164 | switch( get_data_type() ) { 165 | case dmSHORT: DM_DESCRIPTOR_SETSUBARRAY(short, _s); 166 | case dmLONG: DM_DESCRIPTOR_SETSUBARRAY(long, _l); 167 | case dmFLOAT: DM_DESCRIPTOR_SETSUBARRAY(float, _f); 168 | case dmDOUBLE: DM_DESCRIPTOR_SETSUBARRAY(double, _d); 169 | case dmBYTE: DM_DESCRIPTOR_SETSUBARRAY(unsigned char, _ub); 170 | case dmUSHORT: DM_DESCRIPTOR_SETSUBARRAY(unsigned short, _us); 171 | case dmULONG: DM_DESCRIPTOR_SETSUBARRAY(unsigned long, _ul); 172 | default: 173 | except_unknown e; 174 | e.set_descr("Invalid data type in dm::descriptor::set_subarray()"); 175 | throw e; 176 | } 177 | 178 | if( r != dmSUCCESS ) { 179 | except_invalid_param e; 180 | e.set_descr("Invalid return from dmImageDataSetSubArray_x"); 181 | throw e; 182 | } 183 | } 184 | 185 | #undef DM_DESCRIPTOR_SETSUBARRAY 186 | 187 | /////////////////////////////////////////////////////////////////// 188 | 189 | unsigned dm::descriptor::get_dimensionality() 190 | { 191 | return dmGetArrayDim( m_descriptor ); 192 | } 193 | 194 | void dm::descriptor::get_dimensions(pix_vec* retn) 195 | { 196 | retn->clear(); 197 | 198 | long *axeslengths = 0; 199 | 200 | const int no = dmGetArrayDimensions(m_descriptor, &axeslengths); 201 | 202 | for(int i=0; ipush_back( unsigned(axeslengths[i]) ); 204 | 205 | std::free( axeslengths ); 206 | } 207 | 208 | ///////////////////////////////////////////////////////////////////////// 209 | // put these into the object output 210 | #define DM_DEFINE_SET_PIXEL(TYPE) \ 211 | template void \ 212 | descriptor::set_pixel(const pix_vec&, TYPE);\ 213 | template void \ 214 | descriptor::get_pixel(const pix_vec&, TYPE*);\ 215 | template void \ 216 | descriptor::get_subarray(const pix_vec&, const pix_vec&, TYPE**);\ 217 | template void \ 218 | descriptor::set_subarray(const pix_vec&, const pix_vec&, const TYPE*); 219 | 220 | 221 | namespace dm { 222 | DM_DEFINE_SET_PIXEL(short) 223 | DM_DEFINE_SET_PIXEL(long) 224 | DM_DEFINE_SET_PIXEL(float) 225 | DM_DEFINE_SET_PIXEL(double) 226 | DM_DEFINE_SET_PIXEL(unsigned char) 227 | DM_DEFINE_SET_PIXEL(unsigned short) 228 | DM_DEFINE_SET_PIXEL(unsigned long) 229 | } 230 | 231 | #undef DM_DEFINE_SET_PIXEL 232 | -------------------------------------------------------------------------------- /hideregions2/dm/descriptor.hh: -------------------------------------------------------------------------------- 1 | #ifndef DM_DESCRIPTOR_HH 2 | #define DM_DESCRIPTOR_HH 3 | 4 | #include 5 | #include 6 | 7 | #include "general.hh" 8 | 9 | namespace dm 10 | { 11 | 12 | class descriptor 13 | { 14 | public: 15 | ~descriptor() {}; 16 | descriptor(dmDescriptor *d); 17 | 18 | // allowed T classes are: long, short, float, double, unsigned char, 19 | // unsigned short, unsigned long 20 | template void set_pixel(const pix_vec& pos, T val); 21 | template void get_pixel(const pix_vec& pos, T* val); 22 | // get_subarray allocates the necessary space, returning *val 23 | template void get_subarray(const pix_vec& lowerbounds, 24 | const pix_vec& upperbounds, 25 | T** val); 26 | template void set_subarray(const pix_vec& lowerbounds, 27 | const pix_vec& upperbounds, 28 | const T* val); 29 | 30 | unsigned get_dimensionality(); 31 | void get_dimensions(pix_vec* retn); 32 | 33 | dmDataType get_data_type() { return m_dtype; } 34 | dmDescriptor* get_descriptor() { return m_descriptor; } 35 | 36 | private: 37 | descriptor(const descriptor& other); // disallow copy 38 | descriptor& operator=(const descriptor& other); 39 | 40 | private: 41 | dmDescriptor* m_descriptor; 42 | dmDataType m_dtype; 43 | }; 44 | 45 | } 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /hideregions2/dm/dm.hh: -------------------------------------------------------------------------------- 1 | #ifndef DM_DM_HH 2 | #define DM_DM_HH 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /hideregions2/dm/exception.hh: -------------------------------------------------------------------------------- 1 | #ifndef DM_EXCEPTION_HH 2 | #define DM_EXCEPTION_HH 3 | 4 | #include 5 | 6 | namespace dm 7 | { 8 | class exception 9 | { 10 | public: 11 | exception() {} 12 | void set_descr(const std::string& t) { m_descr = t; } 13 | const std::string& operator() () { return m_descr; } 14 | private: 15 | std::string m_descr; 16 | }; 17 | 18 | class except_unable_to_open : public exception {}; 19 | class except_unable_to_create : public exception {}; 20 | class except_invalid_param : public exception {}; 21 | class except_copy_fail : public exception {}; 22 | class except_block_create_fail : public exception {}; 23 | 24 | class except_unknown : public exception {}; 25 | } 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /hideregions2/dm/general.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "general.hh" 4 | 5 | dm::strlike::strlike(const std::string& str) 6 | { 7 | m_cpy = new char[str.size() + 1]; 8 | std::strcpy( m_cpy, str.c_str()); 9 | } 10 | 11 | dm::arraycopy::arraycopy(const pix_vec& array) 12 | { 13 | const unsigned no = array.size(); 14 | m_copy = new long[no]; 15 | for(unsigned i=0; i 5 | #include 6 | 7 | namespace dm 8 | { 9 | // method to use when opening file 10 | // create won't overwrite new files, create_over will 11 | enum open_mode { open, openrw, create, create_over }; 12 | 13 | typedef std::vector pix_vec; 14 | 15 | // little class to make copies of strings 16 | // acts like a kind of 'smart-pointer' 17 | // needed because DM routines use char* rather than const char* 18 | class strlike 19 | { 20 | public: 21 | strlike(const std::string& str); 22 | ~strlike() { delete[] m_cpy; } 23 | char* operator() () { return m_cpy; } 24 | private: 25 | char* m_cpy; 26 | }; 27 | 28 | // arraycopy 29 | // class to make a copy of a vector or unsigned* array 30 | class arraycopy 31 | { 32 | public: 33 | arraycopy(const pix_vec& array); 34 | arraycopy(unsigned no, const unsigned* list); 35 | ~arraycopy() { delete[] m_copy; } 36 | 37 | long* operator() () { return m_copy; } 38 | private: 39 | long* m_copy; 40 | }; 41 | 42 | std::string to_str(int i); 43 | } 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /hideregions2/dm/image.cc: -------------------------------------------------------------------------------- 1 | #include "exception.hh" 2 | #include "image.hh" 3 | 4 | dm::image::image(dmBlock *init) 5 | : block(init), 6 | descriptor( dmImageGetDataDescriptor( m_block ) ) 7 | { 8 | } 9 | 10 | dm::image::~image() 11 | { 12 | if( m_block != 0 ) 13 | dmBlockClose(m_block); 14 | 15 | // don't do anything with descriptors (I think) 16 | } 17 | 18 | template void dm::image::create_memimage(memimage **im) 19 | { 20 | pix_vec dims; 21 | get_dimensions( &dims ); 22 | 23 | if( dims.size() != 2 ) { 24 | except_invalid_param e; 25 | e.set_descr("Invalid number of dimensions in dm::image::create_memimage"); 26 | throw e; 27 | } 28 | 29 | // lower range of coords 30 | pix_vec lower; 31 | lower.push_back(1); lower.push_back(1); 32 | 33 | // get data for range 34 | T* data; 35 | get_subarray(lower, dims, &data); 36 | 37 | // create image 38 | *im = new memimage(dims[0], dims[1], data); 39 | 40 | delete[] data; 41 | } 42 | 43 | template void dm::image::write_from_memimage(const memimage& im) 44 | { 45 | pix_vec dims; 46 | get_dimensions( &dims ); 47 | 48 | if( dims.size() != 2 ) { 49 | except_invalid_param e; 50 | e.set_descr("Invalid number of dimensions in dm::image::write_from_memimage"); 51 | throw e; 52 | } 53 | 54 | if( dims[0] != im.xw() || dims[1] != im.yw() ) { 55 | except_invalid_param e; 56 | e.set_descr("Dimensions of image and data block not matched"); 57 | throw e; 58 | } 59 | 60 | // lower range of coords 61 | pix_vec lower; 62 | lower.push_back(1); lower.push_back(1); 63 | 64 | // copy data 65 | const std::valarray& data = im.data(); 66 | const int size = data.size(); 67 | T* copy = new T[size]; 68 | for(int i=0; i **im); \ 79 | template void \ 80 | dm::image::write_from_memimage(const memimage& im); 81 | 82 | DM_DEFINE_TEMPL(short) 83 | DM_DEFINE_TEMPL(long) 84 | DM_DEFINE_TEMPL(float) 85 | DM_DEFINE_TEMPL(double) 86 | DM_DEFINE_TEMPL(unsigned char) 87 | DM_DEFINE_TEMPL(unsigned short) 88 | DM_DEFINE_TEMPL(unsigned long) 89 | 90 | #undef DM_DEFINE_TEMPL 91 | -------------------------------------------------------------------------------- /hideregions2/dm/image.hh: -------------------------------------------------------------------------------- 1 | #ifndef DM_IMAGE_HH 2 | #define DM_IMAGE_HH 3 | 4 | #include "block.hh" 5 | #include "descriptor.hh" 6 | #include "memimage.hh" 7 | 8 | namespace dm 9 | { 10 | class image : public block, public descriptor 11 | { 12 | public: 13 | virtual ~image(); 14 | 15 | // create an in-memory image from the file 16 | template void create_memimage(memimage **im); 17 | // write memory image to disk (note - must be same size!) 18 | template void write_from_memimage(const memimage& im); 19 | 20 | protected: 21 | image(dmBlock *init); // protected constructor 22 | 23 | friend class dataset; 24 | 25 | private: 26 | image(const image& other); // disallow copy usage 27 | image& operator=(const image& other); // disallow = 28 | 29 | private: 30 | dmDescriptor* m_descriptor; 31 | }; 32 | 33 | 34 | } 35 | 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /hideregions2/dm/memimage.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "memimage.hh" 4 | 5 | template dm::memimage::memimage(const std::string& filename) 6 | { 7 | std::ifstream file(filename.c_str()); 8 | 9 | file >> m_xw >> m_yw; 10 | 11 | m_data.resize(m_xw*m_yw); 12 | if( file ) 13 | { 14 | for(unsigned y=0; y> operator()(x, y); 18 | } 19 | } 20 | 21 | if( ! file ) 22 | { 23 | std::cerr << "Error reading from file\n"; 24 | std::exit(1); 25 | } 26 | 27 | } 28 | 29 | template void dm::memimage::dump_to_file 30 | (const std::string& filename) const 31 | { 32 | std::ofstream file(filename.c_str()); 33 | 34 | file << xw() << ' ' << yw() << '\n'; 35 | for(unsigned y=0; y T& dm::memimage::pixel(unsigned x, unsigned y) 47 | { 48 | if( x >= m_xw || y >= m_yw) 49 | throw out_of_range_exception(); 50 | return operator() (x, y); 51 | } 52 | 53 | // checked const pixel access 54 | template T dm::memimage::pixel(unsigned x, unsigned y) const 55 | { 56 | if( x >= m_xw || y >= m_yw) 57 | throw out_of_range_exception(); 58 | 59 | return operator() (x, y); 60 | } 61 | 62 | #define DM_DEFINE_TEMPL(TYPE) \ 63 | template class dm::memimage; 64 | 65 | DM_DEFINE_TEMPL(short) 66 | DM_DEFINE_TEMPL(long) 67 | DM_DEFINE_TEMPL(float) 68 | DM_DEFINE_TEMPL(double) 69 | DM_DEFINE_TEMPL(unsigned char) 70 | DM_DEFINE_TEMPL(unsigned short) 71 | DM_DEFINE_TEMPL(unsigned long) 72 | 73 | #undef DM_DEFINE_TEMPL 74 | -------------------------------------------------------------------------------- /hideregions2/dm/memimage.hh: -------------------------------------------------------------------------------- 1 | #ifndef DM_MEMIMAGE_HH 2 | #define DM_MEMIMAGE_HH 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace dm { 10 | 11 | template class memimage 12 | { 13 | public: 14 | // blank image 15 | memimage(const unsigned xw, const unsigned yw, const T val = 0) 16 | : m_xw(xw), m_yw(yw), m_data( val, xw*yw ) 17 | {} 18 | 19 | // copy image from another 20 | memimage(const memimage& other) 21 | : m_xw( other.m_xw ), m_yw( other.m_yw ), m_data( other.m_data ) 22 | {} 23 | 24 | // initialise from C-style array 25 | memimage(const unsigned xw, const unsigned yw, const T* data) 26 | : m_xw( xw ), m_yw( yw ), m_data( data, xw*yw ) 27 | {} 28 | 29 | // initialise from other datatypes 30 | template explicit memimage(const memimage& other); 31 | template memimage(const unsigned xw, 32 | const unsigned yw, const T2* const data); 33 | 34 | // read from dumpdata file 35 | memimage(const std::string& filename); 36 | // dump to file 37 | void dump_to_file(const std::string &filename) const; 38 | 39 | // set all the pixels 40 | void set_all(const T val = 0) { m_data = val; } 41 | 42 | // get access to pixels 43 | T& operator() (const unsigned x, const unsigned y) 44 | { return m_data[x+y*m_xw]; } 45 | T operator() (const unsigned x, const unsigned y) const 46 | { return m_data[x+y*m_xw]; } 47 | 48 | // get flat access to pixels 49 | T& flatdata(const unsigned i) 50 | { return m_data[i]; } 51 | T flatdata(const unsigned i) const 52 | { return m_data[i]; } 53 | 54 | // checked access to pixels 55 | class out_of_range_exception {}; 56 | T& pixel(const unsigned x, const unsigned y); 57 | T pixel(const unsigned x, const unsigned y) const; 58 | 59 | class size_mismatch_exception {}; 60 | 61 | // get maximum value in image 62 | T max() const 63 | { 64 | // get minimum value (not smallest value) 65 | T maxval = std::numeric_limits::is_integer 66 | ? std::numeric_limits::min() 67 | : -std::numeric_limits::max(); 68 | 69 | const size_t len = m_xw*m_yw; 70 | for( size_t i = 0; i != len; ++i ) 71 | maxval = std::max( maxval, m_data[i] ); 72 | return maxval; 73 | } 74 | 75 | // get minimum value of image 76 | T min() const 77 | { 78 | T minval = std::numeric_limits::max(); 79 | 80 | const size_t len = m_xw*m_yw; 81 | for( size_t i = 0; i != len; ++i ) 82 | minval = std::min( minval, m_data[i] ); 83 | return minval; 84 | } 85 | 86 | T sum() const 87 | { 88 | T tot = 0; 89 | const size_t len = m_xw*m_yw; 90 | for( size_t i = 0; i != len; ++i ) 91 | tot += m_data[i]; 92 | return tot; 93 | } 94 | 95 | // make all values <= upperval 96 | void trim_down(const T upperval) 97 | { 98 | const size_t len = m_xw*m_yw; 99 | for( size_t i = 0; i != len; ++i ) 100 | m_data[i] = std::min( m_data[i], upperval ); 101 | } 102 | 103 | // make all values >= lowerval 104 | void trim_up(const T lowerval) 105 | { 106 | const size_t len = m_xw*m_yw; 107 | for( size_t i = 0; i != len; ++i ) 108 | m_data[i] = std::max( m_data[i], lowerval ); 109 | } 110 | 111 | private: 112 | void assert_size_other(const memimage& other) const 113 | { 114 | if( m_xw != other.xw() || m_yw != other.yw() ) 115 | throw size_mismatch_exception(); 116 | } 117 | 118 | public: 119 | // various operations with images 120 | // multiply image by another 121 | const memimage& operator *= (const memimage& other) 122 | { 123 | assert_size_other(other); m_data *= other.data(); return *this; 124 | } 125 | const memimage& operator /= (const memimage& other) 126 | { 127 | assert_size_other(other); m_data /= other.data(); return *this; 128 | } 129 | const memimage& operator -= (const memimage& other) 130 | { 131 | assert_size_other(other); m_data -= other.data(); return *this; 132 | } 133 | const memimage& operator += (const memimage& other) 134 | { 135 | assert_size_other(other); m_data += other.data(); return *this; 136 | } 137 | 138 | // with constants 139 | const memimage& operator *= (const T other) 140 | { 141 | m_data *= other; return *this; 142 | } 143 | const memimage& operator /= (const T other) 144 | { 145 | m_data /= other; return *this; 146 | } 147 | const memimage& operator -= (const T other) 148 | { 149 | m_data -= other; return *this; 150 | } 151 | const memimage& operator += (const T other) 152 | { 153 | m_data += other; return *this; 154 | } 155 | 156 | // return information about the image 157 | unsigned xw() const { return m_xw; } // return width 158 | unsigned yw() const { return m_yw; } // return height 159 | unsigned nelem() const { return m_xw*m_yw; } // no elements 160 | const std::valarray& data() const { return m_data; } // return data 161 | 162 | // these make temporaries 163 | memimage operator * (const memimage& other) const 164 | { 165 | memimage t = *this; t *= other; return t; 166 | } 167 | memimage operator / (const memimage& other) const 168 | { 169 | memimage t = *this; t /= other; return t; 170 | } 171 | memimage operator - (const memimage& other) const 172 | { 173 | memimage t = *this; t -= other; return t; 174 | } 175 | memimage operator + (const memimage& other) const 176 | { 177 | memimage t = *this; t += other; return t; 178 | } 179 | 180 | // with constants 181 | memimage operator * (const T other) const 182 | { 183 | memimage t = *this; t *= other; return t; 184 | } 185 | memimage operator / (const T other) const 186 | { 187 | memimage t = *this; t /= other; return t; 188 | } 189 | memimage operator + (const T other) const 190 | { 191 | memimage t = *this; t += other; return t; 192 | } 193 | memimage operator - (const T other) const 194 | { 195 | memimage t = *this; t -= other; return t; 196 | } 197 | 198 | private: 199 | unsigned m_xw, m_yw; 200 | std::valarray m_data; 201 | }; 202 | 203 | 204 | } // namespace 205 | 206 | // convert types 207 | // put this in the header, as we'd generate 10^6 versions of this 208 | // otherwise. 209 | template template 210 | dm::memimage::memimage(const unsigned xw, const unsigned yw, 211 | const T2* const data) 212 | : m_xw(xw), m_yw(yw), 213 | m_data( data, nelem() ) 214 | { 215 | const unsigned size = nelem(); 216 | for(unsigned i=0; i != size; ++i) 217 | m_data[i] = static_cast( data[i] ); 218 | } 219 | 220 | // copy image of different type 221 | template template 222 | dm::memimage::memimage(const memimage& other) 223 | : m_xw( other.xw() ), m_yw( other.yw() ), 224 | m_data( static_cast(0), nelem() ) 225 | { 226 | const unsigned size = nelem(); 227 | const std::valarray& otherdata = other.data(); 228 | 229 | for(unsigned i=0; i != size; ++i) 230 | m_data[i] = static_cast(otherdata[i]); 231 | } 232 | 233 | #endif 234 | -------------------------------------------------------------------------------- /hideregions2/dm/test.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "dataset.hh" 6 | #include "image.hh" 7 | #include "exception.hh" 8 | #include "general.hh" 9 | #include "memimage.hh" 10 | 11 | int main() 12 | { 13 | try { 14 | dm::dataset ds("cen_image_b2.fits", dm::open); 15 | 16 | dm::image* im = ds.get_image(); 17 | 18 | dm::memimage* mem; 19 | im->create_memimage(&mem); 20 | dm::memimage& m = *mem; 21 | 22 | std::cout << m(19, 19) << '\n'; 23 | 24 | delete mem; 25 | delete im; 26 | } 27 | catch( dm::exception& e ) { 28 | std::cout << "Exception: " 29 | << e() << '\n'; 30 | } 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /hideregions2/hideregions2.cc: -------------------------------------------------------------------------------- 1 | // New version of hide regions, which iterates over input regions, 2 | // replacing contents of regions with random choices from the pixels 3 | // in a slightly expanded region 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define EXPANDSIZE 1 18 | 19 | // standard CAIO region files header 20 | extern "C" 21 | { 22 | # include 23 | } 24 | 25 | class Region 26 | { 27 | public: 28 | Region(const std::string& str) 29 | { 30 | reg = regParse(const_cast(str.c_str())); 31 | if(reg == 0) 32 | throw std::string("Invalid region: ") + str; 33 | } 34 | 35 | ~Region() 36 | { 37 | regFree(reg); 38 | } 39 | 40 | bool inside(double x, double y) 41 | { 42 | return regInsideRegion(reg, x, y); 43 | } 44 | 45 | private: 46 | regRegion* reg; 47 | }; 48 | 49 | struct Transform 50 | { 51 | Transform(dm::image* im) 52 | { 53 | dmDescriptor* imdesc = im->get_descriptor(); 54 | dmDescriptor* phys = dmArrayGetAxisGroup(imdesc, 1); 55 | dmCoordGetTransform_d(phys, pcrpix, pcrval, pcdlt, 2); 56 | } 57 | 58 | double x2phys(unsigned x) const 59 | { 60 | return (x + 1 - pcrpix[0])*pcdlt[0] + pcrval[0]; 61 | } 62 | 63 | double y2phys(unsigned y) const 64 | { 65 | return (y + 1 - pcrpix[1])*pcdlt[1] + pcrval[1]; 66 | } 67 | 68 | double pcrpix[2], pcrval[2], pcdlt[2]; 69 | }; 70 | 71 | void fillRegion(Region* reg, Region* enlarge, const Transform& trans, 72 | const dm::memimage* inimage, 73 | dm::memimage* outimage) 74 | { 75 | std::vector vals; 76 | 77 | unsigned minx=outimage->xw(), maxx=0; 78 | unsigned miny=outimage->yw(), maxy=0; 79 | 80 | for(unsigned y = 0; y < inimage->yw(); ++y) 81 | for(unsigned x = 0; x < inimage->xw(); ++x) 82 | { 83 | double px = trans.x2phys(x); 84 | double py = trans.y2phys(y); 85 | 86 | if(enlarge->inside(px, py) && !reg->inside(px, py)) 87 | { 88 | minx=std::min(minx, x); 89 | miny=std::min(miny, y); 90 | maxx=std::max(maxx, x); 91 | maxy=std::max(maxy, y); 92 | 93 | vals.push_back((*inimage)(x, y)); 94 | } 95 | } 96 | 97 | if(vals.empty()) 98 | return; 99 | 100 | for(unsigned y = miny; y <= maxy; ++y) 101 | for(unsigned x = minx; x <= maxx; ++x) 102 | { 103 | double px = trans.x2phys(x); 104 | double py = trans.y2phys(y); 105 | 106 | if(reg->inside(px, py)) 107 | { 108 | (*outimage)(x, y) = 109 | vals[unsigned(rand()*(1./RAND_MAX)*vals.size())]; 110 | } 111 | } 112 | } 113 | 114 | std::string enlargeRegion(const std::string& str) 115 | { 116 | boost::char_separator sep("(,)"); 117 | boost::tokenizer< boost::char_separator > tokens(str, sep); 118 | //BOOST_FOREACH (const std::string& t, tokens) { 119 | // std::cout << t << "." << std::endl; 120 | // } 121 | 122 | boost::tokenizer< boost::char_separator >::iterator it = tokens.begin(); 123 | 124 | std::string name(*it++); 125 | std::string out(name + "("); 126 | if(name == "ellipse") 127 | { 128 | out += *it++; 129 | out += ","; 130 | out += *it++; 131 | out += ","; 132 | 133 | double rad1(boost::lexical_cast(*it++)); 134 | out += boost::lexical_cast(rad1+EXPANDSIZE); 135 | out += ","; 136 | double rad2(boost::lexical_cast(*it++)); 137 | out += boost::lexical_cast(rad2+EXPANDSIZE); 138 | out += ","; 139 | 140 | out += *it++; 141 | } 142 | else if(name == "circle") 143 | { 144 | out += *it++; 145 | out += ","; 146 | out += *it++; 147 | out += ","; 148 | 149 | double rad(boost::lexical_cast(*it++)); 150 | out += boost::lexical_cast(rad+EXPANDSIZE); 151 | } 152 | else 153 | { 154 | throw std::string("Cannot interpret region"); 155 | } 156 | 157 | out += ")"; 158 | return out; 159 | } 160 | 161 | void run(const std::string& infile, 162 | const std::string& regfile, 163 | const std::string& outfile) 164 | { 165 | // load in image 166 | dm::dataset ds(infile); 167 | dm::image* im = ds.get_image(); 168 | 169 | Transform trans(im); 170 | 171 | // load the input image into memory 172 | dm::memimage* inimage; 173 | im->create_memimage(&inimage); 174 | 175 | dm::memimage outimage(inimage->xw(), inimage->yw()); 176 | for(unsigned y=0; y= 1 && line[0] == '#') 190 | continue; 191 | 192 | std::cout << "Region: " << line << '\n'; 193 | Region reg(line); 194 | 195 | Region enlarge( enlargeRegion(line) ); 196 | 197 | fillRegion(®, &enlarge, trans, inimage, &outimage); 198 | } 199 | 200 | dm::dataset ds_im_out(outfile, dm::create_over); 201 | dm::image *im_im_out = ds_im_out.create_image("IMAGE", dmFLOAT, 202 | outimage.xw(), 203 | outimage.yw()); 204 | im_im_out->write_from_memimage(outimage); 205 | } 206 | 207 | int main(int argc, char* argv[]) 208 | { 209 | if( argc != 4 ) 210 | { 211 | std::cerr << "Usage: " 212 | << argv[0] 213 | << " infile.fits region.reg outfile.fits\n"; 214 | return 1; 215 | } 216 | 217 | try 218 | { 219 | run(argv[1], argv[2], argv[3]); 220 | } 221 | catch(std::string s) 222 | { 223 | std::cerr << s << '\n'; 224 | return 1; 225 | } 226 | 227 | return 0; 228 | } 229 | --------------------------------------------------------------------------------