def estimate_skew(flat, bignore=0.1, maxskew=2, skewsteps=8, debug=0):
''' estimate skew angle and rotate'''
d0,d1 = flat.shape
o0,o1 = int(bignore*d0),int(bignore*d1) # border ignore
flat = np.amax(flat)-flat
flat -= np.amin(flat)
est = flat[o0:d0-o0,o1:d1-o1]
ma = maxskew
ms = int(2*maxskew*skewsteps)
# print(linspace(-ma,ma,ms+1))
angle = estimate_skew_angle(est,
np.linspace(-ma,ma,ms+1),
debug=debug)
flat = interpolation.rotate(flat, angle, mode='constant', reshape=0)
flat = np.amax(flat)-flat
return flat, angle
python类rotate()的实例源码
def _transform(self, padded, number_of_transformations):
tiled = np.tile(np.expand_dims(padded, 4), [number_of_transformations])
for transformation_index in xrange(number_of_transformations):
angle = 360.0 * transformation_index / float(number_of_transformations)
tiled[:, :, :, :, transformation_index] = rotate(
tiled[:, :, :, :, transformation_index],
angle,
axes=[1, 2],
reshape=False)
print('finished transforming')
return tiled
def rotate_picture_randomly(self, x_or_probability, low=-10.0, high=10.0, reshape=True):
"""Rotate picture
Edited date:
160521
Test:
160712
Example:
::
da = nutszebra_data_augmentation_picture.DataAugmentationPicture()
da.load_picture(path).rotate_picture_randomly()
Args:
x_or_probability Optional([int, float, str, numpy.ndarray]): If int or float, this argument is considered as the probability and self.x is used for convert_to_image_format. If str or numpy.ndarray, set this argument as self.x and execute crop_center with self.x.
low (float): lower bound of random value (degree)
high (float): higher bound of random value (degree)
reshape (bool): If True, rorated picture is reshaped
__no_record (bool): the value of __no_record changes the value to be returned.
Returns:
Optional([tuple, class]): If __no_record is False, return self, otherwise return tuple(shaped x, info)
"""
np.random.seed()
random_value = np.random.uniform(low, high)
return (imrotate(x_or_probability, random_value, reshape=True), {'random_value': random_value})
def get_align_angles(img, axes="zyx"):
"""
Returns the angles needed to rotate an image to align it with the specified axes
:param img: A CZYX image as a 4d numpy array. The image that will be measured to get the
alignment angles. The image will not be altered by this function
:param axes: string, that must be an arrangement of 'xyz'
The major axis will be aligned with the first one, the minor with the last one.
'zyx' by default
:return: A list of tuple pairs, containing the axis indices and angles to rotate along the paired
axis. Meant to be passed into align_major
"""
if getattr(img, 'ndim', 0) < 3:
raise ValueError('img must be at least a 3d numpy array')
axis_map = {'x': 0, 'y': 1, 'z': 2}
if not isinstance(axes, str) or len(axes) != 3 or not all(a in axis_map for a in axes):
raise ValueError("axes must be an arrangement of 'xyz'")
# axes parameter string turned into a list of indices
axis_list = [axis_map[a] for a in axes]
maj_axis_i = axis_list[0]
# unit vectors for x, y, and z axis
axis_vectors = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
# slices for selecting yz, xz, and xy components from vectors
slices = (slice(1, 3), slice(0, 3, 2), slice(0, 2))
# index of the major axis (0, 1, or 2)
maj_axis = axis_vectors[axis_list[0]]
min_axis = axis_vectors[axis_list[-1]]
img_maj_axis, img_min_axis = get_major_minor_axis(img)
angles = []
for a in range(3):
if a != maj_axis_i:
# rotate around other two axis (e.g if aligning major to Z axis, rotate around Y and X to get there)
angle = angle_between(maj_axis[slices[a]], img_maj_axis[slices[a]])
angles.append([a, -angle])
img_maj_axis = np.dot(_get_rotation_matrix(a, angle), img_maj_axis)
img_min_axis = np.dot(_get_rotation_matrix(a, angle), img_min_axis)
# final rotation goes around major axis to align the minor axis properly
# has to be done last
angles.append([maj_axis_i, angle_between(min_axis[slices[maj_axis_i]], img_min_axis[slices[maj_axis_i]])])
return angles
def align_major(images, angles, reshape=True):
"""
Rotates images based on the angles passed in
:param images: Either a single image or a list of them. Must be at least 3d
numpy arrays, ordered as TCZYX
:param angles: The tuple returned by get_align_angles. Tells the function how to rotate the images
:param reshape: boolean. If True, the output will be resized to ensure that no data
from img is lost. If False, the output will be the same size as the input, with potential to
lose data that lies outside of the input shape after rotation. Default is True
:return: If a single image was passed in, it will will return a rotated copy of that image. If a list was
passed in, it will return a list of rotated images in the same order that they were passed in
"""
if isinstance(images, (list, tuple)):
return_list = True
image_list = images
else:
# turn it into a single element list for easier code
return_list = False
image_list = [images]
if not all(getattr(img, "ndim", 0) >= 3 for img in image_list):
raise ValueError("All images must be at least 3d")
rotate_axes = ((-3, -2), (-3, -1), (-2, -1))
# output list
out_list = []
for img in image_list:
out = img.copy()
for axis, angle in angles:
out = rotate(out, angle, reshape=reshape, order=1, axes=rotate_axes[axis], cval=(np.nan if reshape else 0))
out_list.append(out)
if reshape:
# cropping necessary as each resize makes the image bigger
# np.nan used as fill value when reshaping in order to make cropping easy
for i in range(len(out_list)):
out_list[i] = crop(out_list[i], np.nan)
out_list[i][np.isnan(out_list[i])] = 0
if return_list:
return out_list
else:
# turn it back from a single element list to a single image
return out_list[0]
def testtime_augmentation(image, label):
labels = []
images = []
rotations = [0]
flips = [[0,0],[1,0],[0,1],[1,1]]
shifts = [[0,0]]
zooms = [1]
for r in rotations:
for f in flips:
for s in shifts:
for z in zooms:
image2 = np.array(image)
if f[0]:
image2[:,:] = image2[::-1,:]
if f[1]:
image2 = image2.transpose(1,0)
image2[:,:] = image2[::-1,:]
image2 = image2.transpose(1,0)
#rotate(image2, r, reshape=False, output=image2)
#image3 = zoom(image2, [z,z])
#image3 = crop_or_pad(image3, P.INPUT_SIZE, 0)
#image2 = image3
# #shift(image2, [s[0],s[1]], output=image2)
images.append([image2]) #Adds color channel dimension!
labels.append(label)
return images, labels
def augment_multi(images,random_flip_x,shift_x,shift_y,rotation_degrees,zoom_factor):
pixels = images[0].shape[2]
center = pixels/2.-0.5
for i in range(len(images)):
image = images[i]
if random_flip_x:
print "flipping image"
#image[:,:] = image[:,::-1]
image[:,:,:] = image[:,:,::-1] #original
print image.shape
if rotation_degrees != 0:
print "rotating images"
if i == 0: #lung
image = rotate(image, rotation_degrees, axes=(1,2), reshape=False,cval=-3000,order=0)
else:
image = rotate(image, rotation_degrees, axes=(1,2), reshape=False,order=0)
print "post rotate ",image.shape
image = crop_or_pad_multi(image, pixels, -3000)
print image.shape
if shift_x != 0 or shift_y != 0:
print "shifting images by ",shift_x,shift_y, image.shape
if i == 0:
image = shift(image, [0,shift_x,shift_y],order=0,cval=-3000)
else:
image = shift(image, [0,shift_x,shift_y],order=0)
images[i] = image
return images
def rotate_transform(im, gt):
ang = np.random.uniform(-90, 90)
axes = np.random.permutation(3)[:2]
rot_im = rotate_scipy(im, ang, axes=axes, order=3, reshape=False)
rot_gt = groundtruth_(gt)
rot_gt = np.array([
rotate_scipy(class_map, ang, axes=axes, order=3, reshape=False)
for class_map in np.transpose(rot_gt, (3, 0, 1, 2))])
rot_gt = rot_gt.argmax(axis=0)
rot_gt = np.array(rot_gt, dtype='int8')
return (rot_im, rot_gt)
def rotate_3d_scipy(image, gt):
#if image.dtype != 'float32':
# image = np.asarray(image, dtype='float32')
#if gt.dtype != 'float32':
# gt = np.asarray(gt, dtype='float32')
ang = np.random.uniform(0, 360)
axes = (1,2)#np.random.permutation(3)[:2]
rot_im = rotate_scipy(image, ang, axes=axes, order=1, reshape=False)
rot_gt = rotate_scipy(gt, ang, axes=axes, order=0, reshape=False)
return rot_im, rot_gt
def minor_rotation(x, z):
"""
Assuming a batch size of 1.
More specifically: x is (1, depth, channels, height, width) and z is (1, height*width*depth, classes)
"""
from scipy.ndimage.interpolation import rotate as rotate_scipy
from breze.learn.data import one_hot
z_original_shape = z.shape
n_classes = z.shape[-1]
ang = float(np.random.uniform(-90, 90))
axes = np.random.permutation(3)[:2]
nx = np.transpose(x, (0, 2, 3, 4, 1))
nz = np.reshape(z, (1, x.shape[3], x.shape[4], x.shape[1], n_classes))
nz = np.transpose(nz, (0, 4, 1, 2, 3))
nx[0] = [rotate_scipy(modality, ang, axes=axes, order=3, reshape=False) for modality in nx[0]]
nx = np.transpose(nx, (0, 4, 1, 2, 3))
nz[0] = [rotate_scipy(class_map, ang, axes=axes, order=3, reshape=False) for class_map in nz[0]]
nz = nz[0].argmax(axis=0)
nz = np.reshape(nz, (-1,))
nz = np.reshape(one_hot(nz, n_classes), z_original_shape)
nx = np.asarray(nx, dtype=x.dtype)
nz = np.asarray(nz, dtype=z.dtype)
return (nx, nz)
def full_rotation(x, z):
"""
Assuming a batch size of 1.
More specifically: x is (1, depth, channels, height, width) and z is (1, height*width*depth, classes)
"""
from scipy.ndimage.interpolation import rotate as rotate_scipy
from breze.learn.data import one_hot
z_original_shape = z.shape
n_classes = z.shape[-1]
ang = float(np.random.uniform(0, 360))
axes = np.random.permutation(3)[:2]
nx = np.transpose(x, (0, 2, 3, 4, 1))
nz = np.reshape(z, (1, x.shape[3], x.shape[4], x.shape[1], n_classes))
nz = np.transpose(nz, (0, 4, 1, 2, 3))
nx[0] = [rotate_scipy(modality, ang, axes=axes, order=3, reshape=False) for modality in nx[0]]
nx = np.transpose(nx, (0, 4, 1, 2, 3))
nz[0] = [rotate_scipy(class_map, ang, axes=axes, order=3, reshape=False) for class_map in nz[0]]
nz = nz[0].argmax(axis=0)
nz = np.reshape(nz, (-1,))
nz = np.reshape(one_hot(nz, n_classes), z_original_shape)
nx = np.asarray(nx, dtype=x.dtype)
nz = np.asarray(nz, dtype=z.dtype)
return (nx, nz)
def add_rotations(image):
"""
Rotates the provided image a couple of time to generate more training data.
This should make the autoencoder more robust to diagonal roads for example.
The rotations will keep the dimensions of the image intact.
:param image: The image to rotate
:return: A list of rotated images, including the original image
"""
rot90img = rotate(image, 90, reshape=False, mode='reflect', order=3)
rot45img = rotate(image, 45, reshape=False, mode='reflect', order=3)
rot135img = rotate(image, 135, reshape=False, mode='reflect', order=3)
return [image, rot90img, rot45img, rot135img]
def extract_patches(filename_base, num_images, patch_size=conf.patch_size, phase='train'):
patches = []
for i in range(1, num_images+1):
if phase == 'train':
imageid = "satImage_%.3d" % i
image_filename = filename_base + imageid + ".png"
if os.path.isfile(image_filename):
img = mpimg.imread(image_filename)
img = resize(img, (conf.train_image_resize, conf.train_image_resize))
patches.append(image.extract_patches(img, (patch_size, patch_size), extraction_step=1))
rot90img = rotate(img, 90, reshape=False, mode='reflect', order=3)
patches.append(image.extract_patches(rot90img, (patch_size, patch_size), extraction_step=1))
rot45img = rotate(img, 45, reshape=False, mode='reflect', order=3)
patches.append(image.extract_patches(rot45img, (patch_size, patch_size), extraction_step=1))
rot135img = rotate(img, 135, reshape=False, mode='reflect', order=3)
patches.append(image.extract_patches(rot135img, (patch_size, patch_size), extraction_step=1))
elif phase == 'test':
imageid = "raw_test_%d_pixels" % i
image_filename = filename_base + imageid + ".png"
if os.path.isfile(image_filename):
img = mpimg.imread(image_filename)
img = resize(img, (conf.test_image_resize, conf.test_image_resize))
patches.append(image.extract_patches(img, (patch_size, patch_size), extraction_step=1))
elif phase == 'train_cnn_output':
imageid = "raw_satImage_%.3d_pixels" % i
image_filename = filename_base + imageid + ".png"
if os.path.isfile(image_filename):
img = mpimg.imread(image_filename)
img = resize(img, (conf.train_image_resize, conf.train_image_resize))
patches.append(image.extract_patches(img, (patch_size, patch_size), extraction_step=1))
else:
raise ValueError('incorrect phase')
return patches
def testtime_augmentation(image, label):
labels = []
images = []
rotations = [0]
flips = [[0,0],[1,0],[0,1],[1,1]]
shifts = [[0,0]]
zooms = [1]
for r in rotations:
for f in flips:
for s in shifts:
for z in zooms:
image2 = np.array(image)
if f[0]:
image2[:,:] = image2[::-1,:]
if f[1]:
image2 = image2.transpose(1,0)
image2[:,:] = image2[::-1,:]
image2 = image2.transpose(1,0)
#rotate(image2, r, reshape=False, output=image2)
#image3 = zoom(image2, [z,z])
#image3 = crop_or_pad(image3, P.INPUT_SIZE, 0)
#image2 = image3
# #shift(image2, [s[0],s[1]], output=image2)
images.append([image2]) #Adds color channel dimension!
labels.append(label)
return images, labels
def estimate_skew_angle(image,angles):
estimates = []
for a in angles:
v = mean(interpolation.rotate(image,a,order=0,mode='constant'),axis=1)
v = var(v)
estimates.append((v,a))
_,a = max(estimates)
return a
def estimate_skew_angle(image, angles, debug):
estimates = []
for a in angles:
v = np.mean(interpolation.rotate(image, a, order=0, mode='constant'),
axis=1)
v = np.var(v)
estimates.append((v,a))
if debug>0:
plt.clf()
plt.title("estimate_skew_angle")
plt.plot([y for x,y in estimates],[x for x,y in estimates])
raw_input("PRESS ANY KEY TO CONTINUE.")
_, a = max(estimates)
return a
def _extract(self, images, coords, mapping, args):
assert images.shape[1] == images.shape[2]
n_inst = coords.shape[0]
nb = args.get('num_bins', 8)
rotations = args.get('rotations', np.zeros((n_inst,), dtype=np.float32))
win_sizes = args.get('window_sizes', 32)
win_sizes = win_sizes if isinstance(win_sizes, np.ndarray) else np.ones((n_inst,), dtype=np.int32) * win_sizes
# Prepare descriptors
descriptors = np.zeros(tuple(coords.shape[:2])+(nb*4*4,), dtype=np.float32)
# Fill descriptors
coords, vis = np.copy(coords) - images.shape[1] / 2.0, np.empty(coords.shape[:2], dtype=np.bool)
for i, (c, r, mp, ws) in enumerate(zip(coords, rotations, mapping, win_sizes)):
hsize, qsize = ws/2, ws/4
# Get maximum window half-size, rotate and pad image
im = np.pad(
rotate(images[mp, ...], 57.2957*r),
((hsize, hsize), (hsize, hsize)),
'constant', constant_values=0
)
# Rotate geometry, set landmarks visibility
ims = im.shape[0] - hsize
c = np.dot(c, np.array([[np.cos(r), np.sin(r)], [-np.sin(r), np.cos(r)]])) + im.shape[0] / 2.0
vis[i, :] = (c[:, 0] >= hsize) & (c[:, 1] >= hsize) & (c[:, 0] < ims) & (c[:, 1] < ims)
# Extract descriptors from each interest window
for j, (jc, jv) in enumerate(zip(c, vis[i, :])):
descriptors[i, j, :] = hog(
im[jc[0]-hsize:jc[0]+hsize, jc[1]-hsize:jc[1]+hsize],
orientations=nb,
pixels_per_cell=(qsize, qsize),
cells_per_block=(1, 1)
) if jv else 0
# Normalize descriptors, return extracted information
return descriptors.reshape((len(mapping), -1)), vis
def pango_render_string(s,spec=None,fontfile=None,size=None,bg=(0.0,0.0,0.0),fg=(0.9,0.9,0.9),pad=5,
markup=1,scale=2.0,aspect=1.0,rotation=0.0):
"""Render a string using Cairo and the Pango text rendering interface. Fonts can either be given
as a fontfile or as a fontname. Size should be in pixels (?). You can specify a background and
foreground color as RGB floating point triples. (Currently unimplemented.)"""
S = pango.SCALE
face = None
if fontfile is not None: raise Exception("can't load ttf file into Pango yet; use fontname")
# make a guess at the size
w = max(100,int(scale*size*len(s)))
h = max(100,int(scale*size*1.5))
# possibly run through twice to make sure we get the right size buffer
for round in range(2):
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32,w,h)
cr = cairo.Context(surface)
if spec is not None: fd = pango.FontDescription(spec)
else: fd = pango.FontDescription()
if size is not None: fd.set_size(int(scale*size*S))
pcr = pangocairo.CairoContext(cr)
layout = pcr.create_layout()
layout.set_font_description(fd)
if not markup:
layout.set_text(s)
else:
layout.set_markup(s)
((xbear,ybear,tw,th),_) = layout.get_pixel_extents()
# print(xbear, ybear, tw, th)
tw = tw+2*pad
th = th+2*pad
if tw<=w and th<=h: break
w = tw
h = th
cr.set_source_rgb(*bg)
cr.rectangle(0,0,w,h)
cr.fill()
cr.move_to(-xbear+pad,-ybear+pad)
cr.set_source_rgb(*fg)
pcr.show_layout(layout)
data = surface.get_data()
data = bytearray(data)
a = array(data,'B')
a.shape = (h,w,4)
a = a[:th,:tw,:3]
a = a[:,:,::-1]
if rotation!=0.0: a = rotate(a,rotation,order=1)
a = zoom(a,(aspect/scale,1.0/scale/aspect,1.0),order=1)
return a
def augment(images):
pixels = images[0].shape[1]
center = pixels/2.-0.5
random_flip_x = P.AUGMENTATION_PARAMS['flip'] and np.random.randint(2) == 1
random_flip_y = P.AUGMENTATION_PARAMS['flip'] and np.random.randint(2) == 1
# Translation shift
shift_x = np.random.uniform(*P.AUGMENTATION_PARAMS['translation_range'])
shift_y = np.random.uniform(*P.AUGMENTATION_PARAMS['translation_range'])
rotation_degrees = np.random.uniform(*P.AUGMENTATION_PARAMS['rotation_range'])
zoom_factor = np.random.uniform(*P.AUGMENTATION_PARAMS['zoom_range'])
#zoom_factor = 1 + (zoom_f/2-zoom_f*np.random.random())
if CV2_AVAILABLE:
M = cv2.getRotationMatrix2D((center, center), rotation_degrees, zoom_factor)
M[0, 2] += shift_x
M[1, 2] += shift_y
for i in range(len(images)):
image = images[i]
if CV2_AVAILABLE:
#image = image.transpose(1,2,0)
image = cv2.warpAffine(image, M, (pixels, pixels))
if random_flip_x:
image = cv2.flip(image, 0)
if random_flip_y:
image = cv2.flip(image, 1)
#image = image.transpose(2,0,1)
images[i] = image
else:
if random_flip_x:
#image = image.transpose(1,0)
image[:,:] = image[::-1,:]
#image = image.transpose(1,0)
if random_flip_y:
image = image.transpose(1,0)
image[:,:] = image[::-1,:]
image = image.transpose(1,0)
rotate(image, rotation_degrees, reshape=False, output=image)
#image2 = zoom(image, [zoom_factor,zoom_factor])
image2 = crop_or_pad(image, pixels, -3000)
shift(image2, [shift_x,shift_y], output=image)
#affine_transform(image, np.array([[zoom_x,0], [0,zoom_x]]), output=image)
#z = AffineTransform(scale=(2,2))
#image = warp(image, z.params)
images[i] = image
return images
def augment(images):
pixels = images[0].shape[1]
center = pixels/2.-0.5
random_flip_x = P.AUGMENTATION_PARAMS['flip'] and np.random.randint(2) == 1
random_flip_y = P.AUGMENTATION_PARAMS['flip'] and np.random.randint(2) == 1
# Translation shift
shift_x = np.random.uniform(*P.AUGMENTATION_PARAMS['translation_range'])
shift_y = np.random.uniform(*P.AUGMENTATION_PARAMS['translation_range'])
rotation_degrees = np.random.uniform(*P.AUGMENTATION_PARAMS['rotation_range'])
zoom_factor = np.random.uniform(*P.AUGMENTATION_PARAMS['zoom_range'])
#zoom_factor = 1 + (zoom_f/2-zoom_f*np.random.random())
if CV2_AVAILABLE:
M = cv2.getRotationMatrix2D((center, center), rotation_degrees, zoom_factor)
M[0, 2] += shift_x
M[1, 2] += shift_y
for i in range(len(images)):
image = images[i]
if CV2_AVAILABLE:
#image = image.transpose(1,2,0)
image = cv2.warpAffine(image, M, (pixels, pixels))
if random_flip_x:
image = cv2.flip(image, 0)
if random_flip_y:
image = cv2.flip(image, 1)
#image = image.transpose(2,0,1)
images[i] = image
else:
if random_flip_x:
#image = image.transpose(1,0)
image[:,:] = image[::-1,:]
#image = image.transpose(1,0)
if random_flip_y:
image = image.transpose(1,0)
image[:,:] = image[::-1,:]
image = image.transpose(1,0)
rotate(image, rotation_degrees, reshape=False, output=image)
#image2 = zoom(image, [zoom_factor,zoom_factor])
image2 = crop_or_pad(image, pixels, -3000)
shift(image2, [shift_x,shift_y], output=image)
#affine_transform(image, np.array([[zoom_x,0], [0,zoom_x]]), output=image)
#z = AffineTransform(scale=(2,2))
#image = warp(image, z.params)
images[i] = image
return images
def pango_render_string(s,spec=None,fontfile=None,size=None,bg=(0.0,0.0,0.0),fg=(0.9,0.9,0.9),pad=5,
markup=1,scale=2.0,aspect=1.0,rotation=0.0):
"""Render a string using Cairo and the Pango text rendering interface. Fonts can either be given
as a fontfile or as a fontname. Size should be in pixels (?). You can specify a background and
foreground color as RGB floating point triples. (Currently unimplemented.)"""
S = pango.SCALE
face = None
if fontfile is not None: raise Exception("can't load ttf file into Pango yet; use fontname")
# make a guess at the size
w = max(100,int(scale*size*len(s)))
h = max(100,int(scale*size*1.5))
# possibly run through twice to make sure we get the right size buffer
for round in range(2):
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32,w,h)
cr = cairo.Context(surface)
if spec is not None: fd = pango.FontDescription(spec)
else: fd = pango.FontDescription()
if size is not None: fd.set_size(int(scale*size*S))
pcr = pangocairo.CairoContext(cr)
layout = pcr.create_layout()
layout.set_font_description(fd)
if not markup:
layout.set_text(s)
else:
layout.set_markup(s)
((xbear,ybear,tw,th),_) = layout.get_pixel_extents()
# print(xbear, ybear, tw, th)
tw = tw+2*pad
th = th+2*pad
if tw<=w and th<=h: break
w = tw
h = th
cr.set_source_rgb(*bg)
cr.rectangle(0,0,w,h)
cr.fill()
cr.move_to(-xbear+pad,-ybear+pad)
cr.set_source_rgb(*fg)
pcr.show_layout(layout)
data = surface.get_data()
data = bytearray(data)
a = array(data,'B')
a.shape = (h,w,4)
a = a[:th,:tw,:3]
a = a[:,:,::-1]
if rotation!=0.0: a = rotate(a,rotation,order=1)
a = zoom(a,(aspect/scale,1.0/scale/aspect,1.0),order=1)
return a
def pango_render_string(s,spec=None,fontfile=None,size=None,bg=(0.0,0.0,0.0),fg=(0.9,0.9,0.9),pad=5,
markup=1,scale=2.0,aspect=1.0,rotation=0.0):
"""Render a string using Cairo and the Pango text rendering interface. Fonts can either be given
as a fontfile or as a fontname. Size should be in pixels (?). You can specify a background and
foreground color as RGB floating point triples. (Currently unimplemented.)"""
S = pango.SCALE
face = None
if fontfile is not None: raise Exception("can't load ttf file into Pango yet; use fontname")
# make a guess at the size
w = max(100,int(scale*size*len(s)))
h = max(100,int(scale*size*1.5))
# possibly run through twice to make sure we get the right size buffer
for round in range(2):
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32,w,h)
cr = cairo.Context(surface)
if spec is not None: fd = pango.FontDescription(spec)
else: fd = pango.FontDescription()
if size is not None: fd.set_size(int(scale*size*S))
pcr = pangocairo.CairoContext(cr)
layout = pcr.create_layout()
layout.set_font_description(fd)
if not markup:
layout.set_text(s)
else:
layout.set_markup(s)
((xbear,ybear,tw,th),_) = layout.get_pixel_extents()
# print(xbear, ybear, tw, th)
tw = tw+2*pad
th = th+2*pad
if tw<=w and th<=h: break
w = tw
h = th
cr.set_source_rgb(*bg)
cr.rectangle(0,0,w,h)
cr.fill()
cr.move_to(-xbear+pad,-ybear+pad)
cr.set_source_rgb(*fg)
pcr.show_layout(layout)
data = surface.get_data()
data = bytearray(data)
a = array(data,'B')
a.shape = (h,w,4)
a = a[:th,:tw,:3]
a = a[:,:,::-1]
if rotation!=0.0: a = rotate(a,rotation,order=1)
a = zoom(a,(aspect/scale,1.0/scale/aspect,1.0),order=1)
return a
def halftone(cmyk, size, angles, fill, sharpness):
"""
Generates a halftone image from a cmyk image
Args:
cmyk (numpy array): 0.0-1.0 r x c x 4 image
size (int): half size of the averaging kernel in pixels
angles (list of float): 4 angles for the relative rotation of each channel
Returns:
numpy array: 0.0-1.0 r x c x 4 halftoned image
"""
halftone_image = np.zeros(cmyk.shape)
for i, (channel, angle) in enumerate(zip(np.rollaxis(cmyk, 2), angles)):
# total width of the kernel
s = 2 * size + 1
# rotate the image to eliminate overlap between the channels
rotated = rotate(channel, angle, reshape=True, prefilter=False, order=1)
# apply a gaussian filter to average over a the region of the kernel
averaged = gaussian_filter(rotated, size)
# find the central value of the filtered image; this is the average intensity in the region
halftone_weights = averaged[size::s, size::s]
# tile the weight image with the average intensity value
halftone_weights = np.repeat(np.repeat(halftone_weights, s, 0), s, 1)
halftone_weights = resize(halftone_weights, rotated.shape)
# TODO: consider using sigma to scale with magnitude
# create a 2D gaussian kernel that will be the "dot"; normalize it to be 1.0 in the center
kernel = gauss_kernel(size, sigma=fill*size)
# Apply the sharpness multiplier and clip the kernel to 1.0
kernel *= sharpness / np.max(kernel)
kernel = np.clip(kernel, 0.0, 1.0)
# tile the kernel across the image
num_kernels = np.array(rotated.shape) / s + 1
tiled_kernel = np.tile(kernel, num_kernels)
tiled_kernel = resize(tiled_kernel, rotated.shape)
# multiply the kernel image with the weights to generate the halftone image
halftone = tiled_kernel * halftone_weights
# rotate the image back to zero
halftone = rotate(halftone, -angle, prefilter=False, order=1)
# crop the image to the original size
halftone = crop_center(halftone, channel.shape)
# add this chanel to the full cmyk image
halftone_image[:,:,i] = halftone
# Image.fromarray(halftone*255).show()
# Image.fromarray(cmyk_to_rgb(halftone_image)).show()
return halftone_image