Source code for panstamps.image.image

#!/usr/local/bin/python
# encoding: utf-8
"""
*Add crosshairs, logo, scale, orientation and fake transients to pre-downloaded PS1 images*

Author
: David Young
"""
from __future__ import division
from fundamentals import tools
from builtins import object
from past.utils import old_div
import sys
import os
os.environ['TERM'] = 'vt100'


[docs] class image(object): """ *The worker class for the image module* **Key Arguments** - ``log`` -- logger - ``settings`` -- the settings dictionary - ``imagePath`` -- path to the image to manipulate - ``arcsecSize`` -- the size of the image stamps to download (1 arcsec == 4 pixels). - ``crosshairs`` -- add crosshairs to the image?. Default *True* - ``transient`` -- add a small transient marker at the centre of the image. Default *False* - ``scale`` -- add scale bar and orientation indicator to the image. Default *True* - ``invert`` -- invert the colours of the image. Default *False* - ``greyscale`` -- convert the image to greyscale. Default *False* - ``colorImage`` -- is the input image a color image, Default **False*. Note, also assumes a color image if 'color' in filename **Usage** ```python from panstamps.image import image myimage = image( log=log, settings=False, imagePath="70.60271m21.72433/color_igr_ra70.602710_dec-21.724330_arcsec120_skycell0812.050.jpeg", arcsecSize=120, crosshairs=True, transient=False, scale=True, invert=False, greyscale=False, colorImage=True ).get() ``` Here's the resulting image from this code: .. image:: https://i.imgur.com/TXX2BS0.png """ # Initialisation def __init__( self, log, imagePath, arcsecSize, settings=False, crosshairs=True, transient=False, scale=True, invert=False, greyscale=False, colorImage=False ): self.log = log log.debug("instansiating a new 'image' object") self.settings = settings self.imagePath = imagePath self.crosshairs = crosshairs self.transient = transient self.scale = scale self.invert = invert self.greyscale = greyscale self.arcsecSize = arcsecSize self.colorImage = colorImage # xt-self-arg-tmpx return None # Method Attributes
[docs] def get(self): """ *annotate the PS1 image* **Return** - ``image`` -- a PIL image object """ self.log.debug('starting the ``get`` method') from PIL import Image, ImageDraw, ImageChops, ImageFont im = Image.open(self.imagePath) im = im.convert("RGB") # DETERMINE THE SIZE OF THE IMAGE imWidth, imHeight = im.size # THE CROSS HAIRS SHOULD BE 1/6 THE LENGTH OF THE SMALLEST DIMENSION chLen = int(old_div(min(imWidth, imHeight), 6)) # THE GAP IN THE CENTRE SHOULD BE 1/60 OF THE LENGTH OF THE SMALLEST # DIMENSION gapLen = int(old_div(min(imWidth, imHeight), 60)) # LINE WIDTH SHOULD BE EASILY VIEWABLE AT ALL SIZES - 0.2% OF THE WIDTH SEEMS GOOD # SEEMS FINE lineWidth = int(old_div(max(imWidth, imHeight), 350)) lines = [] l = (old_div(imWidth, 2) - gapLen - chLen, old_div(imHeight, 2), old_div(imWidth, 2) - gapLen, old_div(imHeight, 2)) lines.append(l) l = (old_div(imWidth, 2) + gapLen, old_div(imHeight, 2), old_div(imWidth, 2) + gapLen + chLen, old_div(imHeight, 2)) lines.append(l) l = (old_div(imWidth, 2), old_div(imHeight, 2) - gapLen - chLen, old_div(imWidth, 2), old_div(imHeight, 2) - gapLen) lines.append(l) l = (old_div(imWidth, 2), old_div(imHeight, 2) + gapLen, old_div(imWidth, 2), old_div(imHeight, 2) + gapLen + chLen) lines.append(l) if self.invert: im = ImageChops.invert(im) if self.greyscale: im = im.convert("L") im = im.convert("RGBA") # DETERMINE THE BEST COLOR FOR LINES # tmp = im.resize((1, 1), resample=3) # tmp = ImageChops.invert(tmp) # bestColor = tmp.getcolors()[0][1] if self.invert: bestColor = "#dc322f" elif not self.colorImage and 'color' not in self.imagePath.split("/")[-1]: bestColor = "#3c96ed" elif self.greyscale: bestColor = "#3c96ed" elif self.colorImage or 'color' in self.imagePath.split("/")[-1] and not self.invert: bestColor = "#7eb70a" else: bestColor = "#dc322f" # GENERATE THE DRAW OBJECT AND DRAW THE CROSSHAIRS draw = ImageDraw.Draw(im) if self.crosshairs: for l in lines: draw.line(l, fill=bestColor, width=lineWidth) from PIL import Image, ImageDraw, ImageChops, ImageFont # DRAW A SCALEBAR ON THE IMAGE physicalWidth = int(self.arcsecSize) startRatio = 0.3 sbPhysicalWidth = physicalWidth * startRatio sbPixelWidth = imWidth * startRatio # IF ON THE DEGREE SCALE if sbPhysicalWidth > 3600: divider = 3600 unit = "degree" # IF ON THE ARCMIN SCALE elif sbPhysicalWidth > 60: divider = 60. unit = "arcmin" # IF ON THE ARCSEC SCALE else: divider = 1. unit = "arcsec" # FIND THE WIDTH OF THE BAR TO THE NEAREST WHOLE NUMBER ON GIVEN SCALE tmpWidth = old_div(sbPhysicalWidth, divider) self.log.debug("physicalWidth = %(physicalWidth)s" % locals()) self.log.debug("sbPhysicalWidth = %(sbPhysicalWidth)s" % locals()) displayPhysicalWidth = old_div(sbPhysicalWidth, divider) self.log.debug( "displayPhysicalWidth = %(displayPhysicalWidth)s" % locals()) displayPhysicalWidth = int(old_div(sbPhysicalWidth, divider)) self.log.debug( "displayPhysicalWidth = %(displayPhysicalWidth)s" % locals()) ratio = old_div(displayPhysicalWidth, tmpWidth) sbPhysicalWidth = int(sbPhysicalWidth * ratio) sbPixelWidth = int(sbPixelWidth * ratio) # DRAW THE SCALEBAR l = (old_div(imWidth, 20), imHeight - old_div(imHeight, 20), old_div(imWidth, 20) + sbPixelWidth, imHeight - old_div(imHeight, 20)) if self.scale: draw.line(l, fill=bestColor, width=lineWidth) # ADD SCALE TEXT text = """%(displayPhysicalWidth)s %(unit)s""" % locals() fontsize = int(old_div(imWidth, 30)) moduleDirectory = os.path.dirname(__file__) font = ImageFont.truetype( moduleDirectory + "/../resources/fonts/source-sans-pro-regular.ttf", fontsize) if self.scale: draw.text((old_div(imWidth, 20), imHeight - old_div(imHeight, 20) - fontsize * 1.3), text, fill=bestColor, font=font, anchor=None) # ADD ORIENTATION INDICATOR lines = [] lineLength = int(old_div(min(imWidth, imHeight), 20)) l = (imWidth - old_div(imWidth, 20) - lineLength, imHeight - old_div(imHeight, 20), imWidth - old_div(imWidth, 20), imHeight - old_div(imHeight, 20)) lines.append(l) l = (imWidth - old_div(imWidth, 20), imHeight - old_div(imHeight, 20), imWidth - old_div(imWidth, 20), imHeight - old_div(imHeight, 20) - lineLength) lines.append(l) if self.scale: for l in lines: draw.line(l, fill=bestColor, width=lineWidth) # ADD SCALE TEXT draw.text((imWidth - old_div(imWidth, 20) - fontsize * 0.3, imHeight - old_div(imHeight, 20) - lineLength - fontsize * 1.3), "N", fill=bestColor, font=font, anchor=None) draw.text((imWidth - old_div(imWidth, 20) - lineLength - fontsize * 0.8, imHeight - old_div(imHeight, 20) - fontsize * 0.6), "E", fill=bestColor, font=font, anchor=None) del draw # ADD THE PS1 LOGO wmHeight = int(old_div(max(imWidth, imHeight), 20)) moduleDirectory = os.path.dirname(__file__) imagePath = moduleDirectory + "/../resources/ps1.png" logo = Image.open(imagePath) (logoWidth, logoHeight) = logo.size wmWidth = int((wmHeight / float(logoHeight)) * logoWidth) logo = logo.resize((wmWidth, wmHeight), resample=3) # CREATE A TRANSPARENT IMAGE THE SIZE OF THE ORIGINAL IMAGE - PASTE THE # LOGO WHERE REQUIRED logoPH = Image.new("RGBA", (imWidth, imHeight), color=(0, 0, 0, 0)) logoPH.paste(logo, box=(old_div(imHeight, 25), old_div(imHeight, 25))) # NOW TONE DOWN THE OPACITY trans = Image.new("RGBA", (imWidth, imHeight), color=(0, 0, 0, 0)) logo = Image.blend(trans, logoPH, alpha=0.75) # ADD THE WATERMARK STAMP TO THE ORIGINAL IMAGE im = Image.alpha_composite(im.convert("RGBA"), logo) # ADD A TRANSIENT MARKER outercircle = 60 if self.transient: draw = ImageDraw.Draw(im) xy1 = (old_div(imWidth, 2) - old_div(imWidth, 100), old_div(imWidth, 2) - old_div(imWidth, 100), old_div(imWidth, 2) + old_div(imWidth, 100), old_div(imWidth, 2) + old_div(imWidth, 100)) xy2 = (old_div(imWidth, 2) - old_div(imWidth, outercircle), old_div(imWidth, 2) - old_div(imWidth, outercircle), old_div(imWidth, 2) + old_div(imWidth, outercircle), old_div(imWidth, 2) + old_div(imWidth, outercircle)) xy3 = (old_div(imWidth, 2) - old_div(imWidth, (outercircle)) - 1, old_div(imWidth, 2) - old_div(imWidth, (outercircle)) - 1, old_div(imWidth, 2) + old_div(imWidth, (outercircle)) + 1, old_div(imWidth, 2) + old_div(imWidth, (outercircle)) + 1) xy4 = (old_div(imWidth, 2) - old_div(imWidth, (outercircle)) - 2, old_div(imWidth, 2) - old_div(imWidth, (outercircle)) - 2, old_div(imWidth, 2) + old_div(imWidth, (outercircle)) + 2, old_div(imWidth, 2) + old_div(imWidth, (outercircle)) + 2) draw.arc(xy2, 0, 360, fill="#b2141c") draw.arc(xy3, 0, 360, fill="#b2141c") draw.arc(xy4, 0, 360, fill="#b2141c") draw.pieslice(xy1, 0, 361, fill="#b2141c") im = im.convert("RGB") im.save(self.imagePath) self.log.debug('completed the ``get`` method') return image
# xt-class-method