Está en la página 1de 95

Re-Morph

Facial Warping and Morphing Software

3rd Year Group Project

Final Report
January 2006

Project Supervisor
Dr Daniel Rueckert

Group Members
Anish Mittal Jonathan Enav
Chris Roebuck Dharmesh Malam
Rikin Shah Ravi Madlani
Contents
1. Abstract 3

1.1. Introduction 3
1.2. Team Members 3
1.3. Project Supervisor 3

2. Background 4

2.1. Image Metamorphosis 4

3. Existing Solutions 6

3.1. Common Functionality 6

4. Specification Introduction 7

4.1. Minimum Specification 7


4.2. Extended Specification 8
4.3. Optimal Specification 8
4.4. Choice of Programming Language 8

5. GUI 9

5.1. Introduction 9
5.2. Analysis 9
5.3. Implementation 9
5.3.1. Class Listing 16
5.3.2. Screen Shots of GUI 18
5.3.3. Feature List 30
5.4. Testing 32
5.5. Evaluation 35

6. Core 38

6.1. Introduction 38
6.2. Analysis 38
6.3. Design 38
6.4. Implementation 43
6.4.1. Class Listing (Core) 47
6.4.2. Class Listing (Morph) 48
6.5. Evaluation 49

7. Core Algorithms 50

7.1. Introduction 50
7.2. Field Morphing (Beier-Neely Algorithm) 53
7.2.1. Background 53
7.2.2. Analysis 56

1
7.2.3. Design and Implementation 56
7.2.4. Testing 59
7.2.5. Evaluation 63
7.3. Mesh Warping 64
7.3.1. Background and Analysis 65
7.3.2. Design 66
7.3.3. Implementation 68
7.3.4. Testing 72
7.3.5. Evaluation 74

8. Performance Analysis 75

9. Project Summary 83

9.1. Dividing the Project 83


9.2. Continuous Integration 83
9.3. Module Integration 84

10. Bibliography 88

Appendixes 89

A. Organisational tools and Methods 89


B. Meetings and Pair-Programming Logbooks 91

2
1. Abstract

Facial warping and morphing


Warping is the process of changing the spatial configuration of one image to match
that of another whilst maintaining its coloring. Control points are used to mark out the
spatial landmarks, which a warping algorithm uses as a reference when manipulating
the image pixels. Morphing is a technique which combines warping with cross-
dissolution to create a smooth transition from one image to another. In this document
we outline our approach to solving the problem of implementing a tool to apply these
principles in morphing faces.

1.1 Introduction
The goal of this project was to create a program that would perform facial morphing.
Our implementation has focused on providing a tool which could be used as a
teaching aid in learning the different morphing techniques. We have chosen to
implement two different algorithms, one based on Field Morphing and the other on
the Mesh technique. These were chosen because of their different approach to
morphing, one using lines and the other points, as well as their contrasting
performance and quality levels. In terms of the software engineering aspect, we have
focused on making the application fully extensible, providing a plug-in architecture
whereby it can easily be integrated with third party algorithms.

1.2 Team Members

Jonathan Enav je203


Anish Mittal (Leader) akm103
Ravi Madlani rdm03
Dharmesh Malam (Secretary) dm203
Christopher Roebuck cjr03
Rikin Shah rs303

1.3 Project Supervisor

Dr Daniel Rueckert dr@doc.ic.ac.uk

3
2. Background
Re-Morph is a program which allows users to morph between multiple images using
various algorithms and get a visual representation of the processes required to do so.
In this section we will give a brief account of facial ‘warping’ and ‘morphing’ which
will aid the reader in understanding the core of our project.

2.1 Image Metamorphosis


Metamorphosis is the fluid transformation from one image to another though facial
‘warping’ and ‘morphing’. In this project when we mention ‘morphing’ we will be
referring to what we defined as ‘metamorphosis’.

Cross Dissolve

Before warping the most common method of a transition from one image to another
was through cross-dissolve. Cross dissolve works by interpolating the colour of each
pixel over time from that of the source image to the destination image. The weight of
the start and end pixel depends on the transition period. An example of a cross
dissolve is given below.

Figure [N] Example of Cross Dissolve [vc98]

Warping

Warping an image causes it to change its spatial configuration whilst maintaining its
colouring. In our project we have implemented two types of warping: Field Warping
and Mesh Warping. Field Warping uses corresponding lines in both the source and
destination images to generate the warp. Mesh Warping uses corresponding points in
the images. Both types of warping will be discussed in further detail in the
Algorithms section. An example of a warp is shown below

4
Morphing

Morphing is the process of one image fading into another. It does not necessarily use
warping to achieve this but doing so yields much more realistic effects. Below is an
example of a morph sequence turning a human into a cat.

5
3. Existing Solutions
A great number of morphing solutions are currently available. In this section we
detail the features common to all of. Our solution aims to solve the weaknesses of
existing solutions and extend the existing feature set.

3.1 Common Functionality


All the solutions currently available require the user to follow more or less the same
process for creating a morphing sequence.

Process

Select Start
Image

Select End
Image

Draw
corresponding
control points on
each picture

Generating
Morphing
Sequence

Control Points:

Control Points are used to mark corresponding points on the two images e.g. match
the ears on the source image to the ears on the destination image. Control Points are
either points or lines depending on the warping algorithm being used.
Our Solution

The unique point to our solution is the breakdown of a morph which is available to the
user. The user can see all the images which were used to generate the morphing
sequence. We have also developed a plug-in architecture which will allow the
development of more algorithms. The full feature-list is available in the Feature List
section.

6
4. Specification Introduction
The following section describes the key deliverables necessary for the application to
satisfy our brief.

4.1 Minimum Specification


In order for the application to be complete in its functionality, it must as a minimum
conform to the specification detailed below.

The application will be split into three tangible sections, the GUI, a Core and the
Algorithms. This approach should enable us to split the work most effectively, and as
each section is independent should aid debugging and future extendibility of the
application. The key features of each section are described below.

GUI

The GUI is the interface between the user and the backend processes, and hence
should be both interactive and intuitive in use. It will incorporate the following
features as a means to achieve this goal:

 A wizard to guide the user through the warping-morphing process


 Loading of images either by browsing user-files or through drag-drop support
 Selection of control points for the morph/warp, automatically providing the right
‘control point tool’ depending on the morphing algorithm selected
 Automatic resizing of images
 Provide an interface to set user-preferences and parameters
 Integration of a movie player to show the generated morph/warp clips
 Provision of a ‘slider’ control to enable viewing at the intermediated stages of the
metamorphosis

Core

The Core is the interface between the user-oriented front-end and the backend
algorithms. It will control the following features:

 Provide a standard interface to enable communication between the front and back
ends, maximizing flexibility e.g. the plug-in can specify the number of parameters
required by the algorithm to the GUI dynamically through the core
 File management services to create/edit/save user preferences and files to a
‘project’ which can be reloaded
 Provision of a plug-in architecture for loading algorithms
 Generation of a movie from the series of images returned by the algorithms

Algorithms

 The algorithmic component of the application is where the warping/morphing of


the images actually takes place. The specification is as follows:

7
 A set of code libraries which can be ‘plugged’ into the core to perform the desired
functions
 The algorithms will be as self-contained as possible, with common functions
being grouped into shared code libraries to minimize duplication
 Morphing will be provided through two different warping algorithms to show
their relative effectiveness
 A cross-dissolving algorithm

4.2 Extended Specification


To increase the marketability of the product the following features would be suitable
additions, and should be achievable in the time available.

 Allow multiple face metamorphosis


 A teaching mode
 Output the stages of the metamorphosis individually to describe the intermediate
stages of the process
 Integration with a web-cam to enable instant image capture and metamorphosis

4.3 Optional Specification


These features would enhance the overall usability of the system, but are by no means
necessary and may not be achievable in the allotted project lifecycle

 Real-time warping of an image


 Automatic detection of facial features and auto-cropping of the face
 Enhance application usability through features such as a context sensitive help
 A more intuitive ‘control point tool’ – such as a lasso to increase the accuracy of
the metamorphosis
 Implement more algorithms to illustrate their effectiveness when used as teaching
tool

4.4 Choice of Programming Language


For our project we decided to develop using c#. This was after great consideration of
implementing in Java. The primary reason for using c# is the GUI intensive nature of
this project. C# has a very rich API full of features which are very useful for our
project including image manipulation and graphics features.
As a group we decided we needed a rich language with great GUI tools and an IDE to
help us with rapid development.

8
5. The GUI
5.1 Introduction
In this section we describe the full development cycle of the GUI (Graphical User
Interface). The GUI is the side of the program which the User interacts with in order
to use the features and perform tasks.

In order to measure the usability of the application we shall use Nielsen’s 10 Usability
Heuristics. These are:

1) Visibility of System Status


2) Match between System and Real World
3) User Control and Freedom
4) Consistency and Standards
5) Error Prevention
6) Recognition rather than Recall
7) Flexibility and efficiency of use
8) Aesthetic and minimalist design
9) Help users recognise, diagnose and recover from errors.
10) Help and Documentation

If we find that our GUI satisfies all of Nielsen’s 10 Usability Heuristics then we have
sufficient evidence that a usable interface has been developed.

5.2 Analysis
The GUI should provide an easy to use, intuitive interface for the user. From the
specifications it was agreed that the GUI should provide the following functionality:

 Load multiple images into application for morphing. This should be possible
through Drag-and-Drop and also a File Chooser dialogue box.
 Manipulate images to enable morphing to work
 Add, Select and Manipulate Control Points on all images in a Morph Sequence.
 Provide a wizard which walks the user through a morphing project.
 Display all intermediary frames required to generate the morph for use in a
‘Teaching Mode’.
 Provide a Movie Player to view the animated morph sequence.

5.3 Design
Main Form Design

9
Re-Morph

Menu Bar

Control
Toolbar

Image 1 Image 2 Image ..

Status Bar
Result Form Design

Re-Morph

Menu Bar
Forward

Control
Toolbar

Cross-Fade

Backward

Status Bar

10
Movie Player Design

Re-Morph

Movie
Display box

Slider
Control

Image Resize form

Re-Morph

Image 1 Image 2

Above is the design of the 4 main forms. These forms contain all of the functionality
that is required by the specification. Below is a breakdown of the forms and their use.

The main form

11
This form is the first form that the user will be presented with. It will allow the user to
select a morph from a menu item that will be dynamically created based on the plug-
ins that are available (a full listing of the menu items can be seen below).
Once the user has selected a type of morph, based on the plug-in that was chosen, a
picture box will then appear on the form. At this point the user has two options for
loading and image, firstly they can simply drag and drop an image on to the picture
box, or secondly they can click on a add image button and a file browser will appear
allowing the user to navigate to the images location.
Now that a start image has been loaded the user can add more images depending on
the number of faces they wish to warp between. We will also allow the user to choose
the number of frames they will like to have between each two adjacent images for that
part of the warp.
The user now has to place the anchor markers on the faces to facilitate the morph.
These will be different depending on the plug-in chosen. We propose to aloe the user
to add anchors in many different ways, such as right clicking the image, holding down
shift and clicking and clicking a button.
Once a user has placed all of the anchors in the correct place they are now ready to
proceed with the morph.
By clicking the morph button we may decide on showing the user a preview of the
morph occurring in real time, but this depends on how fast the morphs happens.
Once the morph has finished the next form will pop up.

Result Form

This form will display the information required for the teaching mode as described in
our specification. Firstly the forward warp will be displayed. This means that the
deformation of the leftmost image through all of the points of the other images to the
last will be shown. Secondly the reverse of this will be shown, and finally the
combination of the two images at each index will be shown, this image will be the
output of the warp.
This Form will allow the user to overlay the points on the morph for each image to
see how the morph has progressed, and will also allow the user to proceed to the next
form to view the video.

Movie Player

This form will allow the user to play a video representation of the warp. It should also
allow the user to save the image to disk and encode it with any codec that is installed
on the host pc.

Image Resize

This form was a late addition due to us noticing that morphs between images of
differing sizes was not desirable. We decided to prompt the user to the fact that two

12
images are different when the second is imported into the program. The image Resize
form will open and allow the user to choose to resize an image to match the other
either by stretching, adding blank space to the image to keep the aspect ratio, or
cropping.

Menu System

During the development of the application the menu structure changed, the final
structure is described below.

File
New
Beer Neely
Mesh
Open
Save
Save As
Close
Exit

Edit
Undo
Redo
Parameters

Anchors
Add
Remove
Select All
Clear All
Colour –
Style –
Size –
View Anchors

Images
Zoom --
Reset Size
Increase
Decrease
Auto Horizontal
Auto Vertical
Help
About
Manual

Most of these items are self explanatory and intuitive for a user of Microsoft products
to use.

13
File
New
Beer Neely
Mesh

The above is created dynamically based on the plug-ins that are available.

5.4 Implementation
The GUI was implemented using C# form design mode in Visual Studio. We made
use of both forms and windows GUI controls in our implementation to allow the reuse
of code and flexibility of the design.

Below is a diagrammatic representation of the structure of the program.

14
To the Core

GuiController MainForm
clsResult

Result

VideoForm

ImageEditing
CtlImageLine CtlImageStrip

CtlImagePoint

PictureBox

CtlAnchorPoint
Image

ctlLine

Web Cam

One of our requirements was to allow the user to add an image by the use of image
capture through a web cam. Most of the code to do this is integrated into direct show.
Direct show acts as a wrapper for some c++ code that is written to handle web cam
usage. We have a form with a picture box on it and we allow the user to select an
option to start the capture of an image. This image is then overlaid onto the screen on
top of the picture box. The user is then given 5 seconds before an image is taken, this
image is then displayed in the picturebox and so the user can use it as part of the
morph.

15
5.4.1 Class listing
Below is a list of the classes and a brief description of their functionality.

GuiController

As described in the section ‘Our Solution’ the interfaces between the model and view
act as the controls, and this is such a class. This class acts as the interface between the
GUI and the Core and as such passes information around to allow the morph to
happen. Any change to the underlying model made from the GUI will pass through
this class to the core. For this reason we have made this class a singleton.

Actions such as opening and saving images and videos and loading saved projects are
conducted by this class. If the user wishes to open an existing project this class will
create the model and pass that to ctlImageStrip which will create the view using a
static method to decide which type of morph, i.e. a line or point morph the model
represents.

GUI controller is responsible for serialisation and more about that can be read in the
core section under the Model.

MainForm

This form contains all of the menus that get displayed, and the code to call the correct
code elsewhere to execute the desired action. This form makes extensive use of the
GuiController.

CtlImageStrip

This class is the parent class of both CtlImageLine and CtlImagePoint, and as such
contains all of the common functionality. It allows the user to drag and drop an image
into a picture box, add, select and delete multiple anchors (the points on the images to
facilitate the morph) and do any form of image editing that will be required, all of the
features will be contained in a feature list.

This class is also where changes to the model are initiated, the way that we decided to
solve the problem of updating both the model and the GUI is to have redraw functions
that allow the users to update the view, then update the model and then clear the view
and redraw the view. We took this approach for correctness and it does not lead to a
drop off in performance.

This class also contains the context menus which are displayed when the user right
clicks an area on the control.

ctlResult

This class contains the result of an executed morph. This means that it has to display 3
sets of images. To achieve this we made use of CtlImageStrip and so this form
contains 3 of these, one for the forward morph, one for the reverse morph and then

16
one for the morph with the cross dissolve between the two other sets of images. This
control sits in result.cs.

Result

This form contains the ctlResult , and allows the user to manipulate what they see and
to zoom in and out.

ctlImagePoints

This child GUI control sits on the MainForm form and contains a picture box for each
image that will form the morph. This class contains all of the specific code to points
i.e. the code to delete multiple points.

ctlImageLines

This child GUI control sits on the MainForm form and contains a picture box for each
image that will form the morph. This class contains all of the specific code to lines i.e.
the code to delete multiple points.

The way that the view links into the model is that the view has a picture box with a
list of anchors. These anchors are grouped into families, i.e. when an anchor is added
to a picture one is added to all of the other anchors, these anchors are then part of the
same family. This family number is then the index in the collection in the arraylist in
the model.

ctlAnchorPoint

This class represents a anchor point, which is one of the anchor types that the user can
use, which depends on the algorithm that the user chooses to use in the morph. An
anchor knows which picture that it is on, what family it belongs to and has a reference
to the strip that it is on. All of this information is required due to the nature of deleting
an anchor and the fact that it is required that the corresponding anchor is deleted from
all other images, leaving each image with the same number of anchor points.

The anchors markers are just one character using a system font available on all
windows machines (windings). This is a vector font contain things such as crosshairs,
triangles and diamonds, and these scale to any size easily. This class contains the
information about the font and colour of the anchor; this is required as we allow the
user to change the appearance of the anchors. There are 2 colouring algorithms that
colour the anchors corresponding to their families making them distinguishable.

clsViewLine

For lines the situation is more complex. The approach we decided on was to create to
ctlAnchorPoints and then to instantiate a class clsViewLine which represents the line.
With this representation we use GDI to draw the line. This class is also aware of its
location as the ctlAnchorPoint is.

AboutForm

17
This has some information about the program.

CustomMenuItem

This class is used for anchor settings and for giving the user a dynamic list of the
plug-ins.

GetParams

This class is required to hold the information about the parameters that the algorithm
can take as input so that the user can change them as desired.

globalGUIConsts

This class contains the values of variables such maximum sizes.

ImageEditing

This class contains the code to allow the user to edit the sizes of images. This is
required as images that are different sizes can not be used in a morph.

VideoForm

This is the form that plays the video that is created as a result of the morph. It contains
functionality to allow the user to play, pause and a slider to allow the user to view any
part of the video. The stream generation is conducted in the core, and so in that
section there is a section describing the library we used.

5.4.2 Screen shots of the GUI

In this section I will talk about the GUI design and some of the features avalible for
the user to use. Below is the icon that the user double clicks to run our application.

18
Above is the splash screen that first comes up once the user opens our application. We
designed this to grab the users attention.

Above is the wizard that guides the user through either opening a morph or creating a
new one. The list of the algorithms is created dynamically based on the implemented
plug-ins that are in the plug-ins folder.
User clicks open ->

19
If the user clicks open they will be presented with a file browser to locate any saved
projects. They can choose one and open it like the example below.

20
This has loaded up the images that the user has chosen previously, with all of the
anchors and preferences set from the point that the user clicked save. From this point
the user is free to morph.
MainForm
User clicks New Morph ->
Menus
Toolbar

CtlImagStrip

PictureBox

StatusBar

If the user clicks new morph they will be presented with this window to allow them to
import the images and add anchors. Below is the toolbar.

• The first icon allows the user to create a new morph. The list that hangs of this
icon is dynamically created as the list of the algorithms on the wizard was.

• Clicking on the second icon will open up the file browser seen before.

• The third icon allows the user to save their project to list. This is done through
XML serialisation which is explained later on.

• The third icon is the add anchor icon. This button will add an anchor to each
image that is in this window.

• The forth icon is the add image icon and it will add another picture box to the
form, they user may add as many images as they see fit.

• The next 4 icons are resizing icons which resize the appearance of the images
without changing the underlying image.

21
All of these icons have tool tips to help describe what they do, and at the bottom of
the form there is a status bar which keeps the user informed of any information we
deem necessary to know about where their mouse pointer is on the form.

Below are some menu items of interest for the anchors:-

What sets our application apart from many others is the extent to which IT will allow
the user to change the appearance of the program. This serves two purposes, firstly it
allows the user to set the sizes and font of the anchors to help them best see the
anchors and so place them in the optimal positions. This was the thing that was
lacking from many of the existing applications as anchors became lost when trying to
produce very detailed warps.

From this new project state the user can get to a point read to morph from either by
browsing to an image or by dragging and dropping an image on to the picture box.
The user can choose as many images as they desire. Below is an example of this state.

22
On this form we now have a new box which indicate the number of frames the user
wants to have output of the morph between the two images. The default is 5 as seen
above. The user would now click the morph button on the toolbar.

pictureBox

progressBar

button

The above form will then pop up. It displays the morph as it occurs in real-time and a
progress bar of the progress through the morph. It also displays the time elapsed and
time remaining for the morph which is extrapolated based on the time elapsed and
percentage completed. The video stream is displayed on the picturebox a call back
function from a worker thread passes back the percentage completed and the frame

23
outputted from the algorithm. More on threading later. After the morph finishes this
window disappears.

The user is then prompted with the video screen as seen below.

picturebox

TrackBar

This form allows the user to view a video stream of the morph that was just produced.
The video area is a picturebox and we use a library avi-previewer which draws on top
of it to display the video. Avi previewer is described in a later section.
The track bar is moved when the video is played from within avi-previewer, and
conversely the slider can be moved to change the frame that is in the picturebox.
The video can also be sped up, or slowed down by altering the frames/Sec box, the
code to do this is also within avi-previewer.
The two buttons on the bottom right of the form are the ones of interest. They are
from the top, export video and teaching mode.

User clicks export video ->

24
They are required to give a name and location.

We also allow the user to compress the video as they can become quite large in their
raw state. What this form does is it dynamically creates the list of all of the codecs
that are installed on the machine and allows the user to choose one and the rest is
handled by windows components.

User clicks teaching mode ->

25
ToolBar

picturebox

This form contains 3 image strips of the images that go into making a morph. This
allows the user to see all of the stages of the morph, ie the forward warp, backwards
warp and the cross fade. The toolbar is shown below.

The first four icons allow the user to resize the images.

The next 3 allows them to select which imagestrip or combinations of image strips
they can see.

The eighth icon allows the user to view the anchors on the image, this then shows the
user exactly how the warp has happened as seen below.

26
The last item is a printout of the time that the morph took. This was initially just used
in our performance analysis, but we decided that it would be good information for the
user to have. The time is generated in GuiController.

The way that this form works is that the algorithm passes out each frame and its
anchors after each iteration. This is then held in a clsResult object, which is in the
model. This object contains 3 clsTransforms, which are the model for the
ctlImageStrips. When this objects hits the GUI it looks like the above screenshot, and
hold all of the information to act like a teaching aid. All of these classes are described
in their respective sections; this should act as a brief description of what is happening
behind the scenes. The lines are just drawn on to the pictureboxes using GDI.

The last form to describe is the image editing form.

27
picturebox

picturebox

This form is loaded when the user imports an image that is not compatible with an
existing one. This will usually be due to the size of the image not being the same, and
so this form allows the user to preview the two images and see how altering the
different sizes affects the images so an informed choice of the size of all images can
be made.

Annotation

When clicking the add anchor button an anchor will appear in the top left corner of
each image. Dependent on the type of morph, this anchor could be a line or a dot.

The user can also position anchors with more accuracy by holding down shift as well
as clicking in the desired area where they want the line to appear.

The anchor should correspond to the same feature in both images, for example if there
is an anchor positioned over a mouth on the first image, then the corresponding
anchor should be positioned over the mouth on the second image and every other
image in the proposed morph.

28
To allow users to be able to figure out the corresponding anchors when the user
hovers their mouse over a particular anchor, each corresponding anchor will become
highlighted in every other image.

For increased accessibility and usability, each anchor can be modified in terms of its
size, shape and colour, all from the Anchors toolbar menu. This enables greater
accuracy when placing anchors.

Lines

default line anchor


When clicking the add anchor button from within the Bier Neely algorithm, a line
pops up on the screen as shown below:

The line can now be positioned on a facial landmark on each image, this is done by
dragging either end of the line to position it in the correct place.

It is important that the line points in the same direction in each image otherwise this
would lead to an undesirable morph, for this reason the line has a square end and a
circle end, so the user always can tell which way round the line is.

29
Dots

default dot anchor

When clicking the add anchor button from within the Mesh algorithm, a dot pops up
on the screen as shown below:

The dot can now be positioned on a facial landmark on each image. This is done by
selecting the dot and dragging the dot to the desired position on the image.

Please refer to the manual for further explanation of the GUI.

5.4.3 Feature List

30
As discussed in the section 'Our Solution', we have taken a user-centric approach and
as such we have a list of features that will facilitate the user conducting a morph.
Below is a list of features we intend to include in our final solution:

Images

User should easily be able to:

• Add – Images through dragging and dropping an image into an image


placeholder, through a file browser dialog and by clicking a button on the
toolbar.
• Remove - a specific Image from the proposed morph.
• Morph – the user can morph one image to another image, or can choose to do
a multi-morph with several images.
• Set Frame Rate – the user can set the amount of frames in a morph by
adjusting the number selector in-between images.
• Save – Separate images from before and after a morph.
• Swap Images– since images are displayed left to right in chronological order
of when they will be morphed, by swapping images in the GUI the user can
alter the order in which images are morphed to one another.
• Scale – Image sizes to one another to make them compatible for a morph.
• Capture – Live Images direct from a webcam or similar connected device.
• Zoom – In and out on images to enable accurate placing of anchors and to
view detail post-morph.
• Warp a user can warp an image by placing anchors in different place on 2 or
more of the same image.

Anchors

Anchors are the general term we use to describe the dots or lines that the user can
place on facial landmarks, such as the eyes, nose, mouth, hairline etc. It is essential
that these ‘anchors’ are set up accurately and placed on the same landmark on each
image in the morph to produce a good morph. Therefore the procedure for placing
anchors should be as efficient and intuitive as possible.

User should easily be able to:

• Add – anchors to the image in a variety of ways, using the toolbar button,
holding shift down while clicking, and through the right click menu.
• Modify – each anchor in terms of its size, shape, colour for increased
accessibility and usability.
• Match – the anchor co-ordinates from one image onto another image, through
the right click menu.
• Swap – the anchors co-ordinates from one image to another image, through
the right click menu.
• Select – anchors either single select by clicking the specific anchor or multi-
select in a variety of ways, by dragging a control box around anchors that need
selecting or by holding the ctrl key down and clicking on anchors to add them
one by one to the selection.

31
Miscellaneous

• Teacher Mode – After a morph, the user should be able to view the anchors
and how they have moved to morph the images.
• Show Video – Preview a video of the morph.
• Export Video – Export a video to a number of different video formats.
• Wizard – Basic wizard that prompts the user to open an existing morph file or
to create a new morph.

5.5 Testing
The first part of our external testing was to test the usability of the program from a
user’s perspective. We did this in two parts described below.

Firstly we used two independent testers and with no instruction, just a brief
description of what warping and morphing is.
Sagir ‘Haji’ Hassam (sah03) was the first tester, his feedback is as follows.
‘The splash screen is amazing; it really grabs your attention to the application and
gives a professional appearance to the program. I found conducting the morph very
simple and intuitive, the first problem that I ran into was that the images I used were
too small, but I realised that there was a zooming option and that impressed me a lot.
There are also a lot of options to change the appearance of the icons on the images
and that’s good, but maybe they can be taken out of the toolbar and moved into
menus, but overall I give the program 10/10 for usability and just think that the line
morph took quite a long time but the outcome was amazing and I was kept up-to-date
with the progress so that was a bonus.’
Sagir’s feedback is very positive. He managed to use the program affectively
from the start without any prompting and seemed very impressed with the application.
His point that the toolbar may be too complicated was a big concern to us, and we
have now moved many of the things out of the toolbar and into the menus, he is
pleased with the results.
A.P. – Apurva Udeshi was the second tester.
‘I liked it a lot. One problem that I found was that the anchors pop up in random
places and it is hard to keep up with where the new ones have come. Apart from that
the program was very easy to use, and a lot of fun to see the outcome. Also when
choosing 2 images of differing sizes, it would be good to have a preview of the
changes that are being made and not just the option to do it. Apart from that it is very
good.’
The testing here was interesting and Ap gave good feedback. To explain
further his findings before our final release we had the new anchors come up in a
diagonal line from the top left of the image, and this meant that if the user used many
anchors the new ones my occur on the actual facial part of the image. We changed
this and now all new anchors come up in the same place when the button is used to
create a new anchor, after showing A.P. this he was happy that it solved our problem.
The second issue that he pointed out to use was that if two images are of differing size
the program intervenes and asks the user to resize one of them, but no preview is
shown of how the image now looks, it just appears in its altered state in the picture
box. He proposed that we show the user a preview of what the changes mean to the
look of the image before it is too late for the user to rectify the problem. We did this

32
by adding a new for called the image editing form, and he was also pleased with this
addition.

The second approach to testing was to check the program against the Nielsen’s
10 Usability Heuristics. These are:

1) Visibility of System Status


2) Match between System and Real World
3) User Control and Freedom
4) Consistency and Standards
5) Error Prevention
6) Recognition rather than Recall
7) Flexibility and efficiency of use
8) Aesthetic and minimalist design
9) Help users recognise, diagnose and recover from errors.
10) Help and Documentation

2
10

4,8
6

33
3,5,9

I have picked the above forms to best convey our approach to fulfilling the criteria.

1) Visibility of System Status

Status bar keeps the user apprised of the status of the system.

2) Match between System and Real World

We have many links between the real world and the application such as picture
boxes, but the most vivid has to be the idea of an anchor.

3) User Control and Freedom

The second form above shows how we give the user freedom. We tried to make
the program as bespoke as possible. Allowing the user to change the appearance
of anchors, change values of constants and so on, but the most vivid has to be the
fact that we allow the user alter incompatible images how they desire.

4) Consistency and Standards

34
We tried to keep the GUI design of each form as standard as possible. One way
we did this was to have the toolbars in the same place and use the same icons on
each form for the same functionality.

5) Error Prevention

The second screen above is also an example of error prevention as prompts the
user to the problem of having 2 images that are different sizes.

6) Recognition rather than Recall

Again the reuse of icons the user will be familiar with from everyday use of
windows.

7) Flexibility and efficiency of use

This really was the motto of our project. We used standard windows hotkeys to
allow efficient use. We introduced new ones such as shift and mouse click to
place an anchor onto the image, the feature list out lines many more of these
features.

8) Aesthetic and minimalist design

Again the toolbar is a good representation of this it is very minimal and stiff
effective.

9) Help users recognise, diagnose and recover from errors.

The second screen shot above provides this functionality nicely. It is an error for a
user to try a morph between 2 images of differing size, as the morph is not
possible. This form is a nice way to allow users to recover from this error.

11) Help and Documentation

We have help in the program itself, we also have tool tips which act as help and
manuals and a wizard to guide the user through the use of our program.

Overall I believe that all of our forms meet these criteria well and as such we have
passed all of the test that we have.

5.6 Evaluation
Overall from the screen shots and the testing we can see that the GUI is well designed
from a user’s perspective. One of our main aims was to make the GUI as intuitive as
possible. To test this we used two independent testers and with no instruction, just a
brief description of what warping and morphing is, as described earlier in the testing
section. This was a huge success and so I think that we have met our requirement
here.

35
To further analyse our performance I will go through the main requirements and
evaluate how we performed in respect to what was expected.

 Load multiple images into application for morphing. This should be possible
through Drag-and-Drop and also a File Chooser dialogue box.

This has been achieved fully in mainForm where the images are loaded. The user
can import the images through the file browser and by the process of dragging and
dropping it onto the picture box.

 Manipulate images to enable morphing to work

As described before the morph will not work when images are different sizes, and
so that is what this is addressing. We have an image editing form that will allow
us to edit the images to make them compatible. This addresses the problem and
allows the manipulation of the images.

 Add, Select and Manipulate Control Points on all images in a Morph Sequence.

These points are now known as anchors. We allow the full manipulation of
anchors, from changing their position and their appearance to deleting. This is also
functionality displayed on the main form. We implemented many user friendly
features, all listed in the feature list and so I think we have surpassed our
requirement. From the very positive user feedback on this I think that we have met
this requirement well.

 Provide a wizard which walks the user through a morphing project.

We have provided a wizard to help the user. The feedback on this was that it may
not be required as the GUI is extremely easy to use, but we intend to cater for
every level of user and so this will help in doing that. This wizard is visible each
time the application is loaded.

 Display all intermediary frames required to generate the morph for use in a
‘Teaching Mode’.

This teaching mode is an option to be viewed when a morph is complete, the form
is called Result, and it contains all of the functionality discussed with our
supervisor for teaching purposes.

 Provide a Movie Player to view the animated morph sequence.

There is also a form to provide the functionality of playing the stream produced as
a result of the morph. This was explained above, and it provides all of the
desirable features. The users found that the features were all they required and
were very impressed with the export function that could encode the video.

We have met all of our requirements to a good level and so I think that overall our
GUI development was very good.

36
From a critical point of view I think that the way that we implemented the redraw
function each time that a change was made gave the GUI a flickering appearance,
where icons disappeared and reappeared often. This gives a sluggish look to the
program and could have been voided.

Also the user is able to put two points on the same pixel and this will cause problems
for the mesh algorithm. This could have been guarded against but caused other
undesirable behaviour so we decided to leave the behaviour as is. The undesirable
behaviour was that the user would not be able to put 2 anchors very close together
which gives good morphs, and, as described earlier the user feedback, the new
anchors should all be put onto the image in the same place, when the add anchor
button is used as the method of adding an anchor

The main approach that we took was to compare our solution to that of the Morphious
application available online. Our GUI is a lot more intuitive and we have many more
anchor options which we think distinguishes our application to Morphious and make
it a lot more usable.

Improvements that we could implement:-

• Get rid of redraw. The way that we used redraw worked well when it came to
validation but it lead to a jerky application. We should have had the GUI drive
the model and they way that we ended up having it is having the model drive
the GUI.
• Not all of the image types are acceptable to the application, we could improve
this by allowing the user to use any format they want, this will increase the
complexity of the program a lot, and we did not have the time to finish this
aspect.
• Automated addition of anchors would make the program a lot more user
friendly to a beginner. We looked into this and it became apparent that facial
feature recognition is another group project and so to try and add that to our
application would be very ambitious. We did find some implementations
online, but they did not integrate with out application well.
• Getting the application to be platform independent is always an advantage, but
our choice of development environment restricted this opportunity so getting
this application to work on Linux would be an improvement.
• We also would have liked to make our program appear more professional and
themed to XP, we think that making the GUI appear more ‘flashy’ would give
the wow factor that is somewhat lacking to a novice how many not be able to
appreciate what is behind the GUI.

37
6. Core

6.1 Introduction
The cores main functions are to connect the GUI to the algorithms and to carry out
common procedures in the process of morphing.

The core includes the model, which is an internal representation of the state of the
program including all of the images and anchors in a hierarchy.
.

6.2 Analysis
The core’s main functionality should be to:

• Load and manage all plug-ins and figures out which algorithms are
available to the user.
• Act as the middle man between the GUI and the Algorithms
• Encode the .avi video and outputs it in a variety of different formats.
• Carry out all parts of a morph common to all algorithms.
• Figure out which result generator to call, and sends the results back to the
GUI.
• Include a model that should represent each controls state at each stage of
the program.

6.3 Design

One of the main functions of the core was to ensure that it did as much of the common
work as possible, theoretically it should then be relatively simple for users to write
new plug-ins for our application. Thus it was imperative that the design of the core
was kept modular with a clean separation drawn between core and GUI. We ensured
that the core was designed early, so that no bottlenecks were created in the
development of the GUI and algorithms, and that they could be properly tested and
debugged.

The following basic requirements were used to design the core.

1. Load and manage GUI and plug-ins.

The core must first of all load the other components, i.e. the GUI and the plug-
ins/algorithms.

The sequence of events for this functionality is shown below:

38
Application Core Plugin GUI Main Form
Manager Manager Controller

2.0 run()
1. Create Instance
2.1 Create Instance

2.2 loadPlugins()

4.0. run()

4.1 Create Instance

3.0 Create Instance


4.3 updatePlugin()

2. Provide a Project object

We wanted to introduce the concept of projects and allow the user to save the
state of the program, and preferences to reload at a later date. For this to be
possible we used XML serialization. This integrates well with our model.

3. Provide an interface for communication with other components

The actual interfaces needed became clearer during implementation however it


was obvious from the outset that we would need a standard interface for:

• Anchors

We need to define what an anchor is and then let each different


algorithm extend this definition for its own anchor types.

• Algorithm parameters

We needed a standard way in which we call the algorithm, to do this


we needed to agree upon the amount and type of arguments that each
algorithm takes.

39
• Algorithms

An algorithm takes a set of anchors and generates a series of images,


which the core then streams to a video file.

The sequence Diagram below shows how the core acts as an intermediary
between the GUI and the algorithms when a morph is executed.

MainForm :GuiController :Core AbstractResultGenerator :IPlugin

2.0 res = 3 getResult() 4 Create Instance


1. Morph() doMorph(transform)

4.1.* getNextFrame()
3.1 Create Instance

3.2 GetResult() 4.2 Morph(Pixel,


controlPoints)

We then multi-threaded this process for as described below:

Threads are a powerful abstraction for allowing parallelized operations and for
our purposes are useful because they allow graphical updates to occur by using
another thread to perform computations. It is for this reason that we decided to
make use of a multi-threading architecture, whereby on execution of a morph,
a new thread is invoked to handle the processing, thus enabling the GUI to be
fully functional rather than in a blocked/waiting state.

In our implementation, the following sequence of events occurs:

• GUI sends a request to the Core for a morph


• Core creates a new thread, on which it runs the morph
• For every frame of the morph this thread performs a call-back to a delegate
which displays the current morph progress on the GUI
• On completion of the morph, a Result object is created and passed back to
the GUI through the Core, which can then be traversed as displayed as the
user requests
• The thread created by the Core is then disposed

40
The main problem we faced when implementing this was that we had threads
which interact with the GUI. A requirement in the .Net framework is that any
threads interacting with the GUI must run on a Single Threaded Apartment
(STA) GUI thread. After some research we found that to overcome this
problem, you have to use the beginInvoke / invokeRequired pattern. In
Microsoft’s words this pattern

"executes the specified delegate asynchronously on the thread that the


control's underlying handle was created on"

It basically checks whether the current thread is the one on which the delegate
should be run. If the thread has not been invoked, it invokes it and runs the
process, if it has, it keeps picking threads from the thread pool randomly until
it finds the right thread.

4. Generate video from a series of images

The core should provide this function and return immediately, so that the GUI
can stay responsive. Even though the code can easily be put into the GUI, we
feel that to make a clear separation between core and GUI, this task should be
done from the core.

5. Provide an object model that relates images in a morph to their anchors

The reason why we decided on a model was that it fits the model view
controller approach which gave us the greatest degree of freedom when it
came to things like saving projects and passing parameters around. It also
meant that the team were able to work independently to a greater extent than
we would have if there were no model, and the state was solely represented on
the controls and forms.

41
The design of the Model is very simple. For each control we need a representation of
its state at each stage of the program. Below is a class diagram of the final
representation of the model.

PluginInfo

clsTransform clsResult

clsImages
1 1
1 3
many
1
1
Image clsImageFrame

1
many

1
IAnchor

Triangle Coordinate Line

42
6.4 Implementation

C# was chosen as the implementation language as we wanted to use Visual


Studio.NET to aid in the design of the GUI and to use its various plug-ins to enable
our continuous integration server.

We went through our design goals and classes, properties and method stubs were
added in the right places to accommodate them. With this done, other developers
could write their code with these standards in mind, and the desired functionality of
the GUI and algorithms now finalised. All that remained was to fill out the method
stubs and debug and unforeseen problems, usually requiring collaboration between
developers on different parts of the project.
The implementation of the core evolved in a way such that small changes could be
made as problems were discovered or new features decided upon.

The implementation of the model within the core was a little more complicated, we
implemented the model and view to be as abstract as possible so that it can be
extended as simply as possible. An example of this is that the anchors are just generic
shapes so if an algorithm is designed that requires anchors that are square then Anchor
can be extended to implement this. Also the nature of the plug-in manager means that
other algorithms can be plugged in to our solution.

Below I have outlined two key components of the core, XML Serialization and AVI
Encoding.

XML Serialization

We quickly identified the most tedious part of the warping process for the animator to
be the addition of control-points to the source and target images. It then followed that
a feature enabling them to save their progress would be extremely beneficial. After
some quick research we came upon the inbuilt XmlSerializer class within c#, which
would enable us to transform our objects into some serial data format, namely XML.
We would make use of the Serialize() and DeSerialize() methods to provide the
saving and loading functionalities respectively.

We decided to use XML serialization as opposed to binary serialization for a number


of reasons. Firstly, XML being a text-based format meant it would be human
readable, and so we would be able to understand and manipulate the files, simplifying
debugging considerably. This would also mean that our files could be used by a third
party morphing program, because the author would be able to understand our files and
add functionality to their software to DeSerialize them. Finally, as XML was designed
to be a lightweight, platform independent format that could easily be transported, we
could extend our application by providing a web service, to which the user could send
the XML over HTTP.

The object at the root of the XML is the clsTransform object. The list following
details the attributes that are is serialized:
• Morph Type

43
• Source/Target image and associated properties – size etc.
• Control-Points
• Algorithmic Parameters

On implementing the serialization we came across a number of problems which we


needed to solve. Firstly, we found certain rules by which the classes to be serialized
have to conform:
• XML serialization serializes only the public fields and property values of an
object into an XML stream.
• XML serialization does not include type information.
• XML serialization requires a default constructor to be declared in the class that is
to be serialized.
• XML serialization requires all properties that are to be serialized as read write
properties. Read only properties are not serialized.

Having altered our class to conform to these rules, and adding XML attributes such as
the one shown below, we came to the problem of how to serialize an image.

Tells the serializer to serialize the


object, using the given name, including
the specified types for the elements

Tells the serializer how to serialise the


object

The problem we came across is that an image is stored as a bit map of the RGB
values for each pixel, it is therefore stored as a binary file. In order to store the
image in XML we would need to convert it into ASCII format. To overcome this,
we had to write a new property which would read the images into a memory stream
object, which we would then convert into an ASCII byte array and embed into the
XML file.
To view a sample XML saved data file, see the appendix.

Encoding an AVI

To create an AVI file from a sequence of C# images (each stored as a bit map), we
made use of an existing AVI module. Using this module, which is essentially a
wrapper to windows own AVI processing library, the AVIFil32.dll COM object, we
can create a new AVI and then use the supplied AVI player to view it.

The library is structured as described by the diagram below:

44
AviManager manages the streams in an AVI file. The constructor takes the name of
the file, which we obtain using System.IO.Path.GetTempFileName(), and opens it
with an empty video stream. We then call the AddVideoStream method, with the first
image from the result, which then creates a new video stream whose format is set to
those of the given image. We then iterate through the remaining images, calling the
AddFrame method on the video stream with each one. The stream is then stored in the
GUIController, so it can be exported using the MakeFileFromStream method should
the animator chose to.

The AviPlayer simplifies the task of displaying the AVI file greatly. The VideoForm
simply creates a new instance of the AviPlayer, giving it the video stream and the
picture box in which the video is to be displayed. There are some features which were
not supported by the player module such as pause, loop and a frame slider of the
progress through the movies were not. We extended the module to provide these
functions.

There was one key implementation issue during the integration of this library into our
application. The problem came when the AVIManager was encoding each bit map
into an AVI video frame. We were finding that after encoding the video, all the
images on the result form were flipped vertically, but the orientation of the video was
correct. On stepping through the code we found that the process of encoding an image
involves flipping it vertically, unfortunately the developer had forgotten to reverse
this process and so the images which were displayed on our form were upside-down.
Once these lines of code were found the solution was trivial, but we were remind of

45
how important it is to rigorously test all third party code before integrating it into our
application.

46
6.4.1 Class Listings (Core)
Below is a list of all the classes in the Core and the Model, a brief outline of these
classes is given.

AbstractResultGenerator.cs

This class generates the results for any type of morph. The class also contains the
code for cross fading.

Core.cs

This is the main core class that figures out which algorithms and plug-ins are loaded
and present when the program is launched. It contains the code that encodes the avi
video. It also decides which type of result generator to call the morph method on
before passing the results back to the GUI to process.

Line.cs

Contains all code to model a line, including the start and end point, code to calculate
the length of the line, the centre of the line and the gradient of the line.

LineResultGenerator.cs

See Field Morphing (Beier-Neely Algorithm)

MeshResultGenerator.cs

See Mesh Algorithm

PluginAttribute.cs

This class is used to decorate a plug-in, i.e. the name of the plug-in, a brief description
of what the plug-in does and the markerType used by the plug-in.

47
6.4.2 Class Listings (Model)
Most of these classes are just the representation of the states of the forms and controls
and so just contain state and very little functionality.

clsImageFrame

This class is the representation of a picture box with the anchors on it in the GUI. The
functionality is just various adding and deleting functions needed in order to best
mimic the user’s actions.

clsImages

This class contains a collection of images and a pixel format. This is the
representation of clsImageStrip in the GUI. The need for the pixel format is to make
sure that the images that are used in the morph are of the same type when it comes to
the type of the pixel for the morph to occur with the best results. There is also a
method called compatible which checks if to images are compatible when the second
is dragged into a picture box on the GUI, this is in order to alert the user to the fact
that the types of images should be the same for the morph to work.

Another important thing to point out in this class is that there is a method add which
you can give an image to and it will create a new ImageFrame and copy the anchors
of the previous ImageFrame, this is required in the normal use of the program.

clsResult

This class is used to represent what is shown on the ctlresult control. This means that
it contains 3 clsImages and has functionality to manipulate that data.

clsTransform

This is the representation of the clsImageStrip control. It contains the functionality to


add image replace images and delete images, again all of the functionality to represent
what the user can do in the control.

Coordinate

This class is the internal representation of a point control, and contains functionality
to validate where the point is i.e. the point has to be within the picture box that its
image is in.

IAnchor

This class is the abstract representation of a line or a point, and so is the parent class
of each. Within the core all anchors are referred to as of type IAnchor.

PluginInfo

48
This class contains the data that is held in the plug-in attributes that describe the plug-
in and outlines the marker type for that morph as well as the parameters that can be
altered by the user.

6.5 Evaluation
The finished core fulfils the specification it was built to.

However there was a lot less code re-use than was originally designed. This was due
to the result generators being dependent upon the anchor type used. (i.e. an anchor of
type line returns different results to an anchor of type point). Therefore part of the
originally planned core was actually implemented in the GUI controller.

Even though it would have been easier to encode video in the GUI controller,
structurally, encoding video is more of a responsibility attributed to the core than the
GUI and so was implemented in the core. This meant that a lot of message passing
had to occur, i.e. a button on the form would have to communicate with the form, then
communicate with the GUI controller, then communicate with the core all in order to
initiate something because of the guideline we had employed that stated that there
should be a clear separation between GUI and core.

The model was carefully planned and implemented, and does everything that we
planned. It keeps track of collections of images along with the anchors and their
respective positions on the images. It also enables us to load and save whole morph
projects to files using XML serialisation.

Limitations

Limited image format support:

Due to the limitations of the way C# and Visual Studio.NET handles images, the
application only supports JPEGs and Bitmap.

49
7. Core Algorithms
7.1 Introduction
In this section we go into detail on how the morphing and warping algorithms work in
greater detail.

Morphing

The generic process of morphing two images (SRC and DEST) has been described
below:

1) SRC image is warped to DEST


2) DEST image is warped to SRC
3) Sequences 1) and 2) are run concurrently and oppositely such that when SRC
is fully warped DEST will be unchanged. Therefore in a morphing sequence
of f frames, as we move from frame 1 to frame f we see SRC progressively
warping to DST’s shape (See Fig [n].a) and DST un-warping from SRC’s
shape to it’s unaltered state (See Fig[n].c)
4) Each frame of the concurrent sequence now contains two images Image 1
from Sequence 1 and Image 2 from Sequence 2. Image 1 and Image 2 are then
cross-dissolved (see below) to form the morphed frame. Image 1 goes from
full opacity to complete transparency whilst Image 2 does the opposite.

The above process produces a morph sequence from SRC to DEST.

Cross Dissolution

The most basic morph technique, used before warping came about, was the through a
cross-dissolve. This is when a transition from one image to the other is done simply
through a fading of the colours in each, using the interpolation technique described by
the diagram below:
Warp

Source
f0 f1 f2 f3 Destinationf 4
a) Image
Image

75 % 50 % 25 %
Cross dissolve
f0 f1 f2 f3 f4
b)

Cross dissolve 25 % 50 % 75 %

c) Destinationf 4 f3 f2 f1
Source f 0
Image Image

Warp

50
The above diagram describes the morphing progress described in Section [N].[1]. In
section b) we can see that each fame of the resulting morph sequence is taken as a
cross dissolution of one image from a) and another from c). In a sequence of f frames
the morph frame ‘n’ will consist of frame ‘n’(Image A) from a) and frame ‘f-n’
(Image C) from c). The colour composition of frame n from the morph sequence will
be (1-n/f)% of image A and (n/f)% of Image C.

The algorithm for cross-dissolving implements the method detailed in the diagram
above directly. It simply creates a new image, iterates through each of its pixels and
samples the correct percentage of the RGB values from the corresponding pixels in
each of the warped images. The diagram below should confirm the effectiveness of
the algorithm:

Warping

Warping an image is the process of changing its spatial configuration whilst


maintaining its colouring. There are a number of different algorithms commonly used
for this purpose. They take as input two images and two sets of corresponding Control
Points, and output a warped version of the source image, so its control-point features
match those of the destination image. Generally, a set of intermediate warps are also
created to provide a smooth transition between the source and target images.

Control Points

The warping algorithms use Control Points to identify the common features in both
images, using them as a reference on how the spatial configuration should be
changed. We are focussing on the warping of faces, so the control points in our case
would be the corresponding facial features such as the eyes, lips, mouth, hair line etc.
There are two ways to determine the common features on a face 1) Automatic Feature
Recognition, and 2) User Input.

After researching the industry-standard techniques for automatic recognition, and


through discussion with our supervisor we decided it would be best not to implement
automatic recognition, as the time available would not have permitted us to do so
convincingly.

Once the corresponding points have been marked on the two faces, the algorithm can
ensure that the specified features in the first image will end up with the corresponding
point in the second image. The warping can then be combined with cross-dissolution
to provide a more fluid and realistic morph. An example of a morph using these two
techniques together is shown below.

51
Warp

Destination
Image

Source
Image
Cross-
Disolution

Warp
The warping technique used here is Mesh Warping and the interpolated meshes are
shown in Section a) and c) above. Section b) shows the cross dissolution of the two
warp sequences, resulting in particularly convincing metamorphosis.

In the following sections we will detail the two algorithms we decided to implement,
but it is best to first understand our thinking with regard to the logical placement of
these algorithms and how they would be integrated with the overall system.

A fundamental design consideration we have maintained throughout this project has


been to have a plugin architecture for the algorithms. We wanted users be able to
write their own algorithms and easily integrate them into our program in the form of a
dll. To enable this feature and make it easier for users to write plugins, we wanted the
algorithm plugin to contain only the lowest level operations, i.e. only those which are
unique to the particular algorithm. Typically working on the input of a single pixel
and outputting the coordinates of the replacement pixel.

We therefore created an abstract class AbstractResultGenerator which was


responsible for those features which are common to all algorithms, regardless of the
marker-type. The role of this class then boils down to performing the cross-dissolve
between the Forward Warp and the Backwards Warp. This class then acts as the
interface between the GUI and the algorithms, sending the morphological sequence
back to the GUI for display/AVI encoding.

52
7.2 Field Morphing (Beier-Neely)

7.2.1 Background
The Field Warping technique uses two-dimensional control points to specify
corresponding features in the source and destination images. Each control point exerts
a field of influence on its surrounding area, with the strength of this field decreasing
in proportion to the distance from the control-point.

The algorithm presented by Thadeus Beier and Shawn Neely in 1992 was based upon
this technique, with the aim of simplifying the user-interface for morphing programs.
They presented a technique which would allow the user to specify corresponding
facial features using line pairs, thus providing a greater degree of control over the
morph by allowing the animator to choose the features they wanted the warp to focus
on.

7.2.2 Analysis
There are two ways to warp an image, Forward-Mapping and Reverse-Mapping.
Forward mapping scans the Source image pixel by pixel, copying each one to the
appropriate place in the destination image. The problem with this method is that the
pixels in the source may not map to a unique pixel in the destination, and so there will
remain some dead pixels. Reverse Mapping goes through the destination image in the
same way, but instead samples the correct pixel from the source image, thus ensuring
that every pixel in the destination image is painted to something appropriate.

The Beier-Neely algorithm implements Reverse-Mapping, building a warped image


by using coordinate mappings to find the pixel to be sampled. A mapping from one
image to the other can be defined as a pair of lines (one defined relative to the source,
and the other relative to the destination image), and it would be best to understand
how a single mapping is used to warp a pair of images, and then expand this for the
case of multiple line pairs.

Figure 1: Single line pair [Beier-Neely paper]

Taking the case above, the mapping is used to find a pixel (X’) in the source image
that corresponds to a particular pixel (X) in the destination image. The position of X’
is found by calculating the position of X relative to the control-line PQ, and finding
the pixel from the source image that is in the same position relative to P’Q’. This

53
process is then repeated for every pixel in the destination image, resulting in an image
whose pixels have all been replaced by a corresponding pixel from the source image.
The formulae below explain the mathematics behind this principle, note that
Perpendicular() returns a vector perpendicular to, and of the same length as, the
argument vector.

The value u is the proportional distance along the line, ranging from 0 to 1 as the pixel
moves from P to Q, and is outside this range should the pixel be outside the line. The
value v is either the perpendicular distance from the line, or should the pixel be
outside the line, the distance to the closest endpoint P or Q.

The single line case is simply a special case of the multiple line case, whereby each
line has associated with it a weight that determines the influence it exerts on a pixel,
depending on the distance from it. The following formula is used in the calculation:

where length is the length of the line, dist is the distance of the pixel to the line and α,
β, and ρ are algorithmic parameters as defined below (typical range in brackets).

• α determines the extent of the user’s control over the warp, a low value
meaning the pixels will go exactly where the user intended, with an increasing
value resulting in a less precise control but an increased overall smoothness to
the warp. (>0)
• β determines how a line’s weight is affected by its distance from a pixel, a
large value meaning a pixel will only be affected the line closest to it, and a
zero value meaning every line has the same relative influence. (0.5 – 2)
• ρ determines how line length influences line weight, a zero value meaning
length has no influence, and a higher value meaning weight is affected by
length. (0 – 1)

The multiple line algorithm is described below,

For each pixel X in the destination


DSUM = (0,0)
weightsum = 0
For each line Pi Qi
calculate u,v based on Pi Qi
calculate X'i based on u,v and Pi'Qi'
calculate displacement Di = Xi' - Xi for this line

54
dist = shortest distance from X to Pi Qi
weight = (lengthp / (a + dist))b
DSUM += Di * weight
weightsum += weight
X' = X + DSUM / weightsum
destinationImage(X) = sourceImage(X')

Briefly, for each line the algorithm calculates the u and v values and uses these to find
the position of X’. The displacement of the current pixel, X, from X’ is then weighted
and added to an accumulator, the process then being repeated for every line. Having
iterated through every line, the actual pixel X’ is calculated by taking X and adding
the weighted displacements to find the cumulative effect of every line. The process is
then re-run for every pixel X.

To create a realistic morphing sequence, it is necessary to generate multiple warped


images at intermediate stages between the source and destination frames. A new set of
coordinate mappings (line pairs) is then generated by interpolating the lines between
their positions in the source and destination images. The algorithm is then run at each
intermediate frame with the interpolated feature lines for that frame.

55
7.2.3 Design & Implementation
As the key design consideration we have maintained from the outset has been to have
a plugin architecture with only the most unique features to the algorithm in the plugin,
we structured the classes as detailed in the following section.

In the case of Beier-Neely, the lowest-level unique feature is the calculation of the
pixel to be sampled from the source image. It was decided that this calculation would
be performed by the Beier-Neely plugin. The features common to all morphs using
Line control-points would be handled by a wrapper class, Line Result Generator. This
class would extend an Abstract Result Generator class, which is responsible for the
functionality common to all morphing techniques regardless of control-point type.
The diagram below clarifies the structure:

<<AbstractResultGenerator>> ALGBeierNeely

LineResultGenerator

It is important to understand that we came upon the structure detailed above after
several stages of re-factoring. First, we had everything needed for the warp in the
Beier-Neely plugin, including the cross-dissolve and line-interpolation code. We then
realized that this was wasteful because certain methods would be required by other
algorithms which used lines pairs for control-points. To handle the features common
to all morphs using Line control-points, we decided to have a wrapper
LineResultGenerator class. After a further stage of refactoring we found that we still
had code-repetition, for example the method which would cross-dissolve the forward
and reverse warped frames, this is when we decided to have an
AbstractResultGenerator class which contained features which were common to all
morphs regardless of control-point type.

Below is a description of the functionality of each class:

Abstract Result Generator

As detailed above

Line Result Generator

• Interpolate Control Lines for each intermediate frame


• Iterate through every pixel in the destination image and call the algorithm plugin
to find the corresponding pixel to be sampled from the source image.
• Call the algorithm for each intermediate warp frame, once in the forward and once
in the reverse direction

Interpolation of Control-Point Lines:

56
There are two different methods available for interpolating lines, the first is to just
interpolate the endpoints of each line, and the second is to interpolate the center
position the orientation and the length of each line. In the first case, rotating a line
through 180° would cause it to shrink in the middle of the morph. However, the
second case does not create the most obvious interpolations and so the user may be
confused. As we plan to show the user the intermediate stages of the morph, we will
be using the second method, thus avoiding the problem of line shrinkage. The
diagram below shows an example of the two interpolation techniques:

Source Target

End-Point interpolation: Interpolated lines in blue


t

Source Target

Gradient/Mid-Point interpolation: Interpolated lines in blue


t

It is obvious that using the first method means the line disappears halfway through the
morph, whereas the line-length is maintained with the second method.

The algorithm to interpolate the control-point lines works on the following basis.
Given an array of all the control-point lines and a percentage P of the progress
through the warp, it calculates the centre, length and gradient of each new line. For a
source line S a target line T and a percentage P, it takes P percent of S’s values and
(1-P) percent of T’s values. It then creates a new line using a dedicated constructor
which takes as input a centre coordinate, length and gradient. The pseudocode below
should clarify this:

Line[] interpolatedLines = new Line[SourceLinesArray.Length]


i=0;
While i < sourceLinesArray.Length
S = SourceLinesArray[i]
T = TargetLinesArray[i]
new coordinate centre = P*S.Centre + (1-P)*T.Centre
new double gradient = P*S.Gradient+ (1-P)*T.Gradient
new int newLength = P*S.Length + (1-P)*T.Length
InterpolatedLines[i] = new Line(centre, gradient, newLength)
Loop
Beier-Neely Plugin

57
The Beier-Neely plugin is basically a direct implementation of the pseudocode
describing the multiple-line algorithm listed above. We implemented it directly in
visual C# with the view to porting it to Unmanaged C to improve performance.

C# .NET hides most of memory management, which makes it much easier for the
developer due to features such as the Garbage Collector and the use of references.
However, these features hinder performance and we often need to use Unmanaged C
so we can directly access and manipulate memory using pointers rather than
references. However, Unmanaged C has an extremely complex syntax, and it is harder
to use as you need be more careful and logical while using pointers. If you misuse a
pointer you risk overwriting other variables, causing stack overflows or causing the
program to crash. Furthermore, there is no in-built type checking and so if you access
an incorrect variable which has the wrong type, .net won’t execute the statement.

Once we implemented the algorithm in C#, we performed some further research into
Unmanaged C and found that integrating it with visual C# is inherently unstable. We
had also begun implementing the Mesh algorithm and decided that it would be best to
concentrate on provide the user more features as Mesh is already a much faster
algorithm and even after porting to Unmanaged C, Beier-Neely would never match its
performance.

Integrating Algorithm with Core

When integrating algorithm with the GUI and Core, we came across a few problems.
When we saw visually for the first time how the algorithm worked, we realised that
there were errors in our code. This was due to the fact that we had mis-interpreted
some of the maths from the pseudo code and tested the algorithm using Nunit with
this mis-interpreted maths in the primitive GUI , where all the tests passed. After
debugging the algorithm code, we found the functions that were erroneous and
corrected them.

After this minor set-back, we made the decision to concentrate on a suitable GUI that
would support the second algorithm, the mesh algorithm, before we actually
developed the algorithm.

58
7.2.4 Testing
This section will illustrate how well the algorithm performs, showing real-life
examples of a metamorphosis. It would be best to first analyse how effective it is to
morph a circle to a square, using the cross-dissolve-only morph as a comparison.

The figure below displays the two morph sequences, using 8 control points:

Cross-Dissolve-Only technique

Beier-Neily technique

It is obvious that the second sequence is considerably more effective. The areas of
overlap between the two object has been minimised through the warp, which has
resulted in much smaller shadowed portions. From the warping sequences below, it
can be seen that the algorithm is pulling out the four quadrants of the circle, and is
squashing the corners of the square, to math the spatial configuration defined by the
control-points.

Warp

Warp

59
However, on further inspection of the sequence, it is obvious that the morph is not
fully convincing, in that there are still shadowed portions. This is due to the
incompatible nature of the control-points with the curvature of the circle; it is simply
not possible to accurately specify the spatial configuration of the circle with only 8
straight lines, and so the sequence below shows how the morph improves with 16
lines:

The warp has produced a much more


convincing shape, as the curvature was
defined more accurately; resulting in
smoother edges

The size of the shadowed portion in this image is greatly


reduced, showing that the effect could be reduced further
by adding more control points.

This result is quite significant as we are focusing on the facial morphing, and as the
spatial configuration of a face is best defined with a curve, it will be necessary to use
a large number of points to achieve an effective metamorphosis.

Facial Morphing

The following images display a facial metamorphosis using 40 lines, with the
parameters we found to be most effective: α:1, β:2, ρ:0.5.

60
Forward Warp

Morph Sequence

Reverse Warp

The result is a very convincing morph, as can be seen by the realism of the middle
picture in the morph sequence. In the forward warp the most significant re-
configurations are the subjects jaw line, which has been straightened, and his facial
features which have been skewed up and pulled out slightly. It can also be seen that
the portion showing his clothing has been squashed vertically to match that of his
counterpart. In the reverse warp the most significant change would be in the shape of
this hair, which has now been pulled up in the middle to create a more triangular
shape, and the overall shift of features to the left. On close inspection of the centre
image, some shadowing can still be seen around the neck and left ear, but as
confirmed by the square-circle results, this could be improved by the addition of
further points.

Parameters

When testing the effects of the parameters, we ran 3 tests, one with each of the three
taking its highest value whilst the others were controlled at their lowest, the results are
shown below (two extreme warped frames and the centre metamorphosis frame):

61
α:2, β:0.5, ρ:0

α:0.1, β:2, ρ:0

α:0.1, β:0.5, ρ:1


From the two extreme warps in each sequence it can be seen that having a high value
of α or ρ causes very little facial warping, whilst a high value of β has most effect.
This is directly related to the quality of the warp, which can be seen to be significantly
better in the second sequence, where β is high. This is due to our previous finding,
that a large number of lines are required to define a human face, and so the weight of
the line should be directly correlated to the pixel’s distance from the line. Through
further testing we were able to conclude that the best parameters for a facial morph
are those we stated previously, α:1, β:2, ρ:0.5.

62
7.2.5 Evaluation
As can be seen from the morph sequences produced by the Beier-Neely algorithm,
there are no real issues with the quality of the morphs produced, providing suitable
parameters and control-points are used. The nature of the algorithm is such that there
is difficulty in specifying the spatial configuration of the face due to the straight lines
used as control-points.

Upon some further research into the developments of the algorithm, we found that
Jackel Birkholz has proposed an extension of Beier and Neely’s work whereby feature
curves can be used instead of feature lines. This development directly addresses the
issue of using our current field-warping algorithm, and works on the basis of
calculating the shortest distance from the given pixel to the curve, and scaling this to
the curve length and arc ratio of the target feature-curve. The problem with this
development is the computational power required, and as this is the key issue we have
with this algorithm, as I will now explain, we did not think it necessary to attempt an
implementation of it.

Performance testing, as detailed in the Performance Analysis section, has highlighted


the issues time issues with this algorithm. After testing the Mesh algorithm and seeing
the performance gains by restructuring it, we believe we could potentially improve the
performance of Beier-Neely by:
• Removing all object creation from the plug-in. Currently, new Coordinate
objects are created within the loop and so for every feature line several objects
are created, and as this algorithm is called on every pixel countless new object
are created for one warp frame. When optimising the Mesh algorithm, we
instead replaced all object creation for storing Coordinates with a primitive Int
for the x and y component. This brought significant gains.
• At the start of the algorithm we should check the progress through the morph,
currently the algorithm is called even if we are at 0%, effectively executing the
heavy algorithm but returning an unaltered image.
• Restructuring the classes so they have a lower cohesion would improve the
performance greatly, as the sheer number of cross-class calls means we are
taking a performance hit. However, as this obviously breaks the key software
enginerring concepts we will not look into this further.
All the above improvements are ones we discovered after rigorously testing and
comparing the performance with the mesh algorithm. Unfortunately, as this was not
done till late in the development cycle, we have not implemented any of them, instead
focussing on adding user functionality.

The improvements we did make however, include the following:


• Loop analysis to remove all invariants from the loop
• Storing of repeat calculation – such as (q-p) in the calculations of u and v.

63
7.3 Mesh Warping

7.3.1 Background & Analysis


Mesh warping was pioneered at Industrial Light and Magic (ILM) by Douglas Smythe
[Smythe] for use in the move Willow in 1998. It has been successfully employed in
many subsequent motion pictures. The warp is based upon segmenting the image into
a quadrilateral mesh [Wolberg90]. For the explanation of Mesh warping the source
image will be referred to as IA and the destination image is referred to as IB. The
source image has as a mesh, associated with it called MA. A mesh is a set of control
points which correspond to key features on that image. The destination image also
has a mesh associated with it called MB.

All the intermediary frames in the morph are created from the following process:

For each frame f do


Linearly interpolate mesh M, between MA and MB
Warp IA to I1, using meshes MA and M
Warp IB to I2, using meshes MB and M
Cross fade I1 and I2 to create If
End

In our project we decided to implement Triangulation based warping [Gosh86]. In


Triangulation the source and target images are first dissected into a desirable set of
triangles with the control points as the corners of the triangles.

There are many different methods for optimal triangulation. We chose to implement
Delaunay Triangulation [Shew96] which works by maximising the minimum inner
angle of all triangles to make them look as equilateral as possible and avoid wedge
shaped triangles.

The triangulation can be computed with a divide-and-conquer algorithm of


complexity O(n log n) where n is the number of data points. Due to the strict
mathematical mapping of pixels from one image to the other the pixel transformation
can be computed in constant time. With N number of pixels in an image the overall
complexity is
O(n log n+N) [Lee96].

A common problem with triangulation methods is that ‘foldover’ is possible. This


occurs when the control points are moved in such a way that they cause self-
intersection. This is shown in the diagram below in the destination image it is clear
that the pixels in triangle BCD will come from source triangle ABC and BCD. This
causes ‘foldover’ as there are multiple source pixels referencing a single destination
pixel.

A B B

A
C D C
D
64
This is commonly fixed by freezing the user’s movement. This means that the
resulting warp will not be as expressive as possible.

Each of the generated triangle s are subject to an affine transformation (Fig…) The
co-ordinates of the three vertexes on the source triangle and the corresponding
coordinates in the destination triangle allow us to formulate six simultaneous
equations in x and y which are solved to create a mapping function.

(x’0,y’0)
(x0,y0)
(x’1,y’1)

(x1,y1) (x2,y2)

(x’2,y’2)

Input Image Destination Image

65
7.3.2 Design
Due to the plug-in nature of the algorithm section of our project the part of the lowest
level of the algorithm which returns simply a pixel must be a separate plug-in
component. The UML diagram below shows the structure of the algorithm section:
<<AbstractResultGenerator>> ALGMesh

MeshResultGenerator

Abstract Result Generator

As detailed above

Mesh Result Generator

The Mesh Result Generator is responsible for the following:

 Perform Delaunay triangulation on Source Image


 Performs Match Triangulate on Destination Image
 For each Frame it interpolates the new mesh.
 Calculates Parameters required to map between source and destination triangles.
 Calculates bounds for the Fill method.
 Calls Fill method and for each pixel encountered call method in ALGMesh to find
the replacement pixel.

Foldover

As a group we had to make a decision on how to handle the event of a user moving
control points such that the triangulation algorithm would create more triangles in one
image than the other. This would cause foldover and thus undesirable effects in the
resulting morph sequence.

The common solution is to freeze the mesh boundaries of the MA. This has a
detrimental effect on the warp and the ability of the user to fully control the features
which he wishes to warp.

Instead of preventing the user from moving control points which would result in lots
of overheads in the GUI the group decided it would be best to let the user move the
point wherever they want and then perform a different triangulation algorithm on the
destination image which would always result in both images have the same number of
triangles. For the purpose of the report this second triangulation algorithm is called
Match Triangulate.

Match Triangulate

66
The Match Triangulate takes the set of triangles from the Source Image and replaces
the coordinates of the vertices with the corresponding points coordinates from the
Destination Image.

Interpolating intermediate Meshes

There are two ways in which the intermediate meshes can be calculated. The first is
by taking an edge from the Input Image and the corresponding edge in the Destination
Image and performing a linear interpolation between them. This would have to be
done twice in order to attain values for all three coordinates in the triangle. The
advantage of this is that linear interpolation would already be coded for Beier-Neely
algorithm. The disadvantage is the overhead of creating line objects and then
accessing them multiple times.

The second method for interpolating is by taking each vertex at a time in the Input
Image and linearly interpolating based on the corresponding vertex in the Destination
Image. This is done by taking a ratio of the x and y coordinates of both vertices. This
is a faster method but will require additional code in the Core. The benefit is that it
will allow for a programmer to develop an algorithm plug-in for single Control Points.
This means that Radial Basis Algorithms can be implemented as they are point based
warping algorithms.

Fill Method

The Fill method is required to scan within the bounds of the triangle calling a method
in ALGMesh which given the input of a vector and the set of six parameters will
output a new vector corresponding to the replacement pixel.

ALGMesh
 Contains the lowest level method which returns the coordinate of the replacement
pixel

67
7.3.3 Implementation
In this section we will discuss the implementation of the Mesh Warping algorithm in
the following order

1) Triangulation
2) Interpolation
3) Solving the Affine transformation
4) The Fill Method

Triangulation

The Delaunay triangulation algorithm which was implemented can be summarised by


the pseudo code below.

subroutine triangulate
input : vertex list + 8 boundary points
output : triangle list
initialize the triangle list
determine the supertriangle
add supertriangle vertices to the end of the vertex list
add the supertriangle to the triangle list
for each sample point in the vertex list
initialize the edge buffer
for each triangle currently in the triangle list
calculate the triangle circumcircle center and radius
if the point lies in the triangle circumcircle then
add the three triangle edges to the edge buffer
remove the triangle from the triangle list
endif
endfor
delete all doubly specified edges from the edge buffer
this leaves the edges of the enclosing polygon only
add to the triangle list all triangles formed between the point
and the edges of the enclosing polygon
endfor
remove any triangles from the triangle list that use the
supertriangle vertices
remove the supertriangle vertices from the vertex list
end

Most implementations of the above algorithm have a complexity of O(n2), where n is


the number of vertices. This is a result of the face that other programs incrementally
add vertices as they usually need to dynamically create the mesh as the user adds
more and more control points. Due to this, every time a new point is added, every
other triangle is checked to determine whether its circumcircle encloses the current
vertex point.

Another disadvantage of incrementally adding the vertices and calling the algorithm n
times is that calculations such as the triangle circumcircle data would have to be
calculated n times.

68
This is an overhead associated with the common method of freezing the mesh edges
to prevent the user from causing foldovers and strengthens our decision not to place
constraints on control points on the destination image. As a result of this
implementation issue the complexity is almost a linear function of the number of
points.

Black Triangles

The triangulation method above went through two iterations. In the first iterations the
vertex list passed to the algorithm was populated with the control points from the GUI
as well as the four corners of the picture box in order to create a triangulation
representative of our source image.

Unfortunately although the majority of the triangulations produced desirable results a


few did not. On a few occasions a triangulation similar to the one below occurred.
As a result there was a black triangle in the warped image which resulted in an
undesirable morph.

As shown above the left edge of the image has been completely ignored by the
triangulation algorithm.
In order to fix this we reduced the likelihood of an edge being missed out by adding
the mid points of the four edges as well (marked in red below). The resulting
triangulation was evidently better and it is unlikely that a morph that creates black
triangles will be created by the user.

Interpolation

To interpolate the triangles we used the point-to-point linear interpolation as apposed


to taking the lines. The diagram below

69
(x1,y1)

(xf,yf)
(x0,y0)

xf = x0 + f/n (x1 – x0)


yf = y0 + f/n (y1 – y0)

f = current frame number


n = number of frames

Solving the Affine transformation

In order to solve the Affine transformation a matrix solving c# add-on was used.
Which when given a matrix problem in the common form Ax=b solves the matrix and
returns x. This gave us the unique parameters for each transformation which is used
by ALGMesh to find the coordinates of the replacement pixel. This saved
development time and there were no issues involved in integrating this package into
our existing program.

Fill Algorithm

The fill algorithm works from the vertex with the lowest y-value to the vertex with the
greatest y-value. On each increment it calls a function which works outs the bounds
of the scan line. This is a two-step process, first a call is made to findBoundaries
which takes in a Triangle and a y value and returns the three x coordinates which
corresponding to the intersection of the three edges and the current scan line. Only
two of these x-values will be on the edges of the triangle and these are the bounds of
the scan line. The Fill Algorithm then scans between the x boundaries chosen and

70
calls the algorithm on the pixels. The diagram below shows how the fill algorithm
works.

fill
direction
scan line

points returned by
findBoundaries

Boundary points for filling

Ref. http://astronomy.swin.edu.au/~pbourke/modelling/triangulate/

71
7.3.4 Testing
In order to test the quality of morphing algorithms we are using the same images to
morph between and then compare the results of both algorithms. The input to our
program were the following two pictures. The control points are marked as the multi-
coloured circles on the two faces.

72
As a group we are very satisfied with the overall result of this morph. The key frame
to look at is the middle frame on the cross-fade. From this we are able to see if there
is any shadow effect. It is apparent that there is a little shadow on the bottom of the
image where there is a difference in where the two jackets start.

73
7.3.5 Evaluation
On the whole we were very impressed with the speed of this algorithm. However the
code can be optimised by implementing it in unmanaged C. Unmanaged C allows us
to directly access and manipulate memory using pointers rather than references.
However, Unmanaged C has an extremely complex syntax, and it is harder to use as
you need be more careful and logical while using pointers. If you misuse a pointer
you risk overwriting other variables, causing stack overflows or causing the program
to crash. Furthermore, there is no in-built type checking and so if you access an
incorrect variable which has the wrong type, .net won’t execute the statement.

As discussed later in the Technical Analysis of the solution the Mesh algorithm could
be optimized by checking if triangles have been moved before performing the fill
algorithm. This would prevent unnecessary runs of the fill algorithm which is
computationally quite complex.

74
8. Performance Analysis
This section of our report is concerned with analyzing our application from a
performance point of view. We expect a linear relationship between the time of a
morph and various changes in the number of anchors and pixels an image has.
We will compare and contrast the two algorithms and hopefully come to some sort of
conclusion about the optimal number of anchors each algorithm requires for a good
morph. We also hope to come to a point where we can identify problems or
bottlenecks and alter the application to increase the performance that we observe.

We will be performing these tests on a Dell P4 1.8GHz with 256MB ram machine and
so this should be taken into account when viewing the time taken for a morph, in the
labs the performance is considerably better.

The first thing that we will investigate is the time taken for a cross dissolve for 5, 10
and 15 frames as output, for two images one of 142x190 pixels and another of
285x380 pixels. These results are not skewed by .Net optimisations like the next set. I
re ran these tests because the results were hugely skewed by the fact that the first run
of the application is slower than subsequent ones due to .Net optimisations.

Below are a table and a graph of the times taken for the cross dissolving for the image
that is 142x190.

Frames
5 10 15
Time for small
image (Sec) 0.5 2.6 4.4

Time for small image

5
4.5
4
3.5
3
2.5 Time for small image
2
1.5
1
0.5
0
5 10 15

Frames

The cross-dissolve is a basic requirement for a morph, and is therefore required for
each algorithm. This test gives an indication of the pixel retrieval times and cross-
dissolve overhead. As expected the total number of frames is directly proportional to
the time taken in the dissolve.

75
To back this up, below are the graph and table for the larger image.

Frames
5 10 15
Time for large
image (Sec) 5.0 13.5 20.5

Time for large image

25

20

15
Time for large image
10

0
5 10 15

Frames

Below are the two graphs plotted on the same axis:-

25

20

15
Time for large image
Time for small image
10

0
5 10 15

Frames

As you can see the gradient for the larger image is bigger than that of the smaller.
This is expected as the larger image has more pixels and so the dissolving algorithm
will take longer.

76
The amount of time taken is surprising. It takes this long due to the fact that we store
every image that is created to pass back to the user for the teaching mode, and
creating the model obviously has a big overhead. Also we loop through each pixel on
the pairs of images and view them then take the weighted average and obviously this
is an inefficient way to do the cross fading.

Next we will investigate the relationship between the time taken for a morph and the
number of anchors we place on the images. We are not hugely concerned with the
quality of the morph but will still attempt to make good morphs. While testing the
application on lab machines we identified that the time taken for the same morph
varied, so we will perform each test 3 times and take an average. In these set of results
the first result usually takes longer, this is due to .Net optimisations which speeds up
the later use of some dlls.

Below are the results from the tests that we performed. The times shown are
representative of the time taken to create a five frame morph sequences. From this
point onwards in the report assume five frames unless stated otherwise.
90

80

70

60

50 Small Image (Beier Neely)


Time (Sec)

Small Image (Mesh)


Large Image (Beier Neely)
40 Large Image (Mesh)

30

20

10

0
0 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
No. of control points

No. of control points


0 5 6 7 8 9 10 11 20
Small Image (Beier Neely) 2.7 7.1 7.8 9.9 11.3 13.2 18.2
Small Image (Mesh) 2.4 2.7 2.6 2.6 2.6 2.6 2.6
Large Image (Beier Neely) 14.1 31.6 34.4 38.1 44.5 50.2 78.7
Large Image (Mesh) 13.2 13.1 12.8 12.9 13.1 13.1 12.9

The mesh warping results are not surprising on further inspection of the algorithm. If
one increases the number of points there is an increase in processing time by the
initial

77
triangulation component of the algorithm, we calculate this to be of O(n) time
complexity. This is no problem for modern computers, only if the number of points
were in the order of tens of thousands would it become a major factor. The Delaunay
triangulation algorithm is only run once at the beginning of the morph, so there is only
a very small overhead as the fill algorithm has to traverse through more triangles
when filling new images. Due to the constant linear nature of both of the mesh graphs
I think that we are able to say that the performance of the algorithm is independent of
the number of anchors, and is decreased as the resolution of the image is increased.
We quartered the number of pixels and saw a performance gain of 6 times the morph
speed.
This is enforced by the results shown for the zero anchor plot of the algorithm. This
time is at an equivalent level to that of the rest of the graph and indicates that the
processing that occurs before the manoeuvring of pixels is the bulk of the
performance loss. We have eight default points on the image to reduce the loss of
parts of the image to blank triangles, explained in the core algorithms section, this
means that the triangulation code is executed, the fill code, and the parameter solving
code. This indicates that these functions perform independently of the number of
anchors.

The Beier Neely results do not show the same flat gradient as the Mesh algorithm
exhibits. The gradient of the large picture is in the order of 3 seconds whereas the
gradient of the small picture was in the order of 0.7 seconds. This is expected as there
are approximately four times as many pixels in the large picture than in the small
picture. If one extrapolated the results for the small picture the graph plot for 0
control points would be close to 3.6s, however it turns out that the time is actually
2.7s which is below 3.6s as expected. This shows that the Beier Neely algorithm is
responsive to the absence of control points on the image however there is still no
performance gain over mesh.

An interesting point that we were not expecting to occur is that the Beier Neely
algorithm for a small image with more than 12 anchor points seems to take longer
than a large image using the Mesh algorithm with the same number of anchor points.
This leaves the performance of Beier Neely deterministically dominated by that of the
Mesh algorithm.

From the above analysis we can derive that there is a greater gain in performance by
optimizing the code in Beier Neely which deals with the calculation of replacement
pixels when there are control points on the image. The Mesh algorithm would be best
optimized with code that recognizes unchanged triangles.

78
20

18

16

14
Small Image

12 Small Image
(Cross Fade)
Time (Sec)

10

0
0 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
No. of control points

The chart above shows the time taken for a complete morph in comparison to the time
taken for the cross fade for a number of different control points.

One significant thing to note from this graph, as with all graphs showing cross fades,
is that its time is independent to the number of anchors, as they have no significance
when it comes to the cross fade, they are just ignored.

From this graph we can see that the difference between the time taken for a morph of
no anchors and the time taken for its cross fade is 2 seconds. The code responsible for
these 2 seconds can be identified easily. In this case it is the time taken to loop
through 142x190 pixels 2 times the number of frames times (5 in this case = 10). So
an instant optimization would be a check for no anchors before this loop.
Another observation is that the cross fade code takes a fraction of the time of a morph
and so does not really require optimization.

Below is the same graph for the larger image.

79
90

80

70

60
Large Image

50
Time (Sec)

Large Image
40 (Cross Fade)

30

20

10

0
0 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
No. of control points

The same conclusions can be made from this graph. In addition an increase in the
number of pixels by 4 has lead to an increase of 10 for the time taken to cross fade.
This is an interesting observation and may be an anomalous result. The graph below is
further analysis of this a graph which shows the time taken for the cross fade as the
number of pixels the image has increases.

100
90
y = 5E-05x - 0.3
80
70
Time (Sec)

60
50
40
30
20
10
0
0 200000 400000 600000 800000 1000000 1200000 1400000 1600000 1800000 2000000
No. of Pixels

This proves a linear relationship showing that each pixel takes 5E-5 seconds to cross
fade. The same analysis applies to the Mesh results and so is not included.
In this next section we compare the previous analysis with the same morphs but now
with two stage morphing. One would expect the time to increase linearly with the
number of stages in the morph. We assume that a one stage morph with ten frames

80
would have the same characteristics as a two stage morph with five frames in each
stage. The chart below shows our findings

180

160

140

120

100 Small Image (Beier Neely)


Time (sec)

Small Image (Mesh)


Large Image (Beier Neely)
80 Large Image (Mesh)

60

40

20

0
0 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
No. of control points

No. of control points


5 9 20
Small Image (Beier
Neely) 15.2 23.7 37.3
Small Image (Mesh) 6.09 5.95 6.04
Large Image (Beier
Neely) 64.2 89.79 161.59
Large Image (Mesh) 27.2 27.1 27.2

Above we compared and contrasted each morph with changing the number of anchors
on an image and here we found the relationship between the different types of morphs
with different types of images so the same analysis holds. Below is a table of the
times for a one stage investigation.
No. of control points
0 5 6 7 8 9 10 11 20
Small Image (Beier Neely) 2.7 7.1 7.8 9.9 11.3 13.2 18.2
Small Image (Mesh) 2.4 2.7 2.6 2.6 2.6 2.6 2.6
Large Image (Beier Neely) 14.1 31.6 34.4 38.1 44.5 50.2 78.7
Large Image (Mesh) 13.2 13.1 12.8 12.9 13.1 13.1 12.9

From comparing and contrasting the values we can see that there is a two to one ratio
between the times with the two stage morph being twice as long as the one stage
morph. This is as expected as there is double the work.

81
All of this analysis was very useful in changing the architecture of the application to
the state that it currently is in. The major changes came in our implementation of the
algorithms. We found that placing all of the Mesh code into one class and not making
the process create new instances of other classes improved the performance greatly.
We also moved the bulk of the algorithm into the core and this had the desired effect
of improving the performance.

We did not follow the same approach for the other algorithm because of its nature,
and the fact that a morph with no anchors is 2 seconds longer than just the fade shows
to some extent the performance hit we have taken. We did make some changes
however. Due to time constraints we were not able to go as far as we did with Mesh
(see evaluation of core algorithms), but we made small changes like taking common
parts of repeated calculations out of loops which were executed on each pixel and this
did give a marked improvement.

82
9. Project Summary
9.1 Dividing the Project
When tackling the project is was decided that the software was best split into three
different sections:

1) The GUI
2) The Core
3) The Algorithms

As a group we then collaborated to decide who would work on which areas of the
project. The outcome was that Dharmesh Malam and Christopher Roebuck would
work on the GUI, Jonathon Enav and Anish Mittal would work on the Core and Ravi
Madlani and Rikin Shah would work on the Algorithms.

To allow the teams to develop concurrently without interference, we created a source


repository enveloped with a continuous integration engine. This led us to adopt many
of the practices of Extreme Programming methodology. After some development
iterations, we decided to re-engineer our practices and devise a more team oriented
plan. As a result, the original teams were broken up, the projects were unified under a
single solution and the development efforts became more centric. This meant that all
developers in the team were responsible for the whole project and not just their own
classes. If code needed improvement in the Core a previous GUI developer would
still take up the task. This helped the global understanding of the project and how
each unit was meant to integrate with all of the others.

9.2 Continuous Integration


The continuous integrations ensure that any time code is checked in to the repository
it is compiled, regression tested and archived as a release. During heavy coding, this
can be several times a day. Since each of these integration cycles produces working
binaries, it forces the most stringent of practices and ensures that we have a
deliverable available to demonstrate progress at any time.

83
9.3 Module Integration
We were told early on by our project supervisor Daniel Rueckert that most previous
groups did not realise errors in their algorithms soon enough as they took too long to
develop a GUI to test them. As we did not want to fall into this trap we ensured that
our GUI would have the ability to return visual results from the morphing algorithms
as soon as possible. This allowed us to integrate the three modules and begin ironing
out bugs as soon as possible.

Integration of GUI and Core

The first GUI was extremely simple and primarily used to test the functionality of the
core. On the 1st of November a completely overhauled GUI was uploaded to the
continuous build server. This included support for drag and drop of images and had
the ability for the first time to return visually the results from the Beier Neely
algorithm.

The GUI and core sat together very nicely, and due to the nature of our setup, we
could easily integrate the two components together. The application evolved with
these two components sat along side each other.

Integration of Algorithms with GUI and Core

Beier Neely:

When integrating the first algorithm, Beier Neely, with the GUI and Core, we came
across a few problems. When we saw visually for the first time how the algorithm
worked, we realised that there were errors in our code. This was due to the fact that
we had mis-interpreted some of the maths from the pseudo code and tested the
algorithm using Nunit with this mis-interpreted maths in the primitive GUI , where all
the tests passed. After debugging the algorithm code, we found the functions that
were erroneous and corrected them.
After this minor set-back, we made the decision to concentrate on a suitable GUI that
would support the second algorithm, the mesh algorithm, before we actually
integrated the algorithm.

Mesh Algorithm:

When integrating the mesh algorithm, we realised that too much code was in the mesh
plug-in and not enough in meshResultGenerator so code had to be moved around as
there was no clear separation between the GUI and core part of the algorithm.

Once the Initial Integration was complete we decided to change the entire team
structure. This was due to their being too much overlap between the modules once
they had been integrated. In order to ensure all manpower was being used in the
project we decided to make an Action List. Due to the nature of development after
the initial integration, testing was occurring all the time. As a rule every time a team
member tested something he was working on he recorded any fixes required in an
Action Point list which was available to all developers. When someone felt they

84
could do something on the Action Point list they checked out the point so the whole
team knew what everyone was working on.

This proved to be an extremely effective development methodology and helped us use


our time as efficiently as possible.

Final Usability Testing

Nielsen's Ten Usability Heuristics are listed below, along with a description of how
re-morph attempts to satisfy each heuristic.

1 Visibility of system status -- The system should always keep users informed about
what is going on, through appropriate feedback within reasonable time.

Re-morph keeps users informed about the current system status through the status bar,
and does not allow users to carry out functions (e.g. by graying out options/buttons) if
the system is not in the appropriate state.

2 Match between system and the real world --The system should speak the users'
language, with words, phrases and concepts familiar to the user, rather than system
oriented terms. Follow real-world conventions, making information appear in a
natural and logical order.

Re-morphs interface is intuitive, placing anchors is a metaphor, as the anchors once


placed should not move from their position, as they should be placed on facial
landmarks such as eyes, mouth and nose. The icons represent completely what the
desired function corresponds to.

3 User control and freedom - Users often choose system functions by mistake and will
need a clearly marked "emergency exit" to leave the unwanted state without having to
go through an extended dialogue.

Re-morph has the functionality to be able to delete unwanted images and anchors, to
quickly switch from one algorithm to another without having to carry out any
additional procedure.

4 Consistency and standards -- Users should not have to wonder whether different
words, situations, or actions mean the same thing. Follow platform conventions.

In Re-morph, each button has one and only one function and this is clear to the user
by its description and its icon, however many functions can be achieved by a variety
of methods, leading to increased usability.

5 Error prevention -- Even better than good error messages is a careful design which
prevents a problem from occurring in the first place.

Re-morph prevents a function being called if the system is not in the correct state for
that function to be carried out. This prevents errors from occurring in the first place.

85
6 Recognition rather than recall -- Make objects, actions, and options visible. The
user should not have to remember information from one part of the dialogue to
another. Instructions for use of the system should be visible or easily retrievable
whenever appropriate.

All actions can be carried out through the use of clicking a button or through the right
click menu. Each action can be recalled or recognised by its icon or description.

7 Flexibility and efficiency of use -- Accelerators -- unseen by the novice user – may
often speed up the interaction for the expert user such that the system can cater to
both inexperienced and experienced users. Allow users to tailor frequent actions.

Re-morph does not have support for user created macros, however there are a number
of hotkeys which can be used to speed up frequent actions, for example, adding
anchors is a frequent operation which can be sped up through the use of holding shift
down while clicking to add an anchor.

8 Aesthetic and minimalist design -- Dialogues should not contain information which
is irrelevant or rarely needed. Every extra unit of information in a dialogue competes
with the relevant units of information and diminishes their relative visibility.

Re-morph has minimal dialogue, and where the dialogue appears it does not
overpower the more relevant parts of the forms. It is always clear what the users next
step should be.

9 Help users recognize, diagnose, and recover from errors -- Error messages should
be expressed in plain language (no codes), precisely indicate the problem, and
constructively suggest a solution.

Error messages are in-frequent in re-morph due to the user not being able to do certain
functions at inappropriate times. However, any error messages that do appear should
be sufficiently detailed enough for the user to know what has caused the error and
how they should go about recovering from it.

10 Help and documentation -- Even though it is better if the system can be used
without documentation, it may be necessary to provide help and documentation. Any
such information should be easy to search, focused on the user's task, list concrete
steps to be carried out, and not be too large.

Re-morph does not come with any documentation as we feel that the interface is
intuitive enough on its own, however each icon has a tooltip describing its function.

86
9.4 Evaluation
The initial separation of the system into three components, GUI, Core and Algorithms
was, in our opinion, the best approach. It gave us the ability to follow the Model-
View-Controller design pattern and provided a natural separation which fits into the
philosophy of object orientated programming. The current class structure means there
is very low coupling between the classes of each component, so for example changing
a user form will require no alteration in the rest of the system providing it is
compatible with the GUI controller. However, the high cohesion has led to large
performance overheads in the form of excessive class interaction during the warping
process, as described in the Core Algorithm Section. There is a trade-off between a
performance orientated architecture and one that conforms to good software
engineering practices. In our opinion, we made this trade-off too close to good
software design for Beier-Neely, but learning from our mistakes struck the right
balance when implementing the Mesh algorithm.

A key focus throughout the system development has been to ensure that the software
is designed with a user-centric approach. By giving the system to a panel of
independent users we were able to tweak the application and are now confident with
its usability. We performed a blind test, whereby the panel were given two
applications Re-Morph and Morpheus, and were told to comment on their preference.
The feedback we received confirmed that our application had a higher overall
usability, with the teaching mode receiving high praise. However, there were
comments that our application was slower than its counterpart, though this is
understandable as it’s more feature-rich.

In terms of our group dynamic, by adopting XP practices and guidelines such as the
use of continuous integration, re-factoring and unit-testing tools, we were able to
develop quickly and productively. We held stand –up meetings at regular intervals,
starting with a weekly basis and progressing to a daily frequency as the deadline
approached. Through these meetings and by pair-programming we found that each
pair could be focussed on their individual task whilst receiving guidance from the
leader who maintained the projects focus.

Overall, we are very happy with the application we have produced and are confident
that it stands up well against the existing market leaders.

87
10. Bibliography
Beier Neely
http://www.hammerhead.com/thad/morph.html (George Wolberg Servey)
http://www.cs.princeton.edu/courses/archive/fall00/cs426/papers/beier92.pdf
http://mambo.ucsc.edu/psl/beier.html
http://www.fmrib.ox.ac.uk/~yongyue/morphing.html
http://www.cs.cornell.edu/zeno/projects/vmorph/MM97/VMorph-MM97.html

Mesh Warping
http://davis.wpi.edu/~matt/courses/morph/2d.htm
http://www.cs.berkeley.edu/~jrs/mesh/

Delaunay Triangulation
http://wwwicg.informatik.uni-rostock.de/~hb01/03%20-%20Birkholz%20-
%20Image%20Warping%20with%20Feature%20Curves.pdf

General
rme04 MSC paper Facial Warping and Morphing (2004)
Metise Third Year Project Report (2004)
George Wolberg Image Morphing: A Survey (Visual Computer, vol. 14, pp. 360-372,
1998.)
www.codeproject.com
www.csharpcorner.com
www.citeseer.com

88
Appendix A. Organisational tools and methods
A number of tools were used to aid the success of the project. This section documents
some of those tools.

Microsoft Visual Studio.NET -Visual Studio was the IDE used to develop the
application with a number of plug-ins:

Visual SourceSafe

Source code management and version control system for Visual Studio.NET.
Members can safely and easily manage source code, Web content, and any
other type of file—all from the comfort and convenience of Visual Studio
.NET

CruiseControl.NET (CCNet)

CCNet consists of a suite of applications, but at its core is the


CruiseControl.NET Server, which is an automated continuous integration
server. The Server automates the integration process by monitoring the team's
source control repository directly. Every time a member commits a new set of
modifications, the server will automatically launch an integration build to
validate the changes. When the build is complete, the server notifies the
developer whether the changes that they committed integrated successfully or
not.

ReSharper

89
A plug-in that increases productivity in C# by re-factoring code, providing on-
the-fly error highlighting and quick error correction.

Nunit

A Unit Testing framework for C#, to enable test-driven development.

Easy Icon Maker and Macromedia Fireworks 8 – To aid in the design of the
project logo and icons.

Agile Methods

• Pair programming – One member codes and another observes and adds input.
(XP – Extreme Programming)
• UML Diagrams to provide a visual conceptualisation of the functionality of
the application. (UP – Unified Process)
• Frequent Stand Up Meetings – Where each member would announce what
they have achieved, what they hope to achieve and any problems they have
encountered (Scrum)
• Continuous Integration – a process that completely rebuilds and tests an
application. (XP - Extreme Programming)
• Test driven development – writing test cases first and then implementing the
code necessary to pass the tests. (XP - Extreme Programming)

90
Appendix B. Meetings and Pair-Programming Logbooks

Friday 14th October Meeting with dr (All present)

• Positions of Group Leader (Anish Mittal) and Group Secretary (Chris


Roebuck) decided.
• Background to Facial Warping and Morphing discussed.
• Possible Specification and extensions discussed.

Wednesday 19th October Meeting (All present)

• As a group discussed ideas for minimum and extended specification


• Agreed upon first draft of specification
• Agreed on division of responsibilities

Thursday 20th October Meeting with dr

• Went over the specification with dr


• Modified part of the specification to keep targets realistic

Friday 21st October – Deadline for Report 1

Tuesday 18th October Meeting with dr about algorithms (Present:


rdm03,rs303,akm103)

• Meeting with dr specifically about algorithms


• Given pseudo code and given advice on how to code a solution to Beier Neely
algorithm
• Anish Mittal(GUI/Project Leader) attended to oversee the structure of the first
algorithm and how it fits in with the project as a whole.

Monday 24th October Meeting (Present: cjr03,dm203)


• After making a start on GUI, realise that a lot of the GUI/Core functionality is
shared
• Discuss the beginnings of what is to become the model which will keep track
of all images and anchors attached
• Agree that to further develop a solution, it would be wise to meet with the
Core developers

Tuesday 25th October Meeting (Present: cjr03, dm203, je203, akm103)


• Discuss the object model, and how it should be engineered to keep track of all
images and anchors
• Agree that the model should be part of the core but will be heavily referenced
by the GUI.

91
Tuesday 8th November Meeting (Present je203, rs303, dm203)
• Plug-in Manager updated

Friday 11th November Meeting (Present: all)


• Group meeting to discuss any outstanding content that should be in the second
report

Deadline – Project Report 2

Monday 14th November Meeting (Present je203, rs303, dm203, rdm203)


• Mesh algorithm integrated into core/GUI and debugged/tested

Wednesday 16th November Meeting (Present rs303, rdm203)


• Mesh algorithm working ok with GUI/core
• Progress Bar

Monday 21st November Meeting (Present: dm203,cjr03)


• Lots of usability features added to GUI e.g. selecting anchors, modifying
anchors size, shape, colour.

Wednesday 30th November Meeting (Present akm103, je203)


• XML Serialisation fixed for saving and loading of project files.
Rikin Shah and Jonathan Enav

15th October Worked on Beier Neely algorithm with Ravi Madlani


8 hours

20th October Wrote the line interpolation and cross dissolve.


5 hours

28th October Integrated Beier Neely with core, re factoring to new structure
(abstractResultGenerator)
8 hours

4th November Researched and implemented avi encoder and player


5 hours

10th November Researched and implemented xml serialisation


9 hours

22nd November Extended AVI player fundamentals – slider to select individual


frames in the video
2 hours

Dharmesh Malam and Chris Roebuck

18th October Worked on new GUI with drag and drop of images
4 hours

20th October Planned Object model to keep track of images and their anchors

92
5 hours

28th October Integrate GUI with Beier Neely algorithm, wrote result form to
view intermediary images post morph
9 hours

5th November Updated GUI to enable video preview and export


4 hours

9th November Wrote code to integrate webcam into program and ability to get
a live capture direct from webcam into the program ready to be
used in the morph
5 hours

14th November Wrote, tested and debugged Mesh algorithm with Ravi Madlani
11 hours

15th November Cleaned up GUI with code for progress bar and timer for
statistical analysis of the program
3 hours
23rd November Added many functions to increase usability of GUI, ie multi-
select of anchors, modifying anchors colour, size, shape,
scaling images.
7 hours

27th November Polish application, added icons and logos.


4 hours

Ravi Madlani and Anish Mittal

15th October Research Beiner Neely algorithm


4 hours

17th October Design Pseudo-code for Beier Neely


3 hours

18th October Implement Beier Neely with Rikin Shah


8 hours

2nd November Research mesh algorithm and design appropriate pseudo code
4 hours

14th November Implement mesh algorithm with Dharmesh Malam


11 hours

20th November Began technical analysis of program using timer function to


compare speed of morphs

93
7 hours

15th December Write up of technical analysis, and mesh algorithm in final


report
5 hours

94

También podría gustarte