Source code for scenarioSmallBodyLandmarks

#
#  ISC License
#
#  Copyright (c) 2023, Autonomous Vehicle Systems Lab, University of Colorado at Boulder
#
#  Permission to use, copy, modify, and/or distribute this software for any
#  purpose with or without fee is hereby granted, provided that the above
#  copyright notice and this permission notice appear in all copies.
#
#  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
#  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
#  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
#  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
#  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
#  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
#  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#

r"""
Overview
--------

Discusses how to use the pinhole camera module to obtain landmark-based measurements around a small body.
This script sets up a 6-DOF spacecraft which is orbiting asteroid 433 Eros.

The script is found in the folder ``basilisk/examples`` and executed by using::

    python3 scenarioSmallBodyLandmarks.py

The simulation layout is shown in the following illustration.  A single simulation process is created
which contains both the spacecraft simulation, flight software and pinhole camera modules.

.. image:: /_images/static/test_scenarioSmallBodyLandmarks.svg
   :align: center

When the simulation completes 5 plots are shown for the 3D configuration at :math:`t_0`, the 3D configuration
at :math:`t_f`, the landmark pixels at :math:`t_f`, the number of visible landmarks evolution and the camera frame C axes
projection :math:`\hat c_1`, :math:`\hat c_2` and :math:`\hat c_3` onto the Hill or Orbit frame axes
:math:`\hat\imath_r`, :math:`\hat\imath_{\theta}` and :math:`\hat\imath_h`.

The basic simulation setup is very similar as the one used in :ref:`scenarioAttitudeGuidance`.
The dynamics simulation is setup using a :ref:`Spacecraft` module to which a gravity
effector is attached. Eros gravity is setup through a :ref:`PlanetEphemeris` module which simulates
Eros heliocentric motion. The flight software modules are copied from :ref:`scenarioAttitudeGuidance`
as to align the spacecraft body frame, thus also the camera frame, with the Hill frame. The goal is to
keep the camera pointing towards Eros center of mass in order to observe surface landmarks. In this
case, the attitude control flight software makes :math:`\hat b_1` body axis to point towards the
asteroid center of mass :math:`-\hat\imath_r` (negative radial direction ). The camera direction cosine matrix
is prescribed by the user as :math:`\hat c_3=\hat b_1`, then it follows that attitude control is ensuring
the camera focal direction (:math:`\hat c_3`) is pointing towards the asteroid. This way, the camera is able
to track surface landmarks.

The landmarks are setup based on Eros polyhedron shape. A landmark distribution is defined by surface
positions and their normals expressed in the planet rotating frame. The normal is used to check field
of view and lighting conditions from the camera and Sun respectively.

Since dynamics simulations can be computationally expensive, the module has a ``processBatch(rBatch_CP_P,
mrpBatch_BP, eBatch_SP_P, show_progress)`` method that is detached from the Simulation Base class. This method
requires batches (as matrices) of spacecraft position in the planet frame, spacecraft orientation in the planet
frame and Sun's unit vector in the planet frame. The results can be accessed from the pinhole camera class
as illustrated in the subsequent script.

Illustration of Simulation Results
----------------------------------

::

    show_plots = True, useBatch = False

The ``useBatch`` flag is turned off by default but it showcases how the module can be executed detached from the
dynamics simulation. This is very convenient as it allows isolated experimentation with the pinholeCamera module
without the need of rerunning dynamics simulations (which can be slow if non-Keplerian gravity models are used
e.g. polyhedron). Alternatively, it allows to set up customized situations that may not be in accordance with a
dynamics simulation (e.g. manually prescribe radial pointing towards the asteroid)

The 3D situation is shown in these first two plots. In the first one, the attitude control has not converged
and the camera is not pointing towards the asteroid. Consequently, no landmarks are visible. In the second
one, the attitude control has aligned the camera focal direction (in blue) with the negative radial direction,
thus some landmarks are visible (in blue) on the surface. The third figure shows the visible landmark pixels
(with their labels) in the image plane.

.. image:: /_images/Scenarios/scenarioSmallBodyLandmarks1.svg
   :align: center

.. image:: /_images/Scenarios/scenarioSmallBodyLandmarks2.svg
   :align: center

.. image:: /_images/Scenarios/scenarioSmallBodyLandmarks3.svg
   :align: center

The next two plots show time evolution of the number of visible landmarks and the alignment of the camera
frame with respect to the Hill frame. Note that :math:`\hat c_3` is aligned with the negative radial direction
of the Hill frame as expected.

.. image:: /_images/Scenarios/scenarioSmallBodyLandmarks4.svg
   :align: center

.. image:: /_images/Scenarios/scenarioSmallBodyLandmarks5.svg
   :align: center

"""

#
# Basilisk Scenario Script and Integrated Test
#
# Purpose:  Integrated test of the pinholeCamera() module. Illustrates how
#           the pinhole camera module can be integrated with spacecraft,
#           planet and attitude control modules.
# Author:   Julio C. Sanchez
# Creation Date:  Jun. 26, 2023
#

import os
import matplotlib.pyplot as plt
import numpy as np

# The path to the location of Basilisk
# Used to get the location of supporting data.
from Basilisk import __path__

# import message declarations
from Basilisk.architecture import messaging

# import FSW Algorithm related support
from Basilisk.fswAlgorithms import attTrackingError
from Basilisk.fswAlgorithms import hillPoint
from Basilisk.fswAlgorithms import mrpFeedback

# import simulation related support
from Basilisk.simulation import ephemerisConverter
from Basilisk.simulation import extForceTorque
from Basilisk.simulation import pinholeCamera
from Basilisk.simulation import planetEphemeris
from Basilisk.simulation import simpleNav
from Basilisk.simulation import spacecraft
from Basilisk.simulation.gravityEffector import loadPolyFromFileToList

# import general simulation support files
from Basilisk.utilities import SimulationBaseClass
from Basilisk.utilities import macros
from Basilisk.utilities import orbitalMotion
from Basilisk.utilities import simIncludeGravBody
from Basilisk.utilities import RigidBodyKinematics
from Basilisk.utilities import unitTestSupport  # general support file with common unit test functions

# attempt to import vizard
from Basilisk.utilities import vizSupport

bskPath = __path__[0]
fileName = os.path.basename(os.path.splitext(__file__)[0])


# Landmark distribution function
[docs]def landmark_distribution(vert_list, face_list, n_vert, n_face, n_lmk): """Creates a landmark distribution based on a polyhedron shape.""" pos_vert = np.array(vert_list) order_face = np.array(face_list) - 1 pos_face = np.zeros((n_face,3)) normal_face = np.zeros((n_face,3)) for i in range(n_face): pos0 = pos_vert[order_face[i, 0], 0:3] pos1 = pos_vert[order_face[i, 1], 0:3] pos2 = pos_vert[order_face[i, 2], 0:3] pos_face[i, 0:3] = (pos0 + pos1 + pos2) / 3 normal_face[i, 0:3] = np.cross(pos1-pos0, pos2-pos0) normal_face[i, 0:3] /= np.linalg.norm(normal_face[i, 0:3]) np.random.seed(0) idx_lmk = np.random.choice(n_face, n_lmk, replace=False) idx_lmk.sort() pos_lmk = pos_face[idx_lmk, 0:3] normal_lmk = normal_face[idx_lmk, 0:3] return pos_lmk, normal_lmk
[docs]def plot_3D(t, r, xyz_vert, order_face, posLmk, isvisibleLmk, dcm_CP): """Plot the 3D situation of asteroid, landmarks, spacecraft and camera frame.""" idxOn = isvisibleLmk == 1 idxOff = isvisibleLmk != 1 color_asteroid = [105 / 255, 105 / 255, 105 / 255] fig = plt.figure() ax = fig.add_subplot(1, 1, 1, projection='3d') ax.plot(r[0] / 1000, r[1] / 1000, r[2] / 1000, 'k', marker='s', markersize=5) ax.plot_trisurf(xyz_vert[:, 0] / 1000, xyz_vert[:, 1] / 1000, xyz_vert[:, 2] / 1000, triangles=order_face-1, color=color_asteroid, zorder=0, alpha=0.1) ax.plot(posLmk[idxOn, 0] / 1000, posLmk[idxOn, 1] / 1000, posLmk[idxOn, 2] / 1000, 'b', linestyle='', marker='.', markersize=5) ax.plot(posLmk[idxOff, 0] / 1000, posLmk[idxOff, 1] / 1000, posLmk[idxOff, 2] / 1000, 'r', linestyle='', marker='.', markersize=5, alpha=0.25) ax.quiver(r[0] / 1000, r[1] / 1000, r[2] / 1000, dcm_CP[0,0], dcm_CP[1,0], dcm_CP[2,0], length=10, normalize=True, color='black', alpha=0.25) ax.quiver(r[0] / 1000, r[1] / 1000, r[2] / 1000, dcm_CP[0,1], dcm_CP[1,1], dcm_CP[2,1], length=10, normalize=True, color='black', alpha=0.25) ax.quiver(r[0] / 1000, r[1] / 1000, r[2] / 1000, dcm_CP[0,2], dcm_CP[1,2], dcm_CP[2,2], length=10, normalize=True, color='blue') ax.set_xlabel('${}^{P}r_{x}$ [km]') ax.set_ylabel('${}^{P}r_{y}$ [km]') ax.set_zlabel('${}^{P}r_{z}$ [km]') t_str = str(int(t/60)) ax.set_title('Asteroid rotating frame, t=' + t_str + ' min')
[docs]def plot_pixel(pixelLmk, statusLmk): """Plot landmarks labeled pixels in the camera image.""" idxOn = statusLmk == 1 pixelOn = pixelLmk[idxOn, :] fig = plt.figure() ax = fig.gca() plt.plot(pixelOn[:,0], pixelOn[:,1], linestyle='', marker='s', color='b', markersize=5) for i in range(len(pixelOn)): ax.annotate(str(i), (pixelOn[i,0], pixelOn[i,1])) ax.set_xlim([-1024, 1024]) ax.set_ylim([-768, 768]) plt.xlabel('$p_x$ [-]') plt.ylabel('$p_y$ [-]')
[docs]def plot_nLmk(t, nvisibleLmk): """Plot visible landmarks evolution.""" fig = plt.figure() ax = fig.gca() plt.plot(t/3600, nvisibleLmk, linestyle='--', marker='.', markersize=8) ax.set_xlim([t[0]/3600, t[-1]/3600]) plt.xlabel('Time [h]') plt.ylabel('Visible landmarks [-]')
[docs]def plot_orientation(t, dcm_HP, dcm_CP): """Plot the camera frame orientation with respect to Hill frame.""" data = np.zeros((len(t), 3)) for i in range(len(t)): data[i,0:3] = [np.dot(dcm_HP[i, 0:3, 0], dcm_CP[i, 0:3, 2]), np.dot(dcm_HP[i, 0:3, 1], dcm_CP[i, 0:3, 0]), np.dot(dcm_HP[i, 0:3, 2], dcm_CP[i, 0:3, 1])] fig = plt.figure() ax = fig.gca() labelStrings = (r'$\hat\imath_r\cdot \hat c_3$' , r'${\hat\imath}_{\theta}\cdot \hat c_1$' , r'$\hat\imath_h\cdot \hat c_2$') for idx in range(3): plt.plot(t/3600, data[:, idx], color=unitTestSupport.getLineColor(idx, 3), label=labelStrings[idx]) ax.set_xlim([t[0]/3600, t[-1]/3600]) plt.legend(loc='lower right') plt.xlabel('Time [h]') plt.ylabel('Orientation Illustration')
[docs]def run(show_plots, useBatch): """ The scenarios can be run with the followings setups parameters: Args: show_plots (bool): Determines if the script should display plots useBatch (bool): Specify if the pinhole camera model batch process is to be checked. """ # Create a sim module as an empty container scSim = SimulationBaseClass.SimBaseClass() scSim.SetProgressBar(True) # Create simulation variable names simTaskName = "simTask" simProcessName = "simProcess" # Define the simulation duration simulationTime = macros.min2nano(100.) # Create the process dynProcess = scSim.CreateNewProcess(simProcessName) # Add the dynamics task and specify the integration update time simulationTimeStep = macros.sec2nano(0.1) dynProcess.addTask(scSim.CreateNewTask(simTaskName, simulationTimeStep)) # # Add modules to the task # # Setup celestial object ephemeris module gravBodyEphem = planetEphemeris.PlanetEphemeris() gravBodyEphem.ModelTag = 'erosEphemeris' gravBodyEphem.setPlanetNames(planetEphemeris.StringVector(["eros"])) # Specify asteroid orbit elements and rotational state January 21st, 2022 # https://ssd.jpl.nasa.gov/horizons.cgi#results oeAsteroid = planetEphemeris.ClassicElementsMsgPayload() oeAsteroid.a = 1.4583 * 149597870.7*1e3 # meters oeAsteroid.e = 0.2227 oeAsteroid.i = 10.829 * np.pi/180 oeAsteroid.Omega = 304.3 * np.pi/180 oeAsteroid.omega = 178.9 * np.pi/180 oeAsteroid.f = 246.9 * np.pi/180 AR = 11.369 * np.pi/180 dec = 17.227 * np.pi/180 lst0 = 0 * macros.D2R gravBodyEphem.planetElements = planetEphemeris.classicElementVector([oeAsteroid]) gravBodyEphem.rightAscension = planetEphemeris.DoubleVector([AR]) gravBodyEphem.declination = planetEphemeris.DoubleVector([dec]) gravBodyEphem.lst0 = planetEphemeris.DoubleVector([lst0]) gravBodyEphem.rotRate = planetEphemeris.DoubleVector([360 * macros.D2R / (5.27 * 3600.)]) dcm_PN = RigidBodyKinematics.euler3232C([AR, np.pi/2 - dec, lst0]) # Set up asteroid gravity effector (only Keplerian gravity for simplicity) # https://ssd.jpl.nasa.gov/tools/gravity.html#/vesta gravFactory = simIncludeGravBody.gravBodyFactory() mu = 4.4631 * 1e5 asteroid = gravFactory.createCustomGravObject("eros", mu=mu, radEquator=16*1000) asteroid.isCentralBody = True asteroid.planetBodyInMsg.subscribeTo(gravBodyEphem.planetOutMsgs[0]) # Create an ephemeris converter ephemConverter = ephemerisConverter.EphemerisConverter() ephemConverter.ModelTag = "ephemConverter" ephemConverter.addSpiceInputMsg(gravBodyEphem.planetOutMsgs[0]) # Initialize spacecraft object and set properties scObject = spacecraft.Spacecraft() scObject.ModelTag = "bsk-Sat" I = [900., 0., 0., 0., 800., 0., 0., 0., 600.] scObject.hub.mHub = 750.0 scObject.hub.r_BcB_B = [[0.0], [0.0], [0.0]] scObject.hub.IHubPntBc_B = unitTestSupport.np2EigenMatrix3d(I) # Set spacecraft initial condition oe = orbitalMotion.ClassicElements() oe.a = 34 * 1e3 oe.e = 0.001 oe.i = 45 * macros.D2R oe.Omega = 48.2 * macros.D2R oe.omega = 347.8 * macros.D2R oe.f = 85.3 * macros.D2R rP, vP = orbitalMotion.elem2rv(mu, oe) rN = dcm_PN.transpose().dot(rP) vN = dcm_PN.transpose().dot(vP) scObject.hub.r_CN_NInit = rN scObject.hub.v_CN_NInit = vN scObject.hub.sigma_BNInit = [[0.7], [0.2], [-0.3]] scObject.hub.omega_BN_BInit = [[0.001], [-0.01], [0.03]] # Attach gravity to spacecraft gravFactory.addBodiesTo(scObject) # Set extForceTorque module # The control torque is read in through the messaging system extFTObject = extForceTorque.ExtForceTorque() extFTObject.ModelTag = "externalDisturbance" scObject.addDynamicEffector(extFTObject) # Add the simple Navigation sensor module. This sets the SC attitude, rate, position # velocity navigation message sNavObject = simpleNav.SimpleNav() sNavObject.ModelTag = "SimpleNavigation" sNavObject.scStateInMsg.subscribeTo(scObject.scStateOutMsg) # Set hillPoint guidance module attGuidance = hillPoint.hillPoint() attGuidance.ModelTag = "hillPoint" attGuidance.transNavInMsg.subscribeTo(sNavObject.transOutMsg) attGuidance.celBodyInMsg.subscribeTo(ephemConverter.ephemOutMsgs[0]) # Set the attitude tracking error evaluation module attError = attTrackingError.attTrackingError() attError.ModelTag = "attErrorInertial3D" attError.sigma_R0R = [0, 1, 0] attError.attRefInMsg.subscribeTo(attGuidance.attRefOutMsg) attError.attNavInMsg.subscribeTo(sNavObject.attOutMsg) # Set the MRP Feedback control module mrpControl = mrpFeedback.mrpFeedback() mrpControl.ModelTag = "mrpFeedback" mrpControl.guidInMsg.subscribeTo(attError.attGuidOutMsg) mrpControl.K = 3.5 mrpControl.Ki = -1.0 # make value negative to turn off integral feedback mrpControl.P = 30.0 mrpControl.integralLimit = 2. / mrpControl.Ki * 0.1 # Connect torque command to external torque effector extFTObject.cmdTorqueInMsg.subscribeTo(mrpControl.cmdTorqueOutMsg) # Prepare 100 landmarks distribution polyFile = bskPath + '/supportData/LocalGravData/eros007790.tab' vert_list, face_list, n_vert, n_face = loadPolyFromFileToList(polyFile) n_lmk = 100 pos_lmk, normal_lmk = landmark_distribution(vert_list, face_list, n_vert, n_face, n_lmk) # Set the pinhole camera module camera = pinholeCamera.PinholeCamera() camera.f = 25*1e-3 camera.nxPixel = 2048 camera.nyPixel = 1536 camera.wPixel = (17.3*1e-3) / 2048 dcm_CB = np.array([[0, 0, -1], [0, 1, 0], [1, 0, 0]]) camera.dcm_CB = dcm_CB.tolist() for i in range(n_lmk): camera.addLandmark(pos_lmk[i, 0:3], normal_lmk[i, 0:3]) camera.ephemerisInMsg.subscribeTo(ephemConverter.ephemOutMsgs[0]) camera.scStateInMsg.subscribeTo(scObject.scStateOutMsg) # Add models to task scSim.AddModelToTask(simTaskName, gravBodyEphem, ModelPriority=100) scSim.AddModelToTask(simTaskName, ephemConverter, ModelPriority=99) scSim.AddModelToTask(simTaskName, scObject, ModelPriority=98) scSim.AddModelToTask(simTaskName, extFTObject, ModelPriority=97) scSim.AddModelToTask(simTaskName, sNavObject, ModelPriority=96) scSim.AddModelToTask(simTaskName, attGuidance, ModelPriority=95) scSim.AddModelToTask(simTaskName, attError, ModelPriority=94) scSim.AddModelToTask(simTaskName, mrpControl, ModelPriority=93) scSim.AddModelToTask(simTaskName, camera, ModelPriority=92) # Add data logging to task samplingTime = macros.sec2nano(60) asteroidLog = ephemConverter.ephemOutMsgs[0].recorder(samplingTime) spacecraftLog = scObject.scStateOutMsg.recorder(samplingTime) scSim.AddModelToTask(simTaskName, asteroidLog) scSim.AddModelToTask(simTaskName, spacecraftLog) landmarkLog = [] for i in range(n_lmk): landmarkLog.append(camera.landmarkOutMsgs[i].recorder(samplingTime)) scSim.AddModelToTask(simTaskName, landmarkLog[i]) # Create the FSW vehicle configuration message vehicleConfigOut = messaging.VehicleConfigMsgPayload() vehicleConfigOut.ISCPntB_B = I # use the same inertia in the FSW algorithm as in the simulation configDataMsg = messaging.VehicleConfigMsg().write(vehicleConfigOut) mrpControl.vehConfigInMsg.subscribeTo(configDataMsg) # Initialize Simulation scSim.InitializeSimulation() # Configure a simulation stop time and execute the simulation run scSim.ConfigureStopTime(simulationTime) scSim.ExecuteSimulation() # Retrieve the logged data as numpy arrays t = np.array(spacecraftLog.times() * macros.NANO2SEC) r_PN_N = np.array(asteroidLog.r_BdyZero_N) v_PN_N = np.array(asteroidLog.v_BdyZero_N) mrp_PN = np.array(asteroidLog.sigma_BN) r_BN_N = np.array(spacecraftLog.r_BN_N) v_BN_N = np.array(spacecraftLog.v_BN_N) mrp_BN = np.array(spacecraftLog.sigma_BN) pixelLmk = np.zeros((len(t), n_lmk, 2)) isvisibleLmk = np.zeros((len(t), n_lmk)) for i in range(n_lmk): pixelLmk[:, i, 0:2] = landmarkLog[i].pL isvisibleLmk[:, i] = landmarkLog[i].isVisible nvisibleLmk = np.sum(isvisibleLmk, axis=1) # Compute spacecraft position and velocity w.r.t. asteroid expressed in inertial frame r_BP_N = r_BN_N - r_PN_N v_BP_N = v_BN_N - v_PN_N # Compute variables of interest n = len(t) r_BP_P = np.zeros((n, 3)) v_BP_P = np.zeros((n, 3)) mrp_BP = np.zeros((n, 3)) r_PN_P = np.zeros((n, 3)) dcm_PC = np.zeros((n, 3, 3)) dcm_PH = np.zeros((n, 3, 3)) for i in range(n): # Compute auxiliary dcm dcm_PN = RigidBodyKinematics.MRP2C(mrp_PN[i][0:3]) dcm_BN = RigidBodyKinematics.MRP2C(mrp_BN[i][0:3]) dcm_BP = np.matmul(dcm_BN, dcm_PN.T) # Fill variables of interest r_BP_P[i, 0:3] = dcm_PN.dot(r_BP_N[i, 0:3]) v_BP_P[i, 0:3] = dcm_PN.dot(v_BP_N[i, 0:3]) mrp_BP[i, 0:3] = RigidBodyKinematics.C2MRP(dcm_BP) dcm_PC[i, 0:3, 0:3] = np.matmul(dcm_CB, dcm_BP).T r_PN_P[i, 0:3] = dcm_PN.dot(r_PN_N[i, 0:3]) # Fill dcm between planet and Hill frame ir = r_BP_P[i, 0:3] / np.linalg.norm(r_BP_P[i, 0:3]) hv = np.cross(r_BP_P[i, 0:3], v_BP_P[i, 0:3]) ih = hv / np.linalg.norm(hv) itheta = np.cross(ih, ir) dcm_PH[i, 0:3, 0] = ir dcm_PH[i, 0:3, 1] = ih dcm_PH[i, 0:3, 2] = itheta # Extract conditions at t0 and tf t0 = t[0] r0 = r_BP_P[0, 0:3] dcm0 = dcm_PC[0, 0:3, 0:3] isvisibleLmk0 = isvisibleLmk[0, :] tf = t[-1] rf = r_BP_P[-1, 0:3] dcmf = dcm_PC[-1, 0:3, 0:3] isvisibleLmkf = isvisibleLmk[-1, :] # Showcase a batch computation # This is useful because oftentimes dynamics simulation is computationally expensive # Thus, experimenting with camera parameters can be detached from dynamics if useBatch: # Preallocate output pixels pixelBatchLmk = np.zeros((n, n_lmk, 2)) # Process pinhole camera as a batch camera.processBatch(r_BP_P, mrp_BP, -r_PN_P / np.linalg.norm(r_PN_P, axis=1)[:, None], False) isvisibleBatchLmk = np.array(camera.isvisibleBatchLmk) nvisibleBatchLmk = np.sum(isvisibleBatchLmk, axis=1) pixelBatchLmk[:, :, 0] = np.array(camera.pixelBatchLmk)[:, 0:n_lmk] pixelBatchLmk[:, :, 1] = np.array(camera.pixelBatchLmk)[:, n_lmk:2*n_lmk] # Ensure that results are equal as BSK sim batch_diff = np.array([np.linalg.norm(nvisibleBatchLmk - nvisibleLmk) / np.linalg.norm(nvisibleLmk), np.linalg.norm(isvisibleBatchLmk - isvisibleLmk) / np.linalg.norm(isvisibleLmk), np.linalg.norm(pixelBatchLmk - pixelLmk) / np.linalg.norm(pixelLmk)]) else: batch_diff = 0 # Plot results of interest figureList = {} plot_3D(t0, r0, np.array(vert_list), np.array(face_list), pos_lmk, isvisibleLmk0, dcm0) pltName = fileName + "1" figureList[pltName] = plt.figure(1) plot_3D(tf, rf, np.array(vert_list), np.array(face_list), pos_lmk, isvisibleLmkf, dcmf) pltName = fileName + "2" figureList[pltName] = plt.figure(2) plot_pixel(pixelLmk[-1, : , :], isvisibleLmk[-1, :]) pltName = fileName + "3" figureList[pltName] = plt.figure(3) plot_nLmk(t, nvisibleLmk) pltName = fileName + "4" figureList[pltName] = plt.figure(4) plot_orientation(t, dcm_PH, dcm_PC) pltName = fileName + "5" figureList[pltName] = plt.figure(5) if show_plots: plt.show() # Close the plots being saved off to avoid over-writing old and new figures plt.close("all") return batch_diff, figureList
# # This statement below ensures that the unit test scrip can be run as a # stand-along python script # if __name__ == "__main__": run( True, # show_plots False # useBatch )