© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2021
M. Cywiak, D. CywiakMulti-Platform Graphics Programming with Kivyhttps://doi.org/10.1007/978-1-4842-7113-1_18

18. Stereoscopic Two-Dimensional Fourier Transform

Moisés Cywiak1   and David Cywiak2
(1)
Leon, Guanajuato, Mexico
(2)
Queretaro, Mexico
 
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.
../images/510726_1_En_18_Chapter/510726_1_En_18_Fig1_HTML.jpg
Figure 18-1

Screenshots from an Android cell phone showing a two-dimensional spatial function (left) and its corresponding discrete Fourier transform obtained with the program (right)

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset