Filling pixels under or above some function - python-3.x

Seems like a simple problem, but I just cant wrap my head around it.
I have a config file in which I declare a few functions. It looks like this:
"bandDefinitions" : [
{
"0": ["x^2 + 2*x + 5 - y", "ABOVE"]
},
{
"0": ["sin(6*x) - y", "UNDER"]
},
{
"0": ["tan(x) - y", "ABOVE"]
}
]
These functions should generate 3 images. Every image should be filled depending on solution of equations, and provided position (Under or Above). I need to move the coordinate system to the center of the image, so I'm adding -y into the equation. Part of image which should be filled should be colored white, and the other part should be colored black.
To explain what I mean, I'm providing images for quadratic and sin functions.
What I'm doing is solve the equation for x in [-W/2, W/2] and store the solutions into the array, like this:
#Generates X axis dots and solves an expression which defines a band
#Coordinate system is moved to the center of the image
def __solveKernelDefinition(self, f):
xAxis = range(-kernelSize, kernelSize)
dots = []
for x in xAxis:
sol = f(x, kernelSize/2)
dots.append(sol)
print(dots)
return dots
I'm testing if some pixel should be colored white like this:
def shouldPixelGetNoise(y, x, i, currentBand):
shouldGetNoise = True
for bandKey in currentBand.bandDefinition.keys():
if shouldGetNoise:
pixelSol = currentBand.bandDefinition[bandKey][2](x, y)
renderPos = currentBand.bandDefinition[bandKey][1]
bandSol = currentBand.bandDefinition[bandKey][0]
shouldGetNoise = shouldGetNoise and pixelSol <= bandSol[i] if renderPos == Position.UNDER else pixelSol >= bandSol[i]
else:
break
return shouldGetNoise
def kernelNoise(kernelSize, num_octaves, persistence, currentBand, dimensions=2):
simplex = SimplexNoise(num_octaves, persistence, dimensions)
data = []
for i in range(kernelSize):
data.append([])
i1 = i - int(kernelSize / 2)
for j in range(kernelSize):
j1 = j - int(kernelSize / 2)
if(shouldPixelGetNoise(i1, j1, i, currentBand)):
noise = normalize(simplex.fractal(i, j, hgrid=kernelSize))
data[i].append(noise * 255)
else:
data[i].append(0)
I'm only getting good output for convex quadratic functions. If I try to combine them, I get a black image. Sin just doesn't work at all. I see that this bruteforce approach won't lead me anywhere, so I was wondering what algorithm should I use to generate these kinds of images?

As far as I understood, you want to plot your functions and fill up above or under of these functions. You might easily do this by creating a grid (i.e. a 2D Cartesian coordinate system) in numpy, and define your functions on the grid.
import numpy as np
import matplotlib.pyplot as plt
max_ax = 100
resolution_x = max_ax/5
resolution_y = max_ax/20
y,x = np.ogrid[-max_ax:max_ax+1, -max_ax:max_ax+1]
y,x = y/resolution_y, x/resolution_x
func1 = x**2 + 2*x + 5 <= -y
resolution_x = max_ax
resolution_y = max_ax
y,x = np.ogrid[-max_ax:max_ax+1, -max_ax:max_ax+1]
y,x = y/resolution_y, x/resolution_x
func2 = np.sin(6*x) <= y
func3 = np.tan(x) <= -y
fig,ax = plt.subplots(1,3)
ax[0].set_title('f(x)=x**2 + 2*x + 5')
ax[0].imshow(func1,cmap='gray')
ax[1].set_title('f(x)=sin(6*x)')
ax[1].imshow(func2,cmap='gray')
ax[2].set_title('f(x)=tan(x)')
ax[2].imshow(func3,cmap='gray')
plt.show()
Is this what you are looking for?
Edit: I adjusted the limits of x- and y-axes. Because, for example, sin(x) does not make much sense outside of the range [-1,1].

Related

Compact graph-vizualization using pydot

I would like to visualize a "linear" directed graph with the layout like that:
All the in- and out-degrees are 1 (except the first and last, of course). The length of the labels are different, so I can't calculate easily, how many nodes will fit in one row or the other. The code I have so far is this.
import networkx as nx
from networkx.drawing.nx_pydot import to_pydot
G = nx.DiGraph()
G.add_node("XYZ 1.0")
for i in range(1, 20):
G.add_node(f'XYZ 1.{i}', style='filled', fillcolor='skyblue')
G.add_edge(f'XYZ 1.{i-1}', f'XYZ 1.{i}')
# set defaults
G.graph['graph'] = {'rankdir': 'LR'}
G.graph['node'] = {'shape': 'rectangle'}
G.graph['edges'] = {'arrowsize': '4.0'}
pydt = to_pydot(G)
prog = 'dot'
file_name = f'nx_graph_{prog}.png'
pydt.write(file_name, prog=prog, format="png")
So far I use networkx in a project that needs to be run in a Python docker container, so I would like to use pydot and Networkx, if it is possible.
In some of the graphviz programs I can set coordinates if I understand correctly, but for setting coordinates I should know the widths of the boxes to avoid overlapping boxes.
I managed to find a way to do this with pydot. We can create a dot file with the coordinates with the write_dot function. Reading it back, we can get the coordinates that dot program created (and also the widths, heights). We can somehow calculate the new coordinates and modify them in the networkx Digraph. Converting again to pydot.Dot object, and at the end, we can use neato with the -n option to create the graph, that way we use the coordinates we have set. A working code can be seen below.
import networkx as nx
from networkx.drawing.nx_pydot import to_pydot
import pydot
from typing import List
G = nx.DiGraph()
G.add_node(0, label="XYZ 1.0")
for i in range(1, 20):
G.add_node(i, label=f'XYZ 1.{i}')
G.add_edge(i - 1, i)
# set defaults
G.graph['graph'] = {'rankdir': 'LR'}
G.graph['node'] = {'shape': 'rectangle'}
G.graph['edges'] = {'arrowsize': '4.0'}
pydt = to_pydot(G)
dot_data = pydt.create_dot()
pydt2 = pydot.graph_from_dot_data(dot_data.decode('utf-8'))[0]
def get_position(node):
pydot_node = pydt2.get_node(str(node))[0]
return [float(i) for i in pydot_node.get_attributes().get("pos")[1:-1].split(',')]
def fix_position(position: List, w: float = 1000, shift: float = 80):
x_orig, y_orig = position
n = int(x_orig / w)
y = y_orig - n * shift
remain_x = x_orig - n * w
if n % 2 == 0:
x = remain_x
else:
x = w - remain_x
return x, y
def refresh_coordinates_using_x():
for node in G.nodes:
position = get_position(node)
x, y = fix_position(position)
pos = f'"{x},{y}!"'
G.nodes[node]['pos'] = pos
refresh_coordinates_using_x()
pydt3 = to_pydot(G)
file_name = f'nx_graph_neato.png'
pydt3.write(file_name, prog=["neato", "-n"], format="png")
If you want to calculate the position of the nodes based on the widths, you need to know, that while the coordinates are in points, the widths are in inches. 1 inch is 72 points.
The result will be similar to this one.

Crop satellite image image based on a historical image with OpenCV in Python

I have the following problem, I have a pair of two images one historical and one present-day satellite image and as the historical image covers a smaller area I want to crop the satellite images. Here one can see the code I wrote for this:
import numpy as np
import cv2
import os
import imutils
import math
entries = os.listdir('../')
refImage = 0
histImages = []
def loadImage(index):
referenceImage = cv2.imread("../" + 'ref_' + str(index) + '.png')
top = int(0.5 * referenceImage.shape[0]) # shape[0] = rows
bottom = top
left = int(0.5 * referenceImage.shape[1]) # shape[1] = cols
right = left
referenceImage = cv2.copyMakeBorder(referenceImage, top, bottom, left, right, cv2.BORDER_CONSTANT, None, (0,0,0))
counter = 0
for entry in entries:
if entry.startswith("image_"+str(index)):
refImage = referenceImage.copy()
histImage = cv2.imread("../" + entry)
#histImages.append(img)
points = np.loadtxt("H2OPM/"+"CP_"+ entry[6:9] + ".txt", delimiter=",")
vector_image1 = [points[0][0] - points[1][0], points[0][1] - points[1][1]] #hist
vector_image2 = [points[0][2] - points[1][2], points[0][3] - points[1][3]] #ref
angle = angle_between(vector_image1, vector_image2)
hhist, whist, chist = histImage.shape
rotatedImage = imutils.rotate(refImage, angle)
x = int(points[0][2] - points[0][0])
y = int(points[1][2] - points[1][0])
crop_img = rotatedImage[x+left:x+left+hhist, y+top:y+top+whist]
print("NewImageWidth:", (y+top+whist)-(y+top),(x+left+hhist)-(x+left))
print(entry)
print(x,y)
counter += 1
#histImage = cv2.line(histImage, (points[0][0], ), end_point, color, thickness)
cv2.imwrite("../matchedImages/"+'image_' + str(index) + "_" + str(counter) + '.png' ,histImage)
#rotatedImage = cv2.line(rotatedImage, (), (), (0, 255, 0), 9)
cv2.imwrite("../matchedImages/"+'ref_' + str(index) + "_" + str(counter) + '.png' ,crop_img)
First, I load the original satellite image and pad it so I don't lose information due to the rotation, second, I load one of the matched historical images as well as the matched keypoints of the two images (i.e. a list of x_hist, y_hist, x_present_day, y_present_day). Third, I compute the rotation angle between the two images (which works) and fourth, I crop the image (and fifth, I save the images).
Problem: As stated the rotation works fine, but my program ends up cropping the wrong part of the image.
I think that, due to the rotation, the boundaries (i.e. left, right, top, bottom) are no longer correct and I think this is where my problem lies, but I am not sure how to fix this problem.
Information that might help:
The images are both scaled the same way (so one pixel = approx. 1m)
I have at least 6 keypoints for each image
I haven't looked at your code yet but would it be due to you mixing up the x's and y's ? Check the OpenCV documentation to make sure the variables you import are in the correct order.
During my limited time and experience with opencv, it is quite weird because sometimes, it asks for for example, BGR instead of RGB values. (In my programme, not yours)
Also, you seem to have a bunch of lists, make sure the list[x][y] is not mixed up as list[y][x]
So I found the error in my computation. The bounding boxes of the cutout area were wrongly converted into the present-day image.
So this:
x = int(points[0][2] - points[0][0])
y = int(points[1][2] - points[1][0])
was swapped with this:
v = [pointBefore[0],pointBefore[1],1]
# Perform the actual rotation and return the image
calculated = np.dot(m,v)
newPoint = (int(calculated[0]- points[0][0]),int(calculated[1]- points[0][1]))
where m(=M) is from the transformation:
def rotate_bound(image, angle):
# grab the dimensions of the image and then determine the
# center
(h, w) = image.shape[:2]
(cX, cY) = (w // 2, h // 2)
# grab the rotation matrix (applying the negative of the
# angle to rotate clockwise), then grab the sine and cosine
# (i.e., the rotation components of the matrix)
M = cv2.getRotationMatrix2D((cX, cY), -angle, 1.0)
cos = np.abs(M[0, 0])
sin = np.abs(M[0, 1])
# compute the new bounding dimensions of the image
nW = int((h * sin) + (w * cos))
nH = int((h * cos) + (w * sin))
# adjust the rotation matrix to take into account translation
M[0, 2] += (nW / 2) - cX
M[1, 2] += (nH / 2) - cY
# perform the actual rotation and return the image
return cv2.warpAffine(image, M, (nW, nH)), M
Thanks.

mplcursors: show and highlight coordinates of nearby local extreme

I have code that shows the label for each point in a matplotlib scatterplot using mplcursors, similar to this example. I want to know how to, form a list of values, make a certain point stand out, as in if I have a graph of points y=-x^2. When I go near the peak, it shouldn't show 0.001, but 0 instead, without the trouble needing to find the exact mouse placement of the top. I can't solve for each point in the graph, as I don't have a specific function.
Supposing the points in the scatter plot are ordered, we can investigate whether an extreme in a nearby window is also an extreme in a somewhat larger window. If, so we can report that extreme with its x and y coordinates.
The code below only shows the annotation when we're close to a local maximum or minimum. It also temporarily shows a horizontal and vertical line to indicate the exact spot. The code can be a starting point for many variations.
import matplotlib.pyplot as plt
import mplcursors
import numpy as np
near_window = 10 # the width of the nearby window
far_window = 20 # the width of the far window
def show_annotation(sel):
ind = sel.target.index
near_start_index = max(0, ind - near_window)
y_near = y[near_start_index: min(N, ind + near_window)]
y_far = y[max(0, ind - far_window): min(N, ind + far_window)]
near_max = y_near.max()
far_max = y_far.max()
annotation_str = ''
if near_max == far_max:
near_argmax = y_near.argmax()
annotation_str = f'local max:\nx:{x[near_start_index + near_argmax]:.3f}\ny:{near_max:.3f}'
maxline = plt.axhline(near_max, color='crimson', ls=':')
maxline_x = plt.axvline(x[near_start_index+near_argmax], color='grey', ls=':')
sel.extras.append(maxline)
sel.extras.append(maxline_x)
else:
near_min = y_near.min()
far_min = y_far.min()
if near_min == far_min:
near_argmin = y_near.argmin()
annotation_str = f'local min:\nx:{x[near_start_index+near_argmin]:.3f}\ny:{near_min:.3f}'
minline = plt.axhline(near_min, color='limegreen', ls=':')
minline_x = plt.axvline(x[near_start_index + near_argmin], color='grey', ls=':')
sel.extras.append(minline)
sel.extras.append(minline_x)
if len(annotation_str) > 0:
sel.annotation.set_text(annotation_str)
else:
sel.annotation.set_visible(False) # hide the annotation
# sel.annotation.set_text(f'x:{sel.target[0]:.3f}\n y:{sel.target[1]:.3f}')
N = 500
x = np.linspace(0, 100, 500)
y = np.cumsum(np.random.normal(0, 0.1, N))
box = np.ones(20) / 20
y = np.convolve(y, box, mode='same')
scat = plt.scatter(x, y, s=1)
cursor = mplcursors.cursor(scat, hover=True)
cursor.connect('add', show_annotation)
plt.show()

Weighted moving average in python with different width in different regions

I was trying to take a oscillation avarage of a highly oscillating data. The oscillations are not uniform, it has less oscillations in the initial regions.
x = np.linspace(0, 1000, 1000001)
y = some oscillating data say, sin(x^2)
(The original data file is huge, so I can't upload it)
I want to take a weighted moving avarage of the function and plot it. Initially the period of the function is larger, so I want to take avarage over a large time interval. While I can do with smaller time interval latter.
I have found a possible elegant solution in following post:
Weighted moving average in python
However, I want to have different width in different regions of x. Say when x is between (0,100) I want the width=0.6, while when x is between (101, 300) width=0.2 and so on.
This is what I have tried to implement( with my limited knowledge in programing!)
def weighted_moving_average(x,y,step_size=0.05):#change the width to control average
bin_centers = np.arange(np.min(x),np.max(x)-0.5*step_size,step_size)+0.5*step_size
bin_avg = np.zeros(len(bin_centers))
#We're going to weight with a Gaussian function
def gaussian(x,amp=1,mean=0,sigma=1):
return amp*np.exp(-(x-mean)**2/(2*sigma**2))
if x.any() < 100:
for index in range(0,len(bin_centers)):
bin_center = bin_centers[index]
weights = gaussian(x,mean=bin_center,sigma=0.6)
bin_avg[index] = np.average(y,weights=weights)
else:
for index in range(0,len(bin_centers)):
bin_center = bin_centers[index]
weights = gaussian(x,mean=bin_center,sigma=0.1)
bin_avg[index] = np.average(y,weights=weights)
return (bin_centers,bin_avg)
It is needless to say that this is not working! I am getting the plot with the first value of sigma. Please help...
The following snippet should do more or less what you tried to do. You have mainly a logical problem in your code, x.any() < 100 will always be True, so you'll never execute the second part.
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 10, 1000)
y = np.sin(x**2)
def gaussian(x,amp=1,mean=0,sigma=1):
return amp*np.exp(-(x-mean)**2/(2*sigma**2))
def weighted_average(x,y,step_size=0.3):
weights = np.zeros_like(x)
bin_centers = np.arange(np.min(x),np.max(x)-.5*step_size,step_size)+.5*step_size
bin_avg = np.zeros_like(bin_centers)
for i, center in enumerate(bin_centers):
# Select the indices that should count to that bin
idx = ((x >= center-.5*step_size) & (x <= center+.5*step_size))
weights = gaussian(x[idx], mean=center, sigma=step_size)
bin_avg[i] = np.average(y[idx], weights=weights)
return (bin_centers,bin_avg)
idx = x <= 4
plt.plot(*weighted_average(x[idx],y[idx], step_size=0.6))
idx = x >= 3
plt.plot(*weighted_average(x[idx],y[idx], step_size=0.1))
plt.plot(x,y)
plt.legend(['0.6', '0.1', 'y'])
plt.show()
However, depending on the usage, you could also implement moving average directly:
x = np.linspace(0, 60, 1000)
y = np.sin(x**2)
z = np.zeros_like(x)
z[0] = x[0]
for i, t in enumerate(x[1:]):
a=.2
z[i+1] = a*y[i+1] + (1-a)*z[i]
plt.plot(x,y)
plt.plot(x,z)
plt.legend(['data', 'moving average'])
plt.show()
Of course you could then change a adaptively, e.g. depending of the local variance. Also note that this has apriori a small bias depending on a and the step size in x.

ValueError: operands could not be broadcast together with shapes (3,) (0,)

My aim is to make the image1 move along the ring from its current position upto 180 degree. I have been trying to do different things but nothing seem to work. My final aim is to move both the images along the ring in different directions and finally merge them to and make them disappear.I keep getting the error above.Can you please help? Also can you tell how I can go about this problem?
from visual import *
import numpy as np
x = 3
y = 0
z = 0
i = pi/3
c = 0.120239 # A.U/minute
r = 1
for theta in arange(0, 2*pi, 0.1): #range of theta values; 0 to
xunit = r * sin(theta)*cos(i) +x
yunit = r * sin(theta)*sin(i) +y
zunit = r*cos(theta) +z
ring = curve( color = color.white ) #creates a curve
for theta in arange(0, 2*pi, 0.01):
ring.append( pos=(sin(theta)*cos(i) +x,sin(theta)*sin(i) +y,cos(theta) +z) )
image1=sphere(pos=(2.5,-0.866,0),radius=0.02, color=color.yellow)
image2=sphere(pos=(2.5,-0.866,0),radius=0.02, color=color.yellow)
earth=sphere(pos=(-3,0,-0.4),color=color.yellow, radius =0.3,material=materials.earth) #creates the observer
d_c_p = pow((x-xunit)**2 + (y-yunit)**2 + (z-zunit)**2,0.5) #calculates the distance between the center and points on ring
d_n_p = abs(yunit + 0.4998112152755791) #calculates the distance to the nearest point
t1 = ( d_c_p+d_n_p)/c
t0=d_c_p/c
t=t1-t0 #calculates the time it takes from one point to another
theta = []
t = []
dtheta = np.diff(theta) #calculates the difference in theta
dt = np.diff(t) #calculates the difference in t
speed = r*dtheta/dt #hence this calculates the speed
deltat = 0.005
t2=0
while True:
rate(5)
image2.pos = image2.pos + speed*deltat #increments the position of the image1
t2 = t2 + deltat
Your problem is that image2.pos is a vector (that's the "3" in the error message) but speed*deltat is a scalar (that's the "0" in the error message). You can't add a vector and a scalar. Instead of a scalar "speed" you need a vector velocity. There seem to be some errors in indentation in the program you posted, so there is some possibility I've misinterpreted what you're trying to do.
For VPython questions it's better to post to the VPython forum, where there are many more VPython users who will see your question than if you post to stackoverflow:
https://groups.google.com/forum/?fromgroups&hl=en#!forum/vpython-users

Resources