def build_thumbnail(source_skin, photo_offset_x, photo_offset_y): """ Build a photo of the source_skin by applying the transformation with the mappings/source image reversed. This is useful to visualise how the source_skin will look when uploaded to Minecraft. The photo is the source_skin unwrapped (e.g. head top unfolds at the top, back arms on the left/right etc). :param source_skin: Image representing the skin :param photo_offset_x: x-offset to start creating the photo :param photo_offset_y: y-offset to start creating the photo :return an Image representing the thumbnail """ logger.info("Converting source_skin back to photo...") mapping_skin_to_photo = {} for part in parts: to_coords = get_photo_coords(parts[part], photo_offset_x, photo_offset_y) if to_coords is None: # if the body part doesn't appear on the photo, just skip it (no need to 'paint it' as before logger.debug("Skipping %s because to_coords are None", xstr(part)) continue from_coords = get_skin_coords(parts[part]) mapping_skin_to_photo[xstr(part)] = [ source_skin, parts[part][2] - parts[part][0], parts[part][3] - parts[part][1], from_coords, to_coords ] logger.debug("Adding %s: %s", xstr(part), xstr(mapping_skin_to_photo[xstr(part)])) photo = transform_image(source_skin.width, source_skin.height, 'white', mapping_skin_to_photo) return photo
def transform_image(new_width, new_height, background, mappings): """ Create a new image based on the supplied mappings. Helper function to build images photo > skin > thumbnail :param new_width: width of the new image :param new_height: height of the new image :param background: colour of new image (None is transparent) :param mappings: a dictionary of mappings of the form key: [ sourceImage, widthToGrab, heightToGrab, fromTopLeftX, fromTopLeftY, toTopLeftX, toTopLeftY] :return the new Image """ new_img = Image.new('RGBA', (new_width, new_height), background) for part in mappings: logger.debug("Processing mapping: %s", xstr(mappings[part])) src_img, width, height, (from_x, from_y), (to_x, to_y) = mappings[part] logger.debug("Slicing %s from (%d, %d),(%d, %d) to (%d, %d)", xstr(part), from_x, from_y, from_x + width, from_y + height, to_x, to_y) clipboard = src_img.crop( (from_x, from_y, from_x + width, from_y + height)) new_img.paste(clipboard, (to_x, to_y, to_x + width, to_y + height)) return new_img
def build_skin(photo_filename, photo_offset_x, photo_offset_y): """ Build a minecraft skin and a thumbnail from the provided photo image. :param photo_filename: the source photo :param photo_offset_x: x-offset to start building the skin :param photo_offset_y: y-offset to start building the skin :return a new Image of the skin """ # open the reference images used to 'paint' body parts that are not available from the photo colours = { 'lightGrey': Image.open(IMG_FOLDER + '/lightGrey.png'), 'darkGrey': Image.open(IMG_FOLDER + '/darkGrey.png'), 'blue': Image.open(IMG_FOLDER + '/blue.png'), 'green': Image.open(IMG_FOLDER + '/green.png'), 'black': Image.open(IMG_FOLDER + '/black.png') } logger.info("Converting photo '%s' to skin with offset (%d, %d)", photo_filename, photo_offset_x, photo_offset_y) photo = photos.pick_asset(photos.get_assets(), multi=False).get_image() # resize the photo to match the skin size (keep the aspect ratio to avoid stretching) photo_scale = min(photo.width / SKIN_WIDTH, photo.height / SKIN_HEIGHT) logger.debug("Scaling factor = %f", photo_scale) x = int(photo.width / photo_scale) y = int(photo.height / photo_scale) logger.info("Resizing the photo from %dx%d to %dx%d", photo.width, photo.height, x, y) photo = photo.resize((x, y)) # Build the mappings to build the skin from the photo # by reading the position of each body part in the photo, and finding the location # in the Minecraft skin format. # If the part does not need to be taken from the photo, # use one of the reference images to 'paint' the part instead. # The image where the pixels are to be taken from are included in each # mapping entry (it is not always the photo - it is sometimes one of the reference images) mapping_photo_to_skin = {} for part in parts: from_coords = get_photo_coords(parts[part], photo_offset_x, photo_offset_y) if from_coords is None: # cannot use photo, so need to select another colour # colour = 'lightGrey' # default (e.g. back) colour = 'green' if 'Leg' in xstr(part): colour = 'blue' if 'Arm' in xstr(part): colour = 'green' if 'Bottom' in xstr(part): # takes priority over leg/arm colour = 'darkGrey' logger.debug("Painting %s with %s because from_coords are None", xstr(part), xstr(colour)) from_img = colours[colour] # uncomment this if you want textures # from_img = colours['black'] # uncomment this if you want black from_coords = (0, 0) else: from_coords = (from_coords[0] + photo_offset_x, from_coords[1] + photo_offset_y) from_img = photo to_coords = get_skin_coords(parts[part]) mapping_photo_to_skin[xstr(part)] = [ from_img, parts[part][2] - parts[part][0], parts[part][3] - parts[part][1], from_coords, to_coords ] logger.debug("Adding %s: %s", xstr(part), xstr(mapping_photo_to_skin[xstr(part)])) # create the skin new_skin = transform_image(64, 64, None, mapping_photo_to_skin) photo.close() return new_skin