In this chapter, we present elements for calculating and plotting stereoscopic two-dimensional Fourier transforms. Figure 18-1 provides two screenshots from an Android cell phone. On the left is a two-dimensional spatial function, and on the right is its corresponding Fourier transform obtained with the program.
The buttons to rotate the plots work as described in the previous chapter.
18.1 Plotting the Functions
As in the previous chapter, we will place the plots in the middle between the plane of projection and the screen. We will use two points of projection to create the red and cyan images, as shown in Listing 18-1.
OBJECT=0;
D=2000;
VX1=-60; VY1=600; VZ1=0;
VX2=60; VY2=600; VZ2=0;
P=0.5; #Place the function at z=D/2
N=40; #Number of pixels to represent the function
Listing 18-1
Defining Parameters for the Projections
By assigning 0 to the OBJECT parameter , we are shown the spatial plot. When this parameter is 1, we are making the plot active in the Fourier space.
The spatial and frequency axes, meshes, and functions are the same as described in the previous chapter.
Listings 18-2 and 18-3 provide the code for main.py and File.kv, respectively.
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.graphics import Line, Ellipse, Color
from kivy.clock import Clock
from kivy.core.image import Image as CoreImage
from PIL import Image, ImageDraw, ImageFont
import io
import os
import numpy as np
from kivy.lang import Builder
Builder.load_file(
os.path.join(os.path.dirname(os.path.abspath(
__file__)), 'File.kv')
);
#Avoid Form1 of being resizable
from kivy.config import Config
Config.set("graphics","resizable", False);
Config.set('graphics', 'width', '480');
Config.set('graphics', 'height', '680');
def Rect(s,a):
if np.abs(s)<=a/2:
return 1;
else:
return 0;
def Circ(s,t,a):
if np.sqrt(s**2 +t**2)<=a:
return 1;
else:
return 0;
OBJECT=0;
D=2000;
VX1=-60; VY1=600; VZ1=0;
VX2=60; VY2=600; VZ2=0;
P=0.5; #Place the function at z=D/2
N=40; #Number of pixels to represent the function
#Spatial coordinates
x=np.zeros(N+1);
z=np.zeros(N+1);
f=np.zeros( (N+1,N+1) );
#Creating the mesh
x1=np.zeros( (N+1,N+1) );
y1=np.zeros( (N+1,N+1) );
z1=np.zeros( (N+1,N+1) );
L=1;
XG0=0; ZG0=P*D; Amplitude=130;
for n in range (0,N+1): #Filling the x-z axes
x[n]=(n-N/2)/N*L;
z[n]=(n-N/2)/N*L;
dx=x[1]-x[0]; dz=z[1]-z[0];
for n in range(0,N+1): #Filling function pixels
for m in range(0,N+1):
f[n][m]=Circ(x[n],z[m],L/4);
Scale_x=140; Scale_y=100; Scale_z=140;
#Filling Mesh
for n in range (0,N+1):
for m in range(0,N+1):
x1[n][m]=Scale_x* x[n];
z1[n][m]=Scale_z*z[m]+ZG0;
y1[n][m]=Scale_y*f[n][m];
#Defining frequency parameters
i=1J;
Pi=np.pi;
B=10/L;
u=np.zeros(N+1); v=np.zeros(N+1);
F=np.zeros( (N+1,N+1), dtype=complex );
C=np.zeros( (N+1,N+1), dtype=complex );
u1=np.zeros( (N+1,N+1) );
v1=np.zeros( (N+1,N+1) );
I1=np.zeros( (N+1,N+1) );
for n in range(0,N+1):
u[n]=(n-N/2)/N*B;
v[n]=u[n];
#Calculating the two-dimensional discrete integral
for n in range(0,N+1):
for m in range(0,N+1):
for q in range(0,N+1):
C[n][q]=np.sum( f[q]*np.exp(
-i*2*Pi*u[n]*x) );
F[n][m]=np.sum( C[n]*np.exp(
-i*2*Pi*v[m]*z) )*dx*dz;
I=np.abs(F);
#Creating mesh for plotting
Scale_u1=15; Scale_v1=15; Scale_I1=480;
for n in range(0,N+1):
for m in range(0,N+1):
u1[n][m]=Scale_u1 * u[n];
v1[n][m]=Scale_v1 * v[m]+ZG0;
I1[n][m]=Scale_I1*I[n][m];
PointList=np.zeros( (N+1,2) );
def GraphFunction(xM,yM,zM,Which):
global VX1, VX2, VY1,VY2,VZ1,VZ2, N,D;
x1p=xM; y1p=yM; z1p=zM;
if (Which==0):
r,g,b = 200, 0, 0; #red Image
VX=VX1; VY=VY1; VZ=VZ1;
Draw=Draw1
else:
r,g,b= 0, 200, 200; #cyan image
VX=VX2; VY=VY2; VZ=VZ2;
Draw=Draw2
for n in range (0,N+1): #Horizontal Lines
for m in range (0,N+1):
Factor=(D-VZ)/(D-z1p[n][m]-VZ);
xA=XC+Factor*(x1p[n][m]-VX)+VX
+ (P/(1-P))*VX;
yA=YC-Factor*(y1p[n][m]-VY)
-VY -(P/(1-P))*VY;
PointList[m]=xA,yA;
List=tuple( map(tuple,PointList) );
Draw.line( List, fill=(r,g,b), width=2 );
for n in range (0,N+1): #Vertical Lines
for m in range (0,N+1):
Factor=(D-VZ)/(D-z1p[m][n]-VZ);
xA=XC+Factor*(x1p[m][n]-VX)
+VX + (P/(1-P))*VX;
yA=YC-Factor*(y1p[m][n]-VY)
-VY -(P/(1-P))*VY;
PointList[m]=xA,yA;
List=tuple( map(tuple,PointList) );
Draw.line( List, fill=(r,g,b), width=2 );
Font = ImageFont.truetype('Gargi.ttf', 40)
Draw1.text( (10,10), "3D", fill =
(255,0,0,1), font=Font);
Draw2.text( (30,10), "3D", fill =
(0,255,255,1), font=Font);
def ShowScene(B):
Array1=np.array(PilImage1);
Array2=np.array(PilImage2);
Array3=Array1 | Array2;
PilImage3=Image.fromarray(Array3);
Memory=io.BytesIO();
PilImage3.save(Memory, format="png");
Memory.seek(0);
ImagePNG=CoreImage(Memory, ext="png");
B.ids.Screen1.texture=ImagePNG.texture;
ImagePNG.remove_from_cache()
Memory.close();
PilImage3.close();
Array1=None;
Array2=None;
Array3=None;
def ClearObjects():
Draw1.rectangle( (0, 0, W-10, H-10), fill=
(60, 70, 30, 1) );
Draw2.rectangle( (0, 0, W-10, H-10), fill=
(60, 70, 30, 1) );
def RotateFunction(xM,yM,zM,B, Sense):
global D,N, XG0,ZG0;
x1p=xM; y1p=yM; z1p=zM;
if Sense==-1:
Teta=np.pi/180*(-4.0);
else:
Teta=np.pi/180*(4.0);
Cos_Teta=np.cos(Teta)
Sin_Teta=np.sin(Teta);
X0=XG0; Y0=0; Z0=ZG0 # Center of rotation
for n in range(0,N+1):
for m in range(0,N+1):
if (B.ids.Button3.state=="down" or
B.ids.Button4.state=="down"):
yP=(y1p[n][m]-Y0)*Cos_Teta
+ (x1p[n][m]-X0)*Sin_Teta + Y0;
xP=-(y1p[n][m]-Y0)*Sin_Teta
+(x1p[n][m]-X0)*Cos_Teta + X0;
y1p[n][m]=yP;
x1p[n][m]=xP;
if (B.ids.Button5.state=="down" or
B.ids.Button6.state=="down"):
yP=(y1p[n][m]-Y0)*Cos_Teta
+ (z1p[n][m]-Z0)*Sin_Teta + Y0;
zP=-(y1p[n][m]-Y0)*Sin_Teta
+(z1p[n][m]-Z0)*Cos_Teta + Z0;
y1p[n][m]=yP;
z1p[n][m]=zP;
if (B.ids.Button7.state=="down" or
B.ids.Button8.state=="down"):
xP=(x1p[n][m]-X0)*Cos_Teta
+ (z1p[n][m]-Z0)*Sin_Teta + X0;
zP=-(x1p[n][m]-X0)*Sin_Teta
+(z1p[n][m]-Z0)*Cos_Teta + Z0;
x1p[n][m]=xP;
z1p[n][m]=zP;
class Form1(FloatLayout):
def __init__(Handle, **kwargs):
super(Form1, Handle).__init__(**kwargs);
Event1=Clock.schedule_once(
Handle.Initialize);
def Initialize(B, *args):
global W,H, XC,YC, PilImage1,PilImage2, Draw1,Draw2;
# P= Percentage of the D distance
global P, Amplitude;
W,H=B.ids.Screen1.size;
XC=int (W/2)+0;
YC=int(H/2)+70;
PilImage1= Image.new('RGB', (W-10, H-10),
(60, 70, 30));
Draw1 = ImageDraw.Draw(PilImage1);
PilImage2= Image.new('RGB', (W-10, H-10),
(60, 70, 30));
Draw2 = ImageDraw.Draw(PilImage2);
Font = ImageFont.truetype('Gargi.ttf', 70);
Draw1.text( (30,200), "3D Images", fill =
(255,0,0,1), font=Font);
Draw2.text( (50,200), "3D Images", fill =
(0,255,255,1), font=Font);
ShowScene(B);
def Button1_Click(B):
global Draw1, Draw2;
ClearObjects(); # Clearing Draw1 and Draw2
if (OBJECT==0):
GraphFunction(x1,y1,z1,0);
GraphFunction(x1,y1,z1,1);
if (OBJECT==1):
GraphFunction(u1,I1,v1,0);
GraphFunction(u1,I1,v1,1);
ShowScene(B);
def Button2_Click(B):
ClearObjects(); # Clearing Draw1 and Draw2
Font = ImageFont.truetype('Gargi.ttf', 70);
Draw1.text( (30,200), "3D Images", fill =
(255,0,0,1), font=Font);
Draw2.text( (50,200), "3D Images", fill =
(0,255,255,1), font=Font);
ShowScene(B);
def Button3_Click(B):
ClearObjects(); # Clearing Draw1 and Draw2
if (OBJECT==0):
RotateFunction(x1,y1,z1,B,1);
GraphFunction(x1,y1,z1,0);
GraphFunction(x1,y1,z1,1);
if (OBJECT==1):
RotateFunction(u1,I1,v1,B,1);
GraphFunction(u1,I1,v1,0);
GraphFunction(u1,I1,v1,1);
ShowScene(B);
def Button4_Click(B):
ClearObjects();
if (OBJECT==0):
RotateFunction(x1,y1,z1,B,-1);
GraphFunction(x1,y1,z1,0);
GraphFunction(x1,y1,z1,1);
if (OBJECT==1):
RotateFunction(u1,I1,v1,B,-1);
GraphFunction(u1,I1,v1,0);
GraphFunction(u1,I1,v1,1);
ShowScene(B);
def Button5_Click(B):
ClearObjects();
if (OBJECT==0):
RotateFunction(x1,y1,z1,B,-1);
GraphFunction(x1,y1,z1,0);
GraphFunction(x1,y1,z1,1);
if (OBJECT==1):
RotateFunction(u1,I1,v1,B,-1);
GraphFunction(u1,I1,v1,0);
GraphFunction(u1,I1,v1,1);
ShowScene(B);
def Button6_Click(B):
ClearObjects();
if (OBJECT==0):
RotateFunction(x1,y1,z1,B,1);
GraphFunction(x1,y1,z1,0);
GraphFunction(x1,y1,z1,1);
if (OBJECT==1):
RotateFunction(u1,I1,v1,B,1);
GraphFunction(u1,I1,v1,0);
GraphFunction(u1,I1,v1,1);
ShowScene(B);
def Button7_Click(B):
ClearObjects();
if (OBJECT==0):
RotateFunction(x1,y1,z1,B,-1);
GraphFunction(x1,y1,z1,0);
GraphFunction(x1,y1,z1,1);
if (OBJECT==1):
RotateFunction(u1,I1,v1,B,-1);
GraphFunction(u1,I1,v1,0);
GraphFunction(u1,I1,v1,1);
ShowScene(B);
def Button8_Click(B):
ClearObjects();
if (OBJECT==0):
RotateFunction(x1,y1,z1,B,1),
GraphFunction(x1,y1,z1,0);
GraphFunction(x1,y1,z1,1);
if (OBJECT==1):
RotateFunction(u1,I1,v1,B,1),
GraphFunction(u1,I1,v1,0);
GraphFunction(u1,I1,v1,1);
ShowScene(B);
def Button9_Click(B):
global OBJECT
OBJECT=(OBJECT+1)%2;
B.ids.Label1.text=str(OBJECT);
ClearObjects();
if (OBJECT==0):
GraphFunction(x1,y1,z1,0);
GraphFunction(x1,y1,z1,1);
if (OBJECT==1):
GraphFunction(u1,I1,v1,0);
GraphFunction(u1,I1,v1,1);
ShowScene(B);
# This is the Start Up code.
class StartUp (App):
def build (BU):
BU.title="Form1"
return Form1();
if __name__ =="__main__":
StartUp().run();
Listing 18-2
Code for the main.py File
#:set W 440
#:set H 440
<Form1>:
id : Form1
Image:
id: Screen1
size_hint: None,None
pos_hint: {"x":0.04, "y":0.34}
size: W,H
canvas.before:
Color:
rgba: 0.9, 0.9, 0, 1
RoundedRectangle:
pos: self.pos
size: self.size
Button:
id: Button1
on_press: Form1.Button1_Click()
text: "Button1"
size_hint: None,None
pos_hint: {"x": 0.2, "y":0.03}
size: 100,30
Button:
id: Button2
on_press: Form1.Button2_Click()
text: "Button2"
size_hint: None,None
pos_hint: {"x": 0.63, "y":0.03}
size: 100,30
Button:
id: Button3
on_press: Form1.Button3_Click()
text: "Button3"
size_hint: None,None
pos_hint: {"x": 0.05, "y":0.12}
size: 100,30
always_release: True
Button:
id: Button4
on_press: Form1.Button4_Click()
text: "Button4"
size_hint: None,None
pos_hint: {"x": 0.73, "y":0.12}
size: 100,30
Button:
id: Button5
on_press: Form1.Button5_Click()
text: "Button5"
size_hint: None,None
pos_hint: {"x": 0.05, "y":0.20}
size: 100,30
Button:
id: Button6
on_press: Form1.Button6_Click()
text: "Button6"
size_hint: None,None
pos_hint: {"x": 0.73, "y":0.20}
size: 100,30
Button:
id: Button7
on_press: Form1.Button7_Click()
text: "Button7"
size_hint: None,None
pos_hint: {"x": 0.05, "y":0.28}
size: 100,30
Button:
id: Button8
on_press: Form1.Button8_Click()
text: "Button8"
size_hint: None,None
pos_hint: {"x": 0.73, "y":0.28}
size: 100,30
Button:
id: Button9
on_press: Form1.Button9_Click()
text: "Button9"
size_hint: None,None
pos_hint: {"x": 0.4, "y":0.12}
size: 100,30
Label:
id: Label1
text: "0"
font_size: 30
color: 1,1,0,1
size_hint: None,None
pos_hint: {"x": 0.38, "y":0.20}
size: 100,30
Listing 18-3
Code for the File.kv File
18.2 Summary
In this chapter, we presented a working example for calculating and plotting stereoscopic two-dimensional Fourier transforms.