How to get the precise geometric extent of a CIImage - core-image

Core Image appears to "round off" an image's precise geometric extent to something like the smallest integral rectangle containing the precise bounds. For example, if I create a CIImage from a 1000x1000 tiff file, the description of that image looks like
<CIImage: 0x6000006ccfe0 extent [0 0 1000 1000]>
affine [1 0 0 -1 0 1000] extent=[0 0 1000 1000]
colormatch sRGB_to_workingspace extent=[0 0 1000 1000]
IOSurface 0x6000002740e0(823) seed:1 RGBA16 extent=[0 0 1000 1000]
If I scale that image by 0.1111 with image.scaled(by: 0.1111), the description looks like
<CIImage: 0x600000635e60 extent [0 0 112 112]>
affine hiQ [0.1111 0 0 0.1111 0 0] extent=[0 0 111.1 111.1]
affine [1 0 0 -1 0 1000] extent=[0 0 1000 1000]
colormatch sRGB_to_workingspace extent=[0 0 1000 1000]
IOSurface 0x6000002740e0(823) seed:1 RGBA16 extent=[0 0 1000 1000]
Note that the scaled image's extent is [0 0 112 112], however the precise extent as defined by the affine transformation is actually [0 0 111.1 111.1]. And in fact, we can see in the description of the scaled image that Core Image knows this.
This is a common theme in CIImage extents, not just for affine transformations but for Core Image filters that modify image geometry. For example, applying a keystone filter produces a CIImage like
<CIImage: 0x6000006d8200 extent [11 -92 1378 1303]>
warpkernel _perspectiveWarp(invM=[1.10965 0.226294 0.000304031 -0.0198446 1.08452 0.000235566 -10.7133 -121.219 0.798578]) extent=[11.6103 -91.7677 1376.75 1302.34]
samplemode nearest extent=[0 0 1000 1000]
affine [1 0 0 -1 0 1000] extent=[0 0 1000 1000]
colormatch sRGB_to_workingspace extent=[0 0 1000 1000]
IOSurface 0x6000002740e0(823) seed:1 RGBA16 extent=[0 0 1000 1000]
The extent of the transformed image is [11 -92 1378 1303], but the precise extent of the geometric transformation is [11.6103 -91.7677 1376.75 1302.34]. This example is actually the motivation for my question, because I want to be able to compute precise image geometric extents as I apply a chain of various transformations on CIImage images. I can't do that when an input image's extent uses an approximation like [11 -92 1378 1303].
As another example of how this behavior can lead to undesirable results, translating the extent of a CIImage to (0,0) can be oddly wrong when using image.extent.origin as an offset for a translation transformation. If the precise image offset is not an integer, translating the image to the origin by the negation of the image extent's origin can produce an image whose extent's origin is not (0,0).

Related

EDITED Learning data not correctly

I'm studying deep-learning.
I'm making figure classifier: circle, rectangle, triangle, pentagon, star. And one-hot-encoded into label2idx = dict(rectangle=0, circle=1, pentagon=2, star=3, triangle=4)
But every learning rates per epoch are same and it do not learn about the image.
I made a Layer with using Relu function for activation function, Affine for each layer, Softmax for the last layer, and using Adam to optimizing the gradients.
I have totally 234 RGB images to learn, which has created on window paint 2D tool and it is 128 * 128 size but not using the whole canvas to draw the figure.
And the picture looks like:
The train result. left [] is predict, and the right [] is answer lable(I picked random images to print predict value and answer lable).:
epoch: 0.49572649572649574
[ 0.3149641 -0.01454905 -0.23183 -0.2493432 0.11655246] [0 0 0 0 1]
epoch: 0.6837606837606838
[ 1.67341673 0.27887525 -1.09800398 -1.12649948 -0.39533065] [1 0 0 0 0]
epoch: 0.7094017094017094
[ 0.93106499 1.49599772 -0.98549052 -1.20471573 -0.24997779] [0 1 0 0 0]
epoch: 0.7905982905982906
[ 0.48447043 -0.05460748 -0.23526179 -0.22869489 0.05468969] [1 0 0 0 0]
...
epoch: 0.9230769230769231
[14.13835867 0.32432293 -5.01623202 -6.62469261 -3.21594355] [1 0 0 0 0]
epoch: 0.9529914529914529
[ 1.61248239 -0.47768294 -0.41580036 -0.71899219 -0.0901478 ] [1 0 0 0 0]
epoch: 0.9572649572649573
[ 5.93142154 -1.16719891 -1.3656573 -2.19785097 -1.31258801] [1 0 0 0 0]
epoch: 0.9700854700854701
[ 7.42198941 -0.85870225 -2.12027192 -2.81081263 -1.83810873] [1 0 0 0 0]
I think the more it learn, prediction should like [ 0.00143 0.09357 0.352 0.3 0.253 ] [ 1 0 0 0 0 ], which means answer index should be close to 0, but it does not.
Even the train accuracy sometimes goes to 1.0 ( 100% ).
I'm loading and normalizing the images with below codes.
#data_list = data_list = glob('dataset\\training\\*\\*.jpg')
dataset['train_img'] = _load_img()
def _load_img():
data = [np.array(Image.open(v)) for v in data_list]
a = np.array(data)
a = a.reshape(-1, img_size * 3)
return a
#normalize
for v in dataset:
dataset['train_img'] = dataset['train_img'].astype(np.float32)
dataset['train_img'] /= dataset['train_img'].max()
dataset['train_img'] -= dataset['train_img'].mean(axis=1).reshape(len(dataset['train_img']), 1)
EDIT
I let the images to gray scale with Image.open(v).convert('LA')
and checking my prediction value, and it's example:
[-3.98576886e-04 3.41216374e-05] [1 0]
[ 0.00698861 -0.01111879] [1 0]
[-0.42003415 0.42222863] [0 1]
still not learning about the images. I removed 3 figures to test it, so I just have rectangle, and triangle total 252 images ( I drew more imges. )
And the prediction value is usually like opposite value( 3.1323, -3.1323 or 3.1323, -3.1303 ), I cannot figure out the reason.
Not just increasing numerical accuracy, when I use SGD for optimizer, the accuracy do not increase. Just same accuracy.
[ 0.02090227 -0.02085848] [1 0]
epoch: 0.5873015873015873
[ 0.03058879 -0.03086193] [0 1]
epoch: 0.5873015873015873
[ 0.04006064 -0.04004988] [1 0]
[ 0.04545139 -0.04547538] [1 0]
epoch: 0.5873015873015873
[ 0.05605123 -0.05595288] [0 1]
epoch: 0.5873015873015873
[ 0.06495255 -0.06500597] [1 0]
epoch: 0.5873015873015873
Yes. Your model is performing pretty well. The problem is not related to normalization(not even a problem). The model actually predicted outside of 0,1 which means the model is really confident.
The model will not try to optimize towards [1,0,0,0] because when it calculates the loss, it will firstly clip the values.
Hope this helps!

Is there a way to convert an image to black and white pixel by pixel?

When using image = image.convert("1") on a very light grey, it'll add little black pixels to "average" it out. I'm looking for something that just looks at every individual pixel and determines whether that pixel is closer to black or to white.
Please pay attention to the documentation on PIL.Image.convert:
The default method of converting a greyscale (“L”) or “RGB” image into a bilevel (mode “1”) image uses Floyd-Steinberg dither to approximate the original image luminosity levels. If dither is NONE, all values larger than 128 are set to 255 (white), all other values to 0 (black). To use other thresholds, use the point() method.
So, you actually want no dithering and must set this option explicitly when converting:
from matplotlib import pyplot as plt
import numpy as np
from PIL import Image
# Grayscale image as NumPy array with values from [0 ... 255]
image = np.reshape(np.tile(np.arange(256, dtype=np.uint8), 256), (256, 256))
# PIL grayscale image with values from [0 ... 255]
image_pil = Image.fromarray(image, mode='L')
# PIL grayscale image converted to mode '1' without dithering
image_pil_conv = image_pil.convert('1', dither=Image.NONE)
# Threshold PIL grayscale image using point with threshold 128 (for comparison)
threshold = 128
image_pil_thr = image_pil.point(lambda p: p > threshold and 255)
# Result visualization
plt.figure(1, figsize=(9, 8))
plt.subplot(2, 2, 1), plt.imshow(image, cmap=plt.gray()), plt.ylabel('NumPy array')
plt.subplot(2, 2, 2), plt.imshow(image_pil, cmap=plt.gray()), plt.ylabel('PIL image')
plt.subplot(2, 2, 3), plt.imshow(image_pil_conv, cmap=plt.gray()), plt.ylabel('PIL image converted, no dithering')
plt.subplot(2, 2, 4), plt.imshow(image_pil_thr, cmap=plt.gray()), plt.ylabel('PIL image thresholded')
plt.tight_layout()
plt.show()
The documentation is also imprecise: Actually, all values greater than OR EQUAL 128 are set to white, both for convert as well as for point – which makes sense, since [0 ... 127] are 128 values, and [128 ... 255] are 128 values.
Hope that helps!

How to draw dendrogram in matplotlib without using scipy?

I would like to use matplotlib to draw a dendrogram without using scipy. A similar question has been posted here; however, the marked solution suggests using scipy and the links in the other answers suggesting using ETE do not work. Using this example, I have verified the accuracy of my own method (ie, not scipy method) to apply agglomerative hierarchical clustering using the single-linkage criterion.
Using the same example linked from above, I have the necessary parameters to create my own dendrogram. The original distance_matrix is given by:
.. DISTANCE MATRIX (SHAPE=(6, 6)):
[[ 0 662 877 255 412 996]
[662 0 295 468 268 400]
[877 295 0 754 564 0]
[255 468 754 0 219 869]
[412 268 564 219 0 669]
[996 400 0 869 669 0]]
A masked array of distance_matrix is used such that the diagonal entries from above are not counted as minimums. The mask of the original distance_matrix is given by:
.. MASKED (BEFORE) DISTANCE MATRIX (SHAPE=(6, 6)):
[[-- 662 877 255 412 996]
[662 -- 295 468 268 400]
[877 295 -- 754 564 0]
[255 468 754 -- 219 869]
[412 268 564 219 -- 669]
[996 400 0 869 669 --]]
distance_matrix is changed in-place at every iteration of the algorithm. Once the algorithm has completed, distance_matrix is given by:
.. MASKED (AFTER) DISTANCE MATRIX (SHAPE=(1, 1)):
[[--]]
The levels (minimum distance of each merger) are give by:
.. 5 LEVELS:
[138, 219, 255, 268, 295]
We can also view the indices of the merged datapoints at every iteration; these indices correspond to the original distance_matrix since reducing dimensions has the effect of changing index positions. These indices are given by:
.. 5x2 LOCATIONS:
[(2, 5), (3, 4), (0, 3), (0, 1), (0, 2)]
From these indices, the ordering of the xticklabels of the dendrogram are given chronologically as:
.. 6 XTICKLABELS
[2 5 3 4 0 1]
In relation to the linked example,
0 = BA
1 = FI
2 = MI
3 = NA
4 = RM
5 = TO
Using these parameters, I would like to generate a dendrogram that looks like the one below (borrowed from linked example):
My attempt at trying to replicate this dendrogram using matplotlib is below:
fig, ax = plt.subplots()
for loc, level in zip(locations, levels):
x = np.array(loc)
y = level * np.ones(x.size)
ax.step(x, y, where='mid')
ax.set_xticks(xticklabels)
# ax.set_xticklabels(xticklabels)
plt.show()
plt.close(fig)
My attempt above produces the following figure:
I realize I have to reorder the xticklabels such that the first merged points appear at the right-edge, with each subsequent merger shifting towards the left; doing so necessarily means adjusting the width of the connecting lines. Also, I was using ax.step instead of ax.bar so that the lines would appear more organized (as opposed to rectangular bars everywhere); the only thing I can think to do is to draw horizontal and vertical lines using ax.axhline and ax.axvline. I am hoping there is a simpler way to accomplish what I would like. Is there a straight-forward approach using matplotlib?
While it would certainly be easier to rely on scipy, this is how I'd do it "manually", i.e. without it:
import matplotlib.pyplot as plt
import numpy as np
def mk_fork(x0,x1,y0,y1,new_level):
points=[[x0,x0,x1,x1],[y0,new_level,new_level,y1]]
connector=[(x0+x1)/2.,new_level]
return (points),connector
levels=[138, 219, 255, 268, 295]
locations=[(2, 5), (3, 4), (0, 3), (0, 1), (0, 2)]
label_map={
0:{'label':'BA','xpos':0,'ypos':0},
1:{'label':'FI','xpos':3,'ypos':0},
2:{'label':'MI','xpos':4,'ypos':0},
3:{'label':'NA','xpos':1,'ypos':0},
4:{'label':'RM','xpos':2,'ypos':0},
5:{'label':'TO','xpos':5,'ypos':0},
}
fig,ax=plt.subplots()
for i,(new_level,(loc0,loc1)) in enumerate(zip(levels,locations)):
print('step {0}:\t connecting ({1},{2}) at level {3}'.format(i, loc0, loc1, new_level ))
x0,y0=label_map[loc0]['xpos'],label_map[loc0]['ypos']
x1,y1=label_map[loc1]['xpos'],label_map[loc1]['ypos']
print('\t points are: {0}:({2},{3}) and {1}:({4},{5})'.format(loc0,loc1,x0,y0,x1,y1))
p,c=mk_fork(x0,x1,y0,y1,new_level)
ax.plot(*p)
ax.scatter(*c)
print('\t connector is at:{0}'.format(c))
label_map[loc0]['xpos']=c[0]
label_map[loc0]['ypos']=c[1]
label_map[loc0]['label']='{0}/{1}'.format(label_map[loc0]['label'],label_map[loc1]['label'])
print('\t updating label_map[{0}]:{1}'.format(loc0,label_map[loc0]))
ax.text(*c,label_map[loc0]['label'])
_xticks=np.arange(0,6,1)
_xticklabels=['BA','NA','RM','FI','MI','TO']
ax.set_xticks(_xticks)
ax.set_xticklabels(_xticklabels)
ax.set_ylim(0,1.05*np.max(levels))
plt.show()
This mostly relies on creating the dictionary label_map, which maps the original "locations" (i.e. (2,5)) to the "xtick order" (i.e. (4,5)). A "fork" is created in each step i using mk_fork(), which returns both points (which are subsequently connected in a line plot) as well as the connector point, which is then stored as the new values for 'xpos','ypos' within the label_map.
I've added multiple print() statements to emphasize what happens at each step, and added a .text() to highlight the location of each "connector".
Result:

GeoPandas: How to obtain bounding boxes for every geometry in a geodataframe

I am using GeoPandas in python and have a valid GeoDataframe of polygons.
0 POLYGON Z ((68.70999999999999 623.1 0, 35.71 6...
1 POLYGON Z ((221.33 645.02 0, 185.7 640.33 0, 1...
2 POLYGON Z ((150.3 650 0, 160.9 650 0, 150.58 6...
I want to obtain a new dataframe that has the bounding box coordinates for each row in the dataframe.
Now I am getting some odd behavior for GeoPandas.
Say I name the GeoDataFrame gdf, then using the code:
gdf.bounds
I get the corresponding error. I have no clue what this error is supposed to mean, since I did not pass any values into the bounds method--they were passed implicitly.
ValueError: Shape of passed values is (1, 110042), indices imply (4, 110042)
When I try:
gdf.geometry.bounds
I get the same ValueError...
However, when I do it this way, I get a valid answer:
gdf.head(10).bounds
I get
minx miny maxx maxy
0 0.00 618.15 68.71 650.00
1 169.56 640.33 221.33 650.00
2 150.30 648.64 160.90 650.00
So gdf and gdf.head() are not any different, yet one gives me an error and one does not. Does anyone know the correct way to get the bounding boxes corresponding to each row.
You can also try the following
# remove empty geometry
valid_geom = gdf[gdf.geometry.map(lambda z: True if not z.is_empty else False)]
# get the bounds of each geometry
valid_geom.geometry.map(lambda z: z.exterior.xy)
# or in one line
gdf[gdf.geometry.map(lambda z: True if not z.is_empty else False)].geometry.map(lambda z: z.exterior.xy)
This would result in the following output. you get (minx, miny, maxx, maxy) as a list.
0 ([346494.47052450513, 346512.1633455531, 34642...
1 ([347156.6195963654, 347140.5694171803, 347106...
2 ([347374.2493280142, 347343.280266067, 347331....
3 ([347752.9399173185, 347732.0804000348, 347699...
4 ([352462.7065634858, 352421.82634455897, 35239...
5 ([352398.84073305037, 352366.62657852937, 3523...
6 ([351619.2911484046, 351581.3489685701, 351559...
7 ([349298.04394918215, 349284.4299869118, 34926...
8 ([349402.6562116009, 349390.3714050767, 349364...
9 ([347447.35067824554, 347427.2888365253, 34740...
10 ([351038.9227137904, 351023.75894022046, 35101...
11 ([352360.8991716495, 352311.8060843693, 352289...
12 ([348053.8637179602, 348014.5578245763, 347995...
13 ([350854.3664365387, 350802.39711500367, 35075...
14 ([350661.291738528, 350539.01532645256, 350497...
15 ([349634.9936554617, 349617.43041924713, 34959...
16 ([346588.703008323, 346576.2541223159, 346560....
17 ([347323.7364982413, 347311.6537559405, 347289...
18 ([347592.9326738138, 347588.24603437353, 34757...
19 ([347871.4965194545, 347852.9032783319, 347846...
20 ([349503.7927385038, 349484.6946827946, 349482...
21 ([349917.505834857, 349907.19522809517, 349885...
22 ([350254.82670837734, 350243.1101097837, 35024...
dtype: object

Algorithm to make fewest triangles given points in 3d

With a set of points in 3d. How would one go about finding the fewest set of triangles connecting those points; creating a set of triangles all sharing at least two of their sides? (like a square sheet with bumps in it made of triangles)
example points for a 5x100x500 area:
points = [
# Constant Points
(0 , 0 , 0 ),
(0 , 100, 0 ),
(5 , 100, 500),
(1 , 50 , 100),
(2 , 60 , 200),
(3 , 75 , 300),
(4 , 80 , 400),
(5 , 0 , 499),
]
Here's what the points plotted would look like with the axes normalized:
I think Delaunay Triangulation might give you what you require:
http://en.wikipedia.org/wiki/Delaunay_triangulation

Resources