Related
I want to generate a random placement and a random trajectory inside of a polygon. To speed up computation I am looking to do it in vectorized way (using Numpy).
I created code for it if we have square boundaries:
import numpy as np
# coordinates of square
square_coords = [-255, -255, 256, 256] # [xMin, yMin, xMax, yMax]
# convert to np.array
square_boundaries = np.array([(square_coords[0], square_coords[2]), (square_coords[1], square_coords[3])])
# specify speeds with corresponding probabilities of each speed
ue_speed = [3, 4, 8, 25]
ue_speed_prob = [0.4, 0.2, 0.3, 0.1] # should sum up to 1
steps = 50
time_interval = 10
nwalks = 1
# calcultation without boundaries
v = np.random.choice(ue_speed, size=steps, p=ue_speed_prob)
R = np.expand_dims((v * time_interval), axis=-1)
theta = 2 * np.pi * np.random.rand(nwalks, steps)
xy = np.dstack((np.cos(theta), np.sin(theta))) * R
trajectory_no_boundaries = np.hstack((np.zeros((nwalks, 1, 2)), np.cumsum(xy, axis=1)))
# generete random placement inside of boundaries
start = (np.random.randint(square_coords[0], square_coords[2]), np.random.randint(square_coords[1], square_coords[3]))
# generate random trajectory inside of boundaries
size = np.diff(square_boundaries, axis=1).ravel()
trajectory = np.abs((trajectory_no_boundaries[0] + start - square_boundaries[:, 0] + size) % (2 * size) - size) + square_boundaries[:, 0]
I am trying to understand if it is possible to transform this code if we have polygon coordinates instead of square coordinates e.g.:
polygon_coords = [(100, 100), (80, 130), (90, 130), (90, 140), (70, 140), (150, 200), (120, 150), (100, 100)]
To give an idea of how this polygon looks like here is a visualization with Shapely:
from shapely import geometry
shapely_polygon_coords = [geometry.Point(coord) for coord in polygon_coords]
geometry.Polygon([[p.x, p.y] for p in shapely_polygon_coords])
output:
I have 9 points (longitudes, latitudes in degrees) on the surface of Earth follows.
XY = [(100, 10), (100, 11), (100, 13), (101, 10), (101, 11), (101, 13), (103, 10), (103, 11), (103, 13)]
print (len(XY))
# 9
I wanted to extract those points which are at least 3 degrees far from each other.
I tried it as follows.
results = []
for point in XY:
x1,y1 = point
for result in results:
x2,y2 = result
distance = math.hypot(x2 - x1, y2 - y1)
if distance >= 3:
results.append(point)
print (results)
But output is empty.
edit 2
from sklearn.metrics.pairwise import haversine_distances
from math import radians
results = []
for point in XY:
x1,y1 = [radians(_) for _ in point]
for result in results:
distance = haversine_distances((x1,y1), (x2,y2))
print (distance)
if distance >= 3:
results.append(point)
print (results)
Still the result is empty
edit 3
results = []
for point in XY:
x1,y1 = point
for point in XY:
x2,y2 = point
distance = math.hypot(x2 - x1, y2 - y1)
print (distance)
if distance >= 3:
results.append(point)
print (results)
print (len(results))
# 32 # unexpected len
Important: You've said you want to "Extract those points which are at least 3 degrees far from each other" but then you've used the Euclidean distance with math.hypot(). As mentioned by #martineau, this should use the Haversine angular distance.
Since your points are "(longitudes, latitudes in degrees)", they first need to be converted to radians. And the pairs should be flipped so that latitude comes first, as required by the haversine_distances() function. That can be done with:
XY_r = [(math.radians(lat), math.radians(lon)) for lon, lat in XY]
Here's the kicker - none of the combnation-making or looping is necesssary. If haversine_distances() is passed in a list of points, it will calculate the distances between all of them and provide a result as an array of arrays. These can then be converted back to degrees and checked; or convert 3 degrees to radians and then check against h-dists.
import math
import numpy as np
from sklearn.metrics.pairwise import haversine_distances
XY = [(100, 10), (100, 11), (100, 13), (101, 10), (101, 11), (101, 13), (103, 10), (103, 11), (103, 13)]
# convert to radians and flip so that latitude is first
XY_r = [(math.radians(lat), math.radians(lon)) for lon, lat in XY]
distances = haversine_distances(XY_r) # distances array-of-arrays in RADIANS
dist_criteria = distances >= math.radians(3) # at least 3 degrees (in radians) away
results = [point for point, result in zip(XY, dist_criteria) if np.any(result)]
print(results)
print(len(results))
print('<3 away from all:', set(XY) - set(results))
Output:
[(100, 10), (100, 11), (100, 13), (101, 10), (101, 13), (103, 10), (103, 11), (103, 13)]
8
<3 away from all: {(101, 11)}
Wrt the previous edit and your original code:
Your first two attempts are giving empty results because of this:
results = []
for point in XY:
...
for result in results:
results is initialised as an empty list. So the for result in results loop will directly exit. Nothing inside the loop executes.
The 3rd attempt is getting you 32 results because of repetitions. You've got:
for point in XY:
...
for point in XY:
so some points you get will be the same point.
To avoid duplicates in the loops:
Add a check for it and go to the next iteration:
if (x1, y1) == (x2, y2):
continue
Btw, you're mangling the point variable because it's reused in both loops. It doesn't cause a problem but makes your code harder to debug. Either make them point1 and point2, or even better, instead of for point in XY: x1, y1 = point, you can directly do for x1, y1 in XY - that's called tuple unpacking.
for x1, y1 in XY:
for x2, y2 in XY:
if (x1, y1) == (x2, y2):
continue
...
You also need to change result to be a set instead of a list so that the same point is not re-added to the results when it's more than 3 away from another point. Sets don't allow duplicates, that way points don't get repeated in results.
Use itertools.combinations() to get unique pairs of points without repetitions. This allows you to skip the duplicate check (unless XY actually has duplicate points) and brings the previous block down to one for-loop:
import itertools
import math
results = set() # unique results
for (x1, y1), (x2, y2) in itertools.combinations(XY, r=2):
distance = math.hypot(x2 - x1, y2 - y1) # WRONG! see above
if distance >= 3:
# add both points
results.update({(x1, y1), (x2, y2)})
print(results)
print(len(results))
print('<3 away from all:', set(XY) - results)
The (wrong) output:
{(103, 11), (100, 13), (101, 13), (100, 10), (103, 10), (101, 10), (103, 13), (100, 11)}
8
<3 away from all: {(101, 11)}
(The result is the same but merely by coincidence of the input data.)
I'm generating a list of (x,y) coordinates from detecting a ball's flight in a video. The problem I have is for a few frames in the middle of the video the ball can't be detected, for these frames the list appends (-1,-1).
Is there a way to estimate the true (x,y) coordinates of the ball for these points?
Eg tracked points list being:
pointList = [(60, 40), (55, 42), (53, 43), (-1, -1), (-1, -1), (-1, -1), (35, 55), (30, 60)]
Then returning an estimate of what the 3 (-1,-1) missing coordinates would be with context to the sourounding points (preserving the curve).
If it's a ball then theoretically it should have a parabolic path, you could try and fit a curve ignoring the (-1, -1) and then replace the missing values.
Something like...
import numpy as np
pointList = [(60, 40), (55, 42), (53, 43), (-1, -1), (-1, -1), (-1, -1), (35, 55), (30, 60)]
x, y = list(zip(*[(x, y) for (x, y) in pointList if x>0]))
fit = np.polyfit(x, y, 2)
polynome = np.poly1d(fit)
# call your polynome for missing data, e.g.
missing = (55 - i*(55-35)/4 for i in range(3))
print([(m, polynome(m)) for m in missing])
giving ...
[(55.0, 41.971982486554325), (50.0, 44.426515896714186), (45.0, 47.44514924300471)]
You could use scipys spline to interpolate the missing values:
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import splprep, splev
pointList = [(60, 40), (55, 42), (53, 43),
(-1, -1), (-1, -1), (-1, -1),
(35, 55), (30, 60)]
# Remove the missing values
pointList = np.array(pointList)
pointList = pointList[pointList[:, 0] != -1, :]
def spline(x, n, k=2):
tck = splprep(x.T, s=0, k=k)[0]
u = np.linspace(0.0, 1.0, n)
return np.column_stack(splev(x=u, tck=tck))
# Interpolate the points with a quadratic spline at 100 points
pointList_interpolated = spline(pointList, n=100, k=2)
plt.plot(*pointList.T, c='r', ls='', marker='o', zorder=10)
plt.plot(*pointList_interpolated.T, c='b')
If camera is not moving - just the ball and you ignore the wind, then trajectory is parabolic. See: https://en.wikipedia.org/wiki/Trajectory#Uniform_gravity,_neither_drag_nor_wind
In this case fit quadratic function to points which you know and you will get missing ones. Set also error of boundary points in the vicinity of unknown area (point 53,43 and 35, 55) to be 0 or close to 0 (no-error, big weight in interpolation) when fitting so your interpolation will go through these points.
There are some libraries for polynomial fit. E.g. numpy.polyfit:
https://docs.scipy.org/doc/numpy-1.10.1/reference/generated/numpy.polynomial.polynomial.polyfit.html
I am trying to create a heat map with python. For this I have to assign an RGB value to every value in the range of possible values. I thought of changing the color from blue (minimal value) over green to red (maximal value).
The picture example below explains how I thought of the color composition: We have a range from 1 (pure blue) to 3 (pure red), 2 is in between resembled by green.
I read about linear interpolation and wrote a function that (more or less) handles the calculation for a certain value in the range between a minimum and a maximum and returns an RGB tuple. It uses if and elif conditions (which does not make me completely happy):
def convert_to_rgb(minimum, maximum, value):
minimum, maximum = float(minimum), float(maximum)
halfmax = (minimum + maximum) / 2
if minimum <= value <= halfmax:
r = 0
g = int( 255./(halfmax - minimum) * (value - minimum))
b = int( 255. + -255./(halfmax - minimum) * (value - minimum))
return (r,g,b)
elif halfmax < value <= maximum:
r = int( 255./(maximum - halfmax) * (value - halfmax))
g = int( 255. + -255./(maximum - halfmax) * (value - halfmax))
b = 0
return (r,g,b)
However I wonder if one could write a function for each color value without using if conditions. Does anybody have an idea? Thank you a lot!
def rgb(minimum, maximum, value):
minimum, maximum = float(minimum), float(maximum)
ratio = 2 * (value-minimum) / (maximum - minimum)
b = int(max(0, 255*(1 - ratio)))
r = int(max(0, 255*(ratio - 1)))
g = 255 - b - r
return r, g, b
Here's another way to do it that, while not as absolutely short as possible, is much more general since it hasn't been hardcoded for your specific set of colors. This means it can also be used to linearly interpolate a specified range of values over a variably-sized palette of arbitrary colors.
Also note that colors could have been interpolated in other colorspaces giving results that may be more pleasing than in others. This is illustrated in the different results obtained from the two separate answers I submitted to a related question titled Range values to pseudocolor.
import sys
EPSILON = sys.float_info.epsilon # Smallest possible difference.
def convert_to_rgb(minval, maxval, val, colors):
# `colors` is a series of RGB colors delineating a series of
# adjacent linear color gradients between each pair.
# Determine where the given value falls proportionality within
# the range from minval->maxval and scale that fractional value
# by the total number in the `colors` palette.
i_f = float(val-minval) / float(maxval-minval) * (len(colors)-1)
# Determine the lower index of the pair of color indices this
# value corresponds and its fractional distance between the lower
# and the upper colors.
i, f = int(i_f // 1), i_f % 1 # Split into whole & fractional parts.
# Does it fall exactly on one of the color points?
if f < EPSILON:
return colors[i]
else: # Return a color linearly interpolated in the range between it and
# the following one.
(r1, g1, b1), (r2, g2, b2) = colors[i], colors[i+1]
return int(r1 + f*(r2-r1)), int(g1 + f*(g2-g1)), int(b1 + f*(b2-b1))
if __name__ == '__main__':
minval, maxval = 1, 3
steps = 10
delta = float(maxval-minval) / steps
colors = [(0, 0, 255), (0, 255, 0), (255, 0, 0)] # [BLUE, GREEN, RED]
print(' Val R G B')
for i in range(steps+1):
val = minval + i*delta
r, g, b = convert_to_rgb(minval, maxval, val, colors)
print('{:.3f} -> ({:3d}, {:3d}, {:3d})'.format(val, r, g, b))
Numeric output:
Val R G B
1.000 -> ( 0, 0, 255)
1.200 -> ( 0, 50, 204)
1.400 -> ( 0, 101, 153)
1.600 -> ( 0, 153, 101)
1.800 -> ( 0, 204, 50)
2.000 -> ( 0, 255, 0)
2.200 -> ( 51, 203, 0)
2.400 -> (102, 152, 0)
2.600 -> (153, 101, 0)
2.800 -> (203, 51, 0)
3.000 -> (255, 0, 0)
Here's the output visualized as a horizontal gradient:
You can often eliminate an if with an index into an array of two values. Python lacks a ternary conditional operator, but this works:
r = [red_curve_1, red_curve_2][value>=halfmax]
g = [green_curve_1, green_curve_2][value>=halfmax]
b = [blue_curve_1, blue_curve_2][value>=halfmax]
Replace the *_curve_1 and *_curve_2 expressions with the constants or slopes or curves either left or right of the midpoint, respectively.
I'll leave those substitutions to you, but for example:
red_curve_1 and blue_curve_2 are simply 0
green_curve_1 is 255*(value-minimum)/(halfmax-minimum)
etc.
"We sense light intensity on a logarithmic scale
– an exponential intensity ramp will be seen as a linear
ramp" https://courses.cs.washington.edu/courses/cse455/09wi/Lects/lect11.pdf
From the https://en.wikipedia.org/wiki/RGB_color_model: "an input intensity RGB value of (0.5, 0.5, 0.5) only outputs about 22% of full brightness (1.0, 1.0, 1.0), instead of 50%"
This leads to the brownish smudge at 2.5 in #martineau example, where it should be yellow, and cyan at 1.5 in order to get a proper hue gradient.
So the formula you should use to get the gradient is not necessarily what you will want. (sorry for not answering your question directly)
But it might be handy to convert to the HSV or HLS color space model, and use H (for hue) and use that as input, and convert back to RGB for display purposes.
ie:
colorsys.hsv_to_rgb(value, 1, 1)
https://docs.python.org/2/library/colorsys.html
For whoever do not feel like carrying all the code around, the "terminedia" package packs a gradient class which can handle general gradients with an arbitrary number of color stops, at arbitrary positions.
The resulting ColorGradient instance can be then used with an index from 0 to 1 to get the desired color at the given point.
For example, for the colors given as [(4, 4, 4), (226, 75, 20), (4, 162, 221)], one can do:
In [286]: from terminedia import ColorGradient
In [287]: g = ColorGradient([(0, (4,4,4)), (0.5, (226, 75, 20)), (1, (4, 162, 221))])
In [288]: g[0.2]
Out[288]: <Color (92, 32, 10)>
In [289]: print([tuple(g[i/25]) for i in range(26)])
[(4, 4, 4), (21, 9, 5), (39, 15, 6), (57, 21, 7), (75, 26, 9), (92, 32, 10), (110, 38, 11), (128, 43, 12), (146, 49, 14), (163, 55, 15), (181, 60, 16), (199, 66, 18), (217, 72, 19), (217, 78, 28), (199, 85, 44), (181, 92, 60), (163, 99, 76), (146, 106, 92), (128, 113, 108), (110, 120, 124), (92, 127, 140), (75, 134, 156), (57, 141, 172), (39, 148, 188), (21, 155, 204), (4, 162, 221)]
The current released version of terminedia (0.4.3) can do that - the development code (https://github.com/jsbueno/terminedia/) signature made the stop positions optional when creating the Gradient, and the colors get automatically evenly spaced. That means that in versions after 0.4.3,
the same gradient can be created with: g = ColorGradient( [(4, 4, 4), (226, 75, 20), (4, 162, 221)])
After test in blender, you need to limit the value between the minimum and maximum, then the result is right
import numpy as np
def convert_to_rgb(minimum, maximum, value):
value = np.clip(value, minimum, maximum)
minimum, maximum = float(minimum), float(maximum)
ratio = 2 * (value-minimum) / (maximum - minimum)
b = int(max(0, 255*(1 - ratio)))
r = int(max(0, 255*(ratio - 1)))
g = 255 - b - r
return (r/255.0,g/255.0,b/255.0)
PIL's Image.transform has a perspective-mode which requires an 8-tuple of data but I can't figure out how to convert let's say a right tilt of 30 degrees to that tuple.
Can anyone explain it?
To apply a perspective transformation you first have to know four points in a plane A that will be mapped to four points in a plane B. With those points, you can derive the homographic transform. By doing this, you obtain your 8 coefficients and the transformation can take place.
The site http://xenia.media.mit.edu/~cwren/interpolator/ (mirror: WebArchive), as well as many other texts, describes how those coefficients can be determined. To make things easy, here is a direct implementation according from the mentioned link:
import numpy
def find_coeffs(pa, pb):
matrix = []
for p1, p2 in zip(pa, pb):
matrix.append([p1[0], p1[1], 1, 0, 0, 0, -p2[0]*p1[0], -p2[0]*p1[1]])
matrix.append([0, 0, 0, p1[0], p1[1], 1, -p2[1]*p1[0], -p2[1]*p1[1]])
A = numpy.matrix(matrix, dtype=numpy.float)
B = numpy.array(pb).reshape(8)
res = numpy.dot(numpy.linalg.inv(A.T * A) * A.T, B)
return numpy.array(res).reshape(8)
where pb is the four vertices in the current plane, and pa contains four vertices in the resulting plane.
So, suppose we transform an image as in:
import sys
from PIL import Image
img = Image.open(sys.argv[1])
width, height = img.size
m = -0.5
xshift = abs(m) * width
new_width = width + int(round(xshift))
img = img.transform((new_width, height), Image.AFFINE,
(1, m, -xshift if m > 0 else 0, 0, 1, 0), Image.BICUBIC)
img.save(sys.argv[2])
Here is a sample input and output with the code above:
We can continue on the last code and perform a perspective transformation to revert the shear:
coeffs = find_coeffs(
[(0, 0), (256, 0), (256, 256), (0, 256)],
[(0, 0), (256, 0), (new_width, height), (xshift, height)])
img.transform((width, height), Image.PERSPECTIVE, coeffs,
Image.BICUBIC).save(sys.argv[3])
Resulting in:
You can also have some fun with the destination points:
I'm going to hijack this question just a tiny bit because it's the only thing on Google pertaining to perspective transformations in Python. Here is some slightly more general code based on the above which creates a perspective transform matrix and generates a function which will run that transform on arbitrary points:
import numpy as np
def create_perspective_transform_matrix(src, dst):
""" Creates a perspective transformation matrix which transforms points
in quadrilateral ``src`` to the corresponding points on quadrilateral
``dst``.
Will raise a ``np.linalg.LinAlgError`` on invalid input.
"""
# See:
# * http://xenia.media.mit.edu/~cwren/interpolator/
# * http://stackoverflow.com/a/14178717/71522
in_matrix = []
for (x, y), (X, Y) in zip(src, dst):
in_matrix.extend([
[x, y, 1, 0, 0, 0, -X * x, -X * y],
[0, 0, 0, x, y, 1, -Y * x, -Y * y],
])
A = np.matrix(in_matrix, dtype=np.float)
B = np.array(dst).reshape(8)
af = np.dot(np.linalg.inv(A.T * A) * A.T, B)
return np.append(np.array(af).reshape(8), 1).reshape((3, 3))
def create_perspective_transform(src, dst, round=False, splat_args=False):
""" Returns a function which will transform points in quadrilateral
``src`` to the corresponding points on quadrilateral ``dst``::
>>> transform = create_perspective_transform(
... [(0, 0), (10, 0), (10, 10), (0, 10)],
... [(50, 50), (100, 50), (100, 100), (50, 100)],
... )
>>> transform((5, 5))
(74.99999999999639, 74.999999999999957)
If ``round`` is ``True`` then points will be rounded to the nearest
integer and integer values will be returned.
>>> transform = create_perspective_transform(
... [(0, 0), (10, 0), (10, 10), (0, 10)],
... [(50, 50), (100, 50), (100, 100), (50, 100)],
... round=True,
... )
>>> transform((5, 5))
(75, 75)
If ``splat_args`` is ``True`` the function will accept two arguments
instead of a tuple.
>>> transform = create_perspective_transform(
... [(0, 0), (10, 0), (10, 10), (0, 10)],
... [(50, 50), (100, 50), (100, 100), (50, 100)],
... splat_args=True,
... )
>>> transform(5, 5)
(74.99999999999639, 74.999999999999957)
If the input values yield an invalid transformation matrix an identity
function will be returned and the ``error`` attribute will be set to a
description of the error::
>>> tranform = create_perspective_transform(
... np.zeros((4, 2)),
... np.zeros((4, 2)),
... )
>>> transform((5, 5))
(5.0, 5.0)
>>> transform.error
'invalid input quads (...): Singular matrix
"""
try:
transform_matrix = create_perspective_transform_matrix(src, dst)
error = None
except np.linalg.LinAlgError as e:
transform_matrix = np.identity(3, dtype=np.float)
error = "invalid input quads (%s and %s): %s" %(src, dst, e)
error = error.replace("\n", "")
to_eval = "def perspective_transform(%s):\n" %(
splat_args and "*pt" or "pt",
)
to_eval += " res = np.dot(transform_matrix, ((pt[0], ), (pt[1], ), (1, )))\n"
to_eval += " res = res / res[2]\n"
if round:
to_eval += " return (int(round(res[0][0])), int(round(res[1][0])))\n"
else:
to_eval += " return (res[0][0], res[1][0])\n"
locals = {
"transform_matrix": transform_matrix,
}
locals.update(globals())
exec to_eval in locals, locals
res = locals["perspective_transform"]
res.matrix = transform_matrix
res.error = error
return res
The 8 transform coefficients (a, b, c, d, e, f, g, h) correspond to the following transformation:
x' = (ax + by + c) / (gx + hy + 1)
y' = (dx + ey + f) / (gx + hy + 1)
These 8 coefficients can in general be found from solving 8 (linear) equations that define how 4 points on the plane transform (4 points in 2D -> 8 equations), see the answer by mmgp for a code that solves this, although you might find it a tad more accurate to change the line
res = numpy.dot(numpy.linalg.inv(A.T * A) * A.T, B)
to
res = numpy.linalg.solve(A, B)
i.e., there is no real reason to actually invert the A matrix there, or to multiply it by its transpose and losing a bit of accuracy, in order to solve the equations.
As for your question, for a simple tilt of theta degrees around (x0, y0), the coefficients you are looking for are:
def find_rotation_coeffs(theta, x0, y0):
ct = cos(theta)
st = sin(theta)
return np.array([ct, -st, x0*(1-ct) + y0*st, st, ct, y0*(1-ct)-x0*st,0,0])
And in general any Affine transformation must have (g, h) equal to zero. Hope that helps!
Here is a pure-Python version of generating the transform coefficients (as I've seen this requested by several). I made and used it for making the PyDraw pure-Python image drawing package.
If using it for your own project, note that the calculations requires several advanced matrix operations which means that this function requires another, luckily pure-Python, matrix library called matfunc originally written by Raymond Hettinger and which you can download here or here.
import matfunc as mt
def perspective_coefficients(self, oldplane, newplane):
"""
Calculates and returns the transform coefficients needed for a perspective
transform, ie tilting an image in 3D.
Note: it is not very obvious how to set the oldplane and newplane arguments
in order to tilt an image the way one wants. Need to make the arguments more
user-friendly and handle the oldplane/newplane behind the scenes.
Some hints on how to do that at http://www.cs.utexas.edu/~fussell/courses/cs384g/lectures/lecture20-Z_buffer_pipeline.pdf
| **option** | **description**
| --- | ---
| oldplane | a list of four old xy coordinate pairs
| newplane | four points in the new plane corresponding to the old points
"""
# first find the transform coefficients, thanks to http://stackoverflow.com/questions/14177744/how-does-perspective-transformation-work-in-pil
pb,pa = oldplane,newplane
grid = []
for p1,p2 in zip(pa, pb):
grid.append([p1[0], p1[1], 1, 0, 0, 0, -p2[0]*p1[0], -p2[0]*p1[1]])
grid.append([0, 0, 0, p1[0], p1[1], 1, -p2[1]*p1[0], -p2[1]*p1[1]])
# then do some matrix magic
A = mt.Matrix(grid)
B = mt.Vec([xory for xy in pb for xory in xy])
AT = A.tr()
ATA = AT.mmul(A)
gridinv = ATA.inverse()
invAT = gridinv.mmul(AT)
res = invAT.mmul(B)
a,b,c,d,e,f,g,h = res.flatten()
# finito
return a,b,c,d,e,f,g,h