Documente Academic
Documente Profesional
Documente Cultură
1.INTRODUCTION
Image morphing is an image processing technique used for the visual effect of transforming from one image to another image. Ideally this is a seamless transition over a period of time. The main concept is to create an intermediate image by mixing the pixel color of the original image with another image. The easiest way to morph one image into another, for images that are the same width and height, is to blend each pixel in one image with the corresponding pixel in the other image. Basically, the color of each pixel is interpolated from the first image value to the corresponding second image value. Additional, more complex, image morphing techniques exist . We focused on an approach that makes use of barycentric coordinates for our project.The steps needed for image morphing consist of many repetitive tasks. Some aspects of the process are independent such as the computation to determine color for the pixels in the morphed image. This processing can be completed in parallel and therefore is a good candidate for optimization on FPGA. Image morphing is a special effect in motion pictures
IMAGE MORPHING
and animations that changes (or morphs) one image into another through a seamless transition. Most often it is used to depict one person turning into another through technological means or as part of a fantasy or surreal sequence. Traditionally such a depiction would be achieved through cross-fading techniques on film. Since the early 1990s, this has been replaced by computer software to create more realistic transitions. Morphing is an image processing technique used for the metamorphosis from one image to another. The idea is to get a sequence of intermediate images which when put together with the original images would represent the change from one image to the other. The simplest method of transforming one image into another is to cross-dissolve between them. In this method, the color of each pixel is interpolated over time from the first image value to the corresponding second image value. This is not so effective in suggesting the actual metamorphosis. For morphs between faces, the metamorphosis does not look good if the two faces do not have the same shape approximately.
IMAGE MORPHING
2.MORPHING
Morphing is derived from the word Metamorphosis. Metamorphosis or form. means to change shape, appearance
2.1.DEFINATION
Morphing can be defined as : Transition from one object to another. Process of transforming one image into another. An animation technique that allows you to blend two still images, creating a sequence of in between pictures that when played in Quick Time, metamorphoses the first image into the second.
2.2.TYPES OF MORPHING
1.Single Image Morphs A single morph is the most basic morph you can createmorphing one image to another image. For example, you can morph photos of two brothers; begin with one photo then morph to the other.Just for fun, you can display a
IMAGE MORPHING
frame halfway between the first photo and the second to project an imaginary but realistic "third" brother. 2.Multiple Image Morphs You can have a lot of fun with multiple morphs (also called "Series Morphs" by some manufacturers and "Morph 2+ Images" on our Morphing Software Homepage). Morphing a flower into a bird then morphing that bird into a child is a multiple morph; more than two images are involved. Morphing a series of photos into a mini-morph movie is a challenging but fascinating project. For a morph this diverse, pick a common visual themesuch as a similar background or a dark spot that the eye can latch onto during the morph. The more parallel your photos are in color, contrast and focal point, the more convincing the morph will be. Why? Because the viewer's mind focuses on the smooth change of your focal point. Your viewers will watch the eyes morph while other characteristics blend and change inconspicuously to the viewer's mind. Before you tackle a
IMAGE MORPHING
multiple morph, focus on becoming expert at smooth single morphs. Once you are confident you can place anchor points precisely for convincing single morphs, you are ready. Take time to pick multiple morph photos carefully. you'll want as much similarity as possible. The most impressive multiple morphs have several similarities and a few drastic differences. 3.Deform and Distortion Morphs Most morphing software will allow you to alter a photo in ways that will remind you of funhouse mirrors at the fair, but morphing software manufacturers call these features by different names. On our Morphing Software Homepage, we've labeled warp morphs as morphs you create manually with anchor points. Deform and distortion morphs are morphs that are automated. You can add deform and distort effects by clicking a menu option or by clicking and dragging the mouse. For this article, we'll stick with the generic term "warp morphing," meaning you can stretch, distort, or twist an
IMAGE MORPHING
image. You can make a photo of your little brother grow tall and skinny in seconds (instead of in years) or turn a friend into a cone head. If you are creating a warp morph from one image to another, however, make sure that both images are the same pixel size. Most morphing programs have a resize tool so you can match image sizes closely before distorting. 4.Mask Morphing A "mask" will isolate a specific part of an image and keep that part of the image stationary. Mask morphing tools come in handy when you want to warp, deform or animate just part of the picture. For example, you can mask most of the face and enlarge only the nose on a photo. You can even take a picture of an infant, mask all but one eyelid, and then make the baby appear to wink. 5.Auto Reverse Morphing When the morph sequence reaches the end (the target image) it will reverse, morphing back to the starting photo. If your reverse morph is so impressive that you never want it to end, try auto loop morphing.
IMAGE MORPHING
6.Auto Loop Morphing This morphing tool lets you automatically reverse then replay a morph. After completing just one morph sequence, you can morph a photo of a frog into a dill pickle, back to a frog, then repeat the transformation forever. Beyond these morph types, you can polish your morph with custom backgrounds, custom foregrounds or frames. You can even perform simple edits, such as cropping, rotating, altering color, sharpening focus, and tweaking contrast. Some morphing software allows you to apply filters to your images, such as negative morphing, grayscale morphing, and embossed morphing. For more details on what features each morphing software package offers, in addition to morphing definitions, see our Morphing Software Homepage .
IMAGE MORPHING
has led to the use of morphing techniques to create convincing slow-motion effects where none existed in the original film or video footage by morphing between each individual frame using optical flow technology.Citation is empty Morphing has also appeared as a transition technique between one scene and another in television shows, even if the contents of the two images are entirely unrelated. The algorithm in this case attempts to find corresponding points between the images and distort one into the other as they crossfade. While perhaps less obvious than in the past, morphing is used heavily today. Citation is empty Whereas the effect was initially a novelty, today, morphing effects are most often designed to be seamless and invisible to the eye. Image morphing is a technique to synthesize a fluid transformation from one image (source image) to another (destination image). Morphing has been wildly used in making visual effects. One of the most famous morphing example is Michael Jackson's "Black or White" MTV.
IMAGE MORPHING
3.2.STEPS INVOLVED
The morph process consists of : Warping two images so that they have the same shape. Cross dissolving the resulting images. Outline of our Procedures Our algorithm consists of a feature finder and a face morpher. The following figure illustrates our procedures.
IMAGE MORPHING
10
The details for the implementations will be discussed in the following paragraphs. Pre-Processing When getting an image containing human faces, it is always better to do some pre-processing such like removing the noisy backgrounds, clipping to get a proper facial image, and scaling the image to a reasonable size. So far we have been doing the preprocessing by hand because we would otherwise need to implement a face-finding algorithm. Due to timelimitation, we did not study automatic face finder. Feature-Finding Our goal was to find 4 major feature points, namely the two eyes, and the two end-points of the mouth. Within
IMAGE MORPHING
11
the scope of this project, we developed an eye-finding algorithm that successfully detect eyes at 84% rate. Based on eye-finding result, we can then find the mouth and hence the end-points of it by heuristic approach. 1.Eye-finding The algorithm. figure We below assume illustrates that the our eyes eye-finding are more
complicated than other parts of the face. Therefore, we first compute the complexity map of the facial image by sliding a fixed-size frame and measuring the complexity within the frame in a "total variation" sense. intensity of each pair of adjacent pixels. Total variation is defined as the sum of difference of the Then, we multiply the complexity map by a weighting function that is set a priori. The weighting function specifies how likely we can find eyes on the face if we don't have any prior information about it. Afterwards, we find the three highest peaks in the weighted complexity map, and then we decide which two of the three peaks, which are our candidates of eyes, really correspond to the eyes. The
IMAGE MORPHING
12
decision is based on the similarity between each pair of the candidates, and based on the location where these candidates turn out to be. The similarity is measured in the correlation-coefficient sense, instead of the area inner-product sense, in order to eliminate the contribution from variation in illumination.
2.Mouth-finding After finding the eyes, we can specify the mouth as the red-most region below the eyes. function is given The red-ness by
IMAGE MORPHING
13
where Rth is a threshold, and epsilon is a small number for avoiding division by zero. Likewise, we can define the green-ness and blue-ness functions. The following figure illustrate our red-ness, green-ness, and blue-ness functions. Note that the mouth has relatively high red-ness and low green-ness comparing to the surrounding skin. believe that using simple Therefore, we or edge segmentation
detection techniques we would be able to implement an algorithm to find the mouth and hence its end points automatically, if time permitting.
IMAGE MORPHING
14
3.Image Partitioning Our feature finder can give us the positions of the eyes and the ending points of the mouth, so we get 4 feature points. Beside these facial features, the edges of the face also need to be carefully considered in the morphing algorithm. If the face edges do not match well in the morphing process, the morphed image will look strange on the face edges. We generate 6 more feature points around the face edge, which are the
IMAGE MORPHING
15
intersection points of the extension line of the first 4 facial feature points with the face edges. Hence, totally we have 10 feature points for each face. feature points. In the following figure, the white dots correspond to the
Based on these 10 feature points, our face-morpher partitions each photo into 16 non-overlapping triangular or quadrangular regions. The partition is illustrated in the following two pictures. Ideally, if we could detect more feature points automatically, we would be able to partitioned the image into finer meshes, and the morphing result would have been even better.
IMAGE MORPHING
16
Image
Image
____
______
Since the feature points of images 1 and 2 are, generally speaking, at different positions, when doing morphing between images, the images have to be warped matched. strange such that their feature points are Otherwise, the morphed image will have and unpleasant that way.
four eyes, two mouths, and so forth. It will be very Suppose we would like to make an intermediate image between images 1 and 2, and the weightings for images 1 and 2 are alpha and (1-alpha), respectively. For a feature point A in image 1, and the corresponding feature point B in image 2, we are using linear interpolation to generate the position of the new feature point F:
IMAGE MORPHING
17
4.COORDINATE TRANSFORMATION
There exist many coordinate transformations for the mapping between two triangles or between two quadrangles. We used affine and bilinear transformations
IMAGE MORPHING
18
for the triangles and quadrangles, respectively. Besides, bilinear interpolation is performed in pixel sense. 4.1. Affine Transformation Suppose we have two triangles ABC and DEF. An affine transformation is a linear mapping from one triangle to another. For every pixel p within triangle ABC, assume the position of p is a linear combination of A, B, and C vectors. The transformation is given by the following equations,
Here, there are two unknowns, Lambda1 and Lambda2, and two equations for each of the two dimensions. Consequently, Lambda1 and Lambda2 can be solved, and they are used to obtain q. I.e., the affine transformation is a one-to-one mapping between two triangles.
IMAGE MORPHING
19
Suppose we have two quadrangles ABCD and EFGH. The Bilinear transformation is a mapping from one quadrangle to another. For every pixel p within quadrangle ABCD, assume that the position of p is a linear combination of vectors A, B, C, and D. Bilinear transformation is given by the following equations,
There are two unknowns u and v. Because this is a 2D problem, we have 2 equations. So, u and v can be solved, and they are used to obtain q. Again, the Bilinear transformation is one-to-one mapping for two quadrangles.
IMAGE MORPHING
20
IMAGE MORPHING
21
Scaling along the line and perpendicular to the lineScaling along the line only. The two other scaled images are examples of these two types of scaling.
Original Image
Rotated Image
IMAGE MORPHING
22
The rotation and scaling can be restricted to a region around the line by introducing a weighting function. 5.2.TRANSFORMATION WITH MULTIPLE PAIRS OF LINES Transformations performed using more than one pair of lines involve a weighted combination of the transformations performed by each line. Since reverse mapping is used, for each pixel in the destination image we find a pixel in the source image which would be used. The displacement corresponding to each line is calculated for each pixel. A weigted average of these displacements is used to determine how much each pixel needs to be moved. The weight used depends on the distance of the point under consideration from the line. The weight can also depend on the length of the line. This transformation, based on multiple lines, can be used effectively for morphing.
IMAGE MORPHING
23
An intermediate frame is obtained by doing three things The lines in the first image I0 are warped to the lines corresponding to the intermediate image. The lines in the second image I1 are warped to the lines corresponding to the intermediate image. The two warped images are now combined proportionately depending on how close the frame is with respect to the initial and final frames
Since the images have been warped to the same shape before cross-dissolving the intermediate image looks good.
IMAGE MORPHING
24
IMAGE MORPHING
25
The two original images, the two warped images and the two images corresponding to a cross-dissolve of the original images and a cros-dissolve of the warped images are shown.
7.WARPING
A warp is a 2-D geometric transformation and generates a distorted image when it is applied to an image. Warping an image means to apply a given deformation to it. Let the pair of lines in the source and destination images be as shown, A'B' and AB. The source image pixel is X' and the destination image pixel is X.
IMAGE MORPHING
26
Given X, the destination image pixel position, u and v are determined by the equations given below. Then X' in the source image is determined. The pixel in the souce image at X' is placed in the destination image at X.
IMAGE MORPHING
27
There are two ways to warp an image: Forward mapping Reverse mapping
7.1.FORWARD MAPPING
Each pixel in source image is mapped to an appropriate pixel in destination image. Some pixels in the destination image may not be mapped. We need interpolation to determine these pixel values. This mapping was used in our point morphing algorithm.
7.2.REVERSE MAPPING
This method goes through each pixel in the destination image and samples an appropriate source image pixel. All destination image pixels are mapped to some source image pixel. This mapping is used in the Beier/Neely line morphing method.
IMAGE MORPHING
28
The reverse mapping to determine the pixel in the source image corresponding to a given pixel in the destination image is done as follows.
In either case, the problem is to determine the way in which the pixels in one image should be mapped to the pixels in the other image. So, we need to specify how each pixel moves between the two images. This could be done by specifying the mapping for a few important pixels. The motion of the other pixels could be obtained by appropriately extrapolating the information specified for the control pixels. These sets of control pixels can be specified as lines in one image mapping to lines in the other image or points mapping to points.
7.3.TYPES OF WARPING
Mesh Warp: The warp is specified by control meshes. The algorithm was described in class. Field Warp: The warp is specified by a set of line pairs. An example is the Beier and Neely algorithm.
IMAGE MORPHING
29
Point Warp: The warp is specified by a set of control points. A simple solution is to use triangulation to convert point warp to mesh warp. There are several more complicated techniques such as scattered data interpolation and free-form deformation. This method of image warping is based on a forward mapping technique, where each pixel from the input image is mapped to a new position in the output image. Since not every output pixel will be specified, we must use an interpolating function to complete the output image. We specify several control points, which will map exactly to a given location in the output image. The neighboring pixels will move somewhat less than the control point, with the amount of movement specified by a weighting function consisting of two separate components, both dependent on the distance from the pixel to each control point in the image.
The first component of the weighting function is a Gaussian function which is unity at the control point and decays to zero as you move away from the control point. The idea is to have pixels far away from a control point be unaffected by the movement of that point. The problem
IMAGE MORPHING
30
with this scheme is that each pixel is affected by the weighting functions of every control point in the image. So even though the weighting function at a control point may be one, that point will still be affected by the movement of every other control point in the image, and won't move all the way to its specified location. In order to overcome this effect, we designed the second component of the weighting function, which depends on the relative distance from a pixel to each control point. The distance to the nearest control point is used as a reference, and the contribution of each control point is reduced by a factor depending on the distance to the nearest control point divided by the distance to that control point. This serves to force control point pixels to move exactly the same distance as their associated control points, with all other pixels moving somewhat less and being influenced most by nearby control points. The following examples show some of the uses of warping. The first set of images shows how facial features and/or expressions can be manipulated. The second set shows how the overall shape of the image can be distorted (e.g., to
IMAGE MORPHING
31
match the shape of a second image for use in the morphing algorithm).
The first image is a "normal" Roger with the control points we used identified. The second image shows how those points were moved. The last image is the more cheery Roger that results.
IMAGE MORPHING
32
Here we show what happens to Kevin when he combs his hair a little differently and goes off his diet.
8.CROSS DISSOLVING
A cross-dissolve is a sequence of images which implements a gradual fade from one to the other. After performing coordinate transformations for each of the two facial images, the feature points of these images are matched. i.e., the left eye in one image will be at the same position as the left eye in the other image. To complete face morphing, we need to do cross-dissolving as the coordinate transforms are taking place. Cross-dissolving is described by the following equation,
where A,B are the pair of images, and C is the morphing result.This operation is performed pixel by pixel, and each of the color components RGB are dealt with individually.
IMAGE MORPHING
33
The following example demonstrates a typical morphing process. 1. The original images of Ally and Lion, scaled to the same size. Please note that the distance between the eyes and the mouth is significantly longer in the lion's picture than in Ally's picture.
__________
2. Perform coordinate transformations on the partitioned images to match the feature points of these two images. Here, we are matching the eyes and the mouths for these two images. We can find that Ally's face becomes longer, and the lion's face becomes shorter.
IMAGE MORPHING
34
__________ 3. Cross-dissolve the two images to generate a new image. The morph result looks like a combination of these two wrapped faces. The new face has two eyes and one mouth, and it possesses the features from both Ally's and the lion's faces.
_______________
IMAGE MORPHING
35
9.MORPHING PROCESS
Step I : Interpolating the lines: Interpolate the coordinates of the end points of every pair of lines. Step II : Warping the Images: Each of the source images has to be deformed towards the needed frame. The deformation works pixel by pixel is based on the reverse mapping. This algorithm is called Beier-Neely Algorithm.
10.BEIER-NEELY ALGORITHM
IDEA IS TO: 1) Compute position of pixel X in destination image relative to the line drawn in destination image. (x,y) (u,v)
IMAGE MORPHING
36
2) Compute coordinates of pixel in source image whose position relative to the line drawn in source image is (u,v). (u,v) (x,y)
IMAGE MORPHING
37
For each pixel X=(x,y) in the destination image DSUM=(0,0) , weightsum=0 for each line(Pi, Qi) calculate(ui,vi) based on Pi, Qi calculate (xi, yi) based on u,v and Pi, Qi calculate displacement Di = Xi X for this line compute weight for line(Pi,Qi) DSUM+=Di*weight weightsum+=weight (xy) = (x,y)+DSUM/weightsum color at destination pixel(x,y) = color at source pixel(xy).
IMAGE MORPHING
38
Detector. Step 1: Start with an image of a good looking team member. Since no such images were available, we used the image shown to the right. Step 2: Blur the image. Since we want to select edges to perform a morph, we don't really need "every" edge in the image, only the main features. Thus, we blur the image prior to edge detection. This blurring is accomplished by convolving the image with a gaussian (A gaussian is used because it is "smooth"; a general low pass filter has ripples, and ripples show up as edges) Step 3: Perform the laplacian on this blurred image. Why do we use the laplacian? Let's look at an example in one dimension. Suppose we have the following signal, with an edge as highlited
below.
signal (which, in one dimension, is just the first derivative with respect to t) we get the
IMAGE MORPHING
39
following:
large peak centered around the edge. By comparing the gradient to a threshold, we can detect an edge whenever the threshold is exceeded (as shown above). In this case, we have found the edge, but the edge has become "thick" due to the thresholding. However, since we know the edge occurs at the peak, we can localize it by computing the laplacian (in one dimension, the second derivative with respect to t) and finding the zero
crossings.
laplacian of our one-dimensional signal. As expected, our edge corresponds to a zero crossing, but we also see other zero crossings which correspond to small ripples in the original signal. When we apply the laplacian to our test image, we get the following images:-
IMAGE MORPHING
40
The left image is the log of the magnitude of the laplacian, so the dark areas correspond to zeros. The right image is a binary image of the zero crossings of the laplacian. As expected, we have found the edges of the test image, but we also have many false edges due to ripple and texture in the image. To remove these false edges, we add a step to our algorithm. When we find a zero crossing of the laplacian, we must also compute an estimate of the local variance of the test image, since a true edge corresponds to a significant change in intensity of the original image. If this variance is low, then our zero crossing must have been caused by ripple. Thus, we have find the zero crossings of the laplacian and compare the local variance at this point to a threshold. If the threshold is exceeded, declare an edge. The result of this step is shown to the right. And finally, we have median Filter the image. We apply a
IMAGE MORPHING
41
median filter because it removes the spot noise while preserving the edges. This yields a very clean representation of the major edges of the original image, as shown below
Other Methods of Edge Detection There are many ways to perform edge detection. However, the most may be grouped into two categories, gradient and Laplacian. The gradient method detects the edges by looking for the maximum and minimum in the first derivative of the image. The Laplacian method searches for zerocrossings in the second derivative of the image to find edges. This first figure shows the edges of an image detected using the gradient method (Roberts, Prewitt,
IMAGE MORPHING
42
Various Edge Detection Filters Notice that the facial features (eyes, nose, mouth) have very sharp edges. These also happen to be the best reference points for morphing between two images. Notice also that the Marr-Hildreth not only has a lot more noise than the other methods, the low-pass filtering it uses distorts the actual position of the facial features. Due to the nature of the Sobel and Prewitt filters we can select out only vertical and horizontal edges of the image as shown below. This is
IMAGE MORPHING
43
very useful since we do not want to morph a vertical edge in the initial image to a horizontal edge in the final image. This would cause a lot of warping in the transition image and thus a bad morph.
Vertical and Horizontal Edges The next pair of images show the horizontal and vertical edges selected out of the group members images with the Sobel method of edge detection. You will notice the difficulty it had with certain facial features, such as the hairline of Sri and Jim. This is essentially due to the lack of
IMAGE MORPHING
44
IMAGE MORPHING
45
Horizonatal Sobel Filter We can then compare the feature extraction using the Sobel edge detection to the feature extraction using the Laplacian.
IMAGE MORPHING
46
Sobel Filtered Common Edges: Roger We see that although it does do better for some features (ie. the nose), it still suffers from miss mapping some of the lines. A morph constructed using individually selected points would still work better. It should also be noted that this method suffers the same drawbacks as the previous page. Another method of detecting edges is using wavelets. Specifically a two-dimensional Haar wavelet transform of the image produces essentially edge maps of the vertical, horizontal, and diagonal edges in an image. This can be seen in the figure of the transform below, and the following figure where we have combined them to see the edges of
IMAGE MORPHING
47
IMAGE MORPHING
48
And here are the maps of common control points generated by the feature extraction algorithm for the Jim-Roger morph.
IMAGE MORPHING
49
Although the Haar filter is nearly equivalent to the gradient and Laplacian edge detection methods, it does offer the ability to easily extend our edge detection to multiscales as demonstrated in this figure.
12.DESIGN DESCRIPTION
12.1.SOFTWARE DEVELOPMENT
The main idea of our project is to blend the pixels between two images.For this project, the concept of barycentric coordinates is used in the logic to create the morphed image.
IMAGE MORPHING
50
To make use of the barycentric coordinates concept, the project must contain two images that are preferably the same size and oriented such that the outline of the object is similar. In this project, those points are the eyes and no se of the face. Using these points, the image is divided into triangle, such as in the figure below of the kitten and cat images divided into eight triangles, as seen in the Figure below. Once the images are divided into triangles, the processing for each triangle is independent. This processing can be completed in parallel and therefore is a good candidate for optimization on FPGA.
IMAGE MORPHING
51
For each triangle ( A0B0C0 in the first image, A1B1C1 in the second image) there is a corresponding triangle AtBtCt for the morphed image. For this project, t = 0.5 or halfway through with image morph. For the triangle AtBtCt for the morphed image, each point Pt can be determined using barycentric coordinates (, , ), which are derived from the ratio of the smaller triangle formed by P (PAB, PAC, PBC) over the area of the entire triangle. The equations to determine a barycentric coordinate (, , ), is shown here. Using , , the corresponding points (and their pixel color, I0 and I1) in triangle A0B0C0 and A1B1C1 can be determined as shown P0 = t A0 + tB0 + tC0 P1 = t A1 + tB1 + tC1 The color of point Pt can be determined as shown: It = (1-t)I0 + t I1. By doing this procedure for all points in all triangles in the morphed image, the pixel color for the entire image can be determined by an interpolation of the initial color and final color.
IMAGE MORPHING
52
12.2.HARDWARE DEVELOPMENT
The MP3 software architecture was reused for the project with the intent that integration of the image morphing functions will be much easier. Also, by using MP3 architecture the results would have easily been displayed on the monitor. APU_INV entity was reused, with slight modifications from MP3. The state machine architecture was designed to perform two successive loads followed by a store. The first load was used to store image data from the first image while the second load was used to store data from the second image. Both loads were 128 bits long, with bits 120 to 127 containing blank bits. The primary goal of each load was to store data of 5 pixels, each of which contains 24 bits of color element data. Therefore for 5 pixels total number of bits came out to be 120, hence last 8 bits were ignored. The hardware design was broken up into 4 processes. below outlines each of the processes. InputReg process is where all inputs are mapped to local signals with in the entity. If reset is detected, all signals are set to 0, otherwise they are mapped to the input ports. The second
IMAGE MORPHING
53
process, StateMachineReg is where the next state of the state machine is determined. this was a clocked process used to help sync the hardware design flow. OutPutReg is also a clocked process where image merging is carried out. The initial goal was to take average of each element from each of the two images, i.e pixel 1 red from image 1 was added to pixel 1 red from image 2 and divided by two. The last process of the hardware design is Comp_Nxt_St. As described earlier, the state machine for checking the instruction type is similar to the MP3 design, except the state machine is designed to track two consecutive loads. The design defaults to INST_TYPE state where it waits for a valid instruction. If a load instruction is detected, the next state is set to WAIT_WBLV where it waits for either a loadvalid or a writebackok input, whichever comes first. Depending on which signal is detected first, the next state is either set to WAIT_LV or WAIT_WB. From here the next state is set to LOAD_DATA where data from the first image is stored. At this point local signal LDCNT is set to 1 and the next state is set to WAIT_LV to get ready for data from second load. LDCNT here is used to track the number of consecutive loads. Once the second load arrives,
IMAGE MORPHING
54
LDCNT is set o 2 and image merging takes place inside OutputReg. The next state is set to WAIT_INST, where the state machine either waits for the store or another set of load instructions. It is important to note that if another set of load instructions are detected before the store, result from the first two loads will be lost. INST_TYPE WAIT_WBL V WAIT_WB WAIT_L V LOAD_DA T A STORE_DA T A DECLOAD = 1 DECSTORE = 1 WRITEBACKOK = 1 LOADV ALID = 1 LDCNT = 01b DECNONAUTION = 0 LDCNT : Local signal that counts number of consecutive loads WRITEBACKOK = 1 LOADV ALID = 1 LDCNT = 10b Figure 6. Hardware design state machine.
IMAGE MORPHING
55
The hardware section was designed and tested with the TestApp_Peripheral from MP3. The file was slightly modified with two sets of two loads followed by a store. The design was tested out in simulation. A couple of run time errors were encountered where division was carried out and so this part of the code was commented out. The goal was to either do the division in software or to come back and fix the error after a successful integration had occurred.
13.DESIGN INTEGRATION
Since the morphing algorithm was complex, we decided to break the integration into multiple steps. The main morphing algorithm was coded on a windows based machine and the hardware was developed on the Xilinx-3 machine. The goal was to design both sections independent of each other to keep the complexity of the design simple. The next steps were to integrate the software on the ML507 board without the use of the VHDL. Once everything was working, VHDL code for pixel merging would have been off
IMAGE MORPHING
56
loaded to the hardware. However, a critical mistake was made in this design step. We highly under estimated the integration step and spent majority of our time on the software and hardware designs. To much of our disappointment we were unable to integrate the morphing on the ML507 board. Independently, both the software and hardware features work but once combined together on the hardware we kept running into one roadblock after another. Our integration via a modified echo didnt work out as expected. Our work schedules also didnt help the cause, as most of us couldnt meet regularly to discuss issues and come up with an appropriate solution. We were able to successfully implement the morphing algorithm in software and on any windows or linux-based machine. We were also able to code and test majority of the hardware design. However, both of the design blocks could not be combined to work on the hardware board.
IMAGE MORPHING
57
14.REQUIREMENTS
Program Name: IMAGE MORPHING 14.1.Hardware Requirement and Recommendations Hardware Component Processor RAM Free Hard disk Space Monitor Minimum 133 MHz 486 32 MB 30 MB VGA Recommendation 500MHz Pentium 128 MB 50 MB SVGA
14.2.SOFTWARE REQUIREMENTS: JAVA (jdk1.6.0, jre1.6.0) Pakage used: .io, .net, .util, .awt.event, .swing 14.3.System Requirements: 32-Bit versions
IMAGE MORPHING
58
Windows 95
64-Bit Versions
15.SYSTEM ANALYSIS
15.1.IDENTIFICATION OF NEED
In the description given below we have talked about the system used by the various Organization & Institutes for chatting. We are describing the nature, purpose and limitation of their existing system, and what we have done to improve the functionality of their system in the new system. Existing system can only work, if internet facility is provided, which result in high cost. Person not belonging to the organization can also interfere and can try to break the security of the organizational data.
IMAGE MORPHING
59
In the system used by the various Organizations and institutions, communication is generally being held frequently. The institutes used telephones, fax & e-mail systems to communicate with each other in which redundancy of data is normal. A lot of work has to be done for every task for mailing & fax. Besides this all the management is also difficult and user has to depend upon the services of ISPs for too long unnecessarily. 15.3.PROBLEM WITH EXISTING SYSTEM: This existing system has several limitation and drawbacks so switch to a better and effective system. Low functionality Security problem Future improvement is difficult. Miscellaneous.
16.FEASIBILITY STUDY
The objective of feasibility study is to examine the technical, operational and economical viability.
IMAGE MORPHING
60
16.1.TECHNICAL FEASIBILTY Technical feasibility centers on the existing computer system (hardware, software etc) and to what extent it can support the proposed addition. In our proposed solution formulated, did not require any additional technology. Hence the system is technically feasible. The entire solution enlisted does not require any additional software for this solution. 16.2.ECONOMIC FEASIBILTY More commonly known as cost benefit analysis, the procedure is to determine the benefits and savings that are expected from proposed system compare them with cost. The proposed system utilizes the currently available technologies. The cost during the development of the product is low. The interface and the scripts used are as simple as it could be. Therefore the net cost in its development is less. 16.3.OPERATIONAL FEASIBILTY
IMAGE MORPHING
61
Organizational,
political
and
human
aspects
are
considered in order to ensure that the proposed system will be workable when implemented.
17.SOFTWARE USED
ENGINEERING
PARADIGM
Like any other product, a software product completes a cycle from its inception obsolescence/replacement/wearing out. The process of software development not only needs writing the program and maintains it, but also a detail study of the system to identify current requirements related to software development as well as to anticipate the future requirements. It also needs to meet the objective of low cost, and good quality along with minimum development time. Requirement analysis and specification for clear
understanding of the problem. Software design for planning the solution of the problem.
IMAGE MORPHING
62
Coding (implementation) for writing program as per the suggested solution. Testing for verifying and validating the objective of the product. Operation and maintenance for use and to ensure its availability to users. This application was also developed in phases for effective output. Each phase was given its due importance with respect to time and cost. The time scheduling is later described in the PERT and Gantt chart. The system development life cycle of Project Management Information System is shown below.
Requirement Analysis and Specification
Designing
Coding
IMAGE MORPHING
63
Design
Implementation
IMAGE MORPHING
64
19.CODING
MORPH.JAVA import java.awt.image.BufferedImage; import java.awt.image.WritableRaster; import edu.rit.pj.BarrierAction; import edu.rit.pj.IntegerForLoop; import edu.rit.pj.IntegerSchedule; import edu.rit.pj.ParallelRegion; import edu.rit.pj.ParallelTeam; public class Morph { // This describes how often the GUI will redraw the morphed picture // Set this to a higher value, like 16, for larger jobs. public static int UPDATE_STEPS = 1; public static int inner_steps; //take a larger stepping factor for faster morphing public static int FAST_STEPPING = 1;
IMAGE MORPHING
65
// The total number of iterations may be as high as 256 becuase // the difference in color-intensity in some point may be 256 for some color public static int MORPH_ITERATIONS = 256/FAST_STEPPING + FAST_STEPPING; // After cropping, height/width should be equal for both images. public static int height; public static int width; public static BufferedImage morph; public static WritableRaster morphRaster; static int [][][] array1; static int [][][] array2; static MorphImg gui; public static int numThreads =1 ; public static boolean displayOff= false; private Morph(){} public static void setNumThreads(int threads) {
IMAGE MORPHING
66
numThreads = threads; } public static void setUpdateSteps(int steps) { UPDATE_STEPS = steps; } public static void setFastStepping(int steps) { FAST_STEPPING = steps; MORPH_ITERATIONS = 256/FAST_STEPPING + FAST_STEPPING; } public static void setDisplayOff(boolean c) { displayOff = c; } public static void MorphInit(BufferedImage image1, BufferedImage image2, MorphImg guiapp) { gui = guiapp;
IMAGE MORPHING
67
// Double check that we clipped the images at read in time // if not the same size assert(image1.getWidth() == image2.getWidth()); assert(image1.getHeight() == image2.getHeight()); height = image1.getHeight(); width = image1.getWidth(); int[] image1pixels = image1.getRaster().getPixels(0, 0, width, height, (int[])null); int[] image2pixels = image2.getRaster().getPixels(0, 0, width, height, (int[])null); // The gui displays the morphed image, which initially is the source image // "From" image morph = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); // Return a WritableRaster, an internal reprentation of the // image (class Raster), that we can modify
IMAGE MORPHING
68
morphRaster = morph.getRaster(); //Copy the "from" image and update the raster morphRaster.setPixels(0, 0, width, height, image1pixels); gui.updateMorphImage(morph, false);
// Generate the 3D representations of the images array1 = convertTo3DArray(image1pixels, width, height); array2 = convertTo3DArray(image2pixels, width, height); } //redraws only if display mode is on static void redrawPicture(int [][][]array1) { if(!displayOff){ //gui.freezeTimer(); int[] arr1D = convertTo1DArray(array1, width, height);
IMAGE MORPHING
69
morphRaster.setPixels(0, 0, width, height, arr1D); gui.updateMorphImage(morph, false); // update the reported timing gui.updateTimer(); //gui.unFreezeTimer(); } } public static BufferedImage parallelDoMorph() { final int last_iteration = MORPH_ITERATIONS % UPDATE_STEPS; //start the timer gui.startTimer(); try { //create the threads to execute the code in parallel (data parallelism) new ParallelTeam(numThreads).execute(new ParallelRegion() {
IMAGE MORPHING
70
//run method for the parallel team; all the team threads call run(). This is where the execution of the parallel region code happens. @Override public void run() throws Exception { //morph the picture for(int h = 0; h < MORPH_ITERATIONS; h += UPDATE_STEPS ) { // MORPH //System.out.println("h is "+ h+ " from thread "+ getThreadIndex()); final int steps = (h + UPDATE_STEPS) > MORPH_ITERATIONS ? last_iteration : UPDATE_STEPS; //have each thread loop through a subsection of the array1 array execute(0, array1.length, new IntegerForLoop(){ public IntegerSchedule schedule(){
IMAGE MORPHING
71
return IntegerSchedule.dynamic(16); //return IntegerSchedule.guided(128); } @Override public void run(int start, int finish) throws Exception { // send the array1 and array 2 variables between start and finish to be processed. //System.out.println("There will be "+ MORPH_ITERATIONS+ " MORPH_ITERATIONS"); for(int i=0 ; i < steps; i++){ morphTick(array1, array2, start, finish-1); //System.out.println("start to finish: "+ start + " "+ (finish-1) + " on thread "+ getThreadIndex()); //morphTick(array1, array2, 0, array1.length-1);
IMAGE MORPHING
72
/*barrier(); if(getThreadIndex()==0)
redrawPicture(array1); */ } }} new BarrierAction(){ public void run() { //System.out.println("IN THE BARRIER, ALL THREADS ARE @ SAME POINT"); //Redraw the pictures, for "animation". redrawPicture(array1); } }
} } }
IMAGE MORPHING
73
} catch (Exception e) { e.printStackTrace(); } //redrawPicture(array1); // We are done timing now gui.stopTimer(); // Add a done label, if we are using the display gui.updateDoneLabel(); // Saves the last image into a raster int[] arr1D = convertTo1DArray(array1, width, height); morphRaster.setPixels(0, 0, width, height, arr1D); return morph; } // The Morphing code, including display // The actual morphing is done in morphTick public static BufferedImage serialDoMorph() {
IMAGE MORPHING
74
int last_iteration = MORPH_ITERATIONS % UPDATE_STEPS; gui.startTimer(); for(int h = 0; h < MORPH_ITERATIONS; h += UPDATE_STEPS ) { // MORPH int steps = (h + UPDATE_STEPS) > MORPH_ITERATIONS ? last_iteration : UPDATE_STEPS; for(int i=0 ; i < steps; i++){ //System.out.println("start is "+ 0 + " finish is "+ (array1.length-1)); morphTick(array1, array2, 0, array1.length-1); // update the reported timing gui.updateTimer(); } // Redraw the pictures, for "animation". redrawPicture(array1); } // We are done timing now
IMAGE MORPHING
75
gui.stopTimer(); // Add a done label, if we are using the display gui.updateDoneLabel(); // Saves the last image into a raster int[] arr1D = convertTo1DArray(array1, width, height); morphRaster.setPixels(0, 0, width, height, arr1D); return morph; } /** * @param image1 is modified, with each pixel modified to look one * * */ private static void morphTick(int[][][] image1, int[][][] image2, int start, int finish) { //row for(int i = start; i <= finish; i++){ // column quantum level (or FAST_STEPPING) more like the corresponding pixel in image2.
IMAGE MORPHING
76
for(int j = 0; j < image1[0].length; j++){ // colors: red, green and blu // calculate the difference between two images for each color int diffr = (image2[i][j][0] - image1[i][j][0]); int diffg = (image2[i][j][1] - image1[i][j][1]); int diffb = (image2[i][j][2] - image1[i][j][2]); //signum() returns -1, 0 or 1. int redstep = (int)Math.signum(diffr); int greenstep = (int) Math.signum(diffg); int bluestep = (int)Math.signum(diffb); int adiffr = Math.abs(diffr); int adiffg = Math.abs(diffg); int adiffb = Math.abs(diffb); //take a big or small step if (adiffr >= FAST_STEPPING) redstep *= (int) Math.min(adiffr,FAST_STEPPING); if (adiffg >= FAST_STEPPING)
IMAGE MORPHING
77
greenstep *= (int) Math.min(adiffg,FAST_STEPPING); if (adiffb >= FAST_STEPPING) bluestep *= (int) Math.min(adiffb,FAST_STEPPING); //update the source image image1[i][j][0] += redstep; image1[i][j][1] += greenstep; image1[i][j][2] += bluestep; } } } // Unpack a 1D image array into a 3D array public static int[][][] convertTo3DArray( int[] oneDPix1, int width, int height){ int[][][] data = new int[height][width][3]; // Convert 1D array to 3D array for(int row = 0; row < height; row++){ for(int col = 0; col < width; col++){
IMAGE MORPHING
78
int element = (row * width + col)*3; // Red data[row][col][0] = oneDPix1[element+0]; // Green data[row][col][1] = oneDPix1[element+1]; // Blue data[row][col][2] = oneDPix1[element+2]; }} return data; } public static int[] convertTo1DArray( int[][][] data, int width, int height){ // Pack a 3D image array into a 1D array because raster requires 1D int array int[] oneDPix = new int[ width * height * 3]; int cnt = 0; for (int row = 0; row < height; row++){ for (int col = 0; col < width; col++){ //red
IMAGE MORPHING
79
oneDPix[cnt++] = data[row][col][0]; //green oneDPix[cnt++] = data[row][col][1]; //blue oneDPix[cnt++] = data[row][col][2]; } } return oneDPix; }}
MORPHIMG.JAVA import java.awt.BorderLayout; import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO;
IMAGE MORPHING
80
import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; public class MorphImg extends JPanel { static int THUMB_MAX_WIDTH = 280; static int THUMB_MAX_HEIGHT = 210; static int MORPHIMAGE_MAX_WIDTH = 400; static int MORPHIMAGE_MAX_HEIGHT = 300; static boolean parallelMode = false; static boolean displayOff = false; BufferedImage fromThumb; BufferedImage toThumb; BufferedImage fromImage; BufferedImage toImage; BufferedImage morphImage; JLabel timerLabel;
IMAGE MORPHING
81
JLabel timerLabelSeconds; JLabel morphLabel; ImageIcon morphIcon; Dimension morphImageSize; long time0; static long elapsedTime; long timeSub0; long timeSub; public MorphImg(BufferedImage fromImage, BufferedImage toImage) { // Crop images to make them the same size if (fromImage.getHeight() != toImage.getHeight() || fromImage.getWidth() != toImage.getWidth()){ int width = Math.min(fromImage.getWidth(), toImage.getWidth()); int height = Math.min(fromImage.getHeight(), toImage.getHeight()); toImage = toImage.getSubimage(0, 0, width, height);
IMAGE MORPHING
82
fromImage = fromImage.getSubimage(0, 0, width, height); } this.fromImage = fromImage; this.toImage = toImage; if(!displayOff){ // create thumbnails morphImageSize = ImageUtils.determineSize(fromImage.getWidth(), fromImage.getHeight(), MORPHIMAGE_MAX_WIDTH, MORPHIMAGE_MAX_HEIGHT); Dimension thumbSize = ImageUtils.determineSize(fromImage.getWidth(), fromImage.getHeight(), THUMB_MAX_WIDTH, THUMB_MAX_HEIGHT); this.fromThumb = ImageUtils.getScaledInstance(fromImage, thumbSize.width, thumbSize.height, RenderingHints.VALUE_INTERPOLATION_BILINEAR, true);
IMAGE MORPHING
83
this.toThumb = ImageUtils.getScaledInstance(toImage, thumbSize.width, thumbSize.height, RenderingHints.VALUE_INTERPOLATION_BILINEAR, true); this.createLayout(); } } private void createLayout() { this.setLayout(new BorderLayout()); //Display the from and to images Container topContainer = new JPanel(); topContainer.setLayout(new FlowLayout(FlowLayout.CENTER)); Container btmContainer = new JPanel(); btmContainer.setLayout(new FlowLayout(FlowLayout.CENTER)); ImageIcon fromIcon = new ImageIcon(fromThumb); JLabel fromLabel = new JLabel(""); fromLabel.setIcon(fromIcon);
IMAGE MORPHING
84
fromLabel.setBorder(BorderFactory.createTitledBorder("Ori ginal image")); topContainer.add(fromLabel); ImageIcon toIcon = new ImageIcon(toThumb); JLabel toLabel = new JLabel(""); toLabel.setIcon(toIcon); toLabel.setBorder(BorderFactory.createTitledBorder("Target image")); topContainer.add(toLabel); this.add(topContainer, BorderLayout.NORTH); //Display the image being changed //morphIcon = new ImageIcon(morphImage); morphLabel = new JLabel(); //morphLabel.setIcon(morphIcon); morphLabel.setBorder(BorderFactory.createTitledBorder("M orphing progress")); btmContainer.add(morphLabel); this.add(btmContainer, BorderLayout.CENTER);
IMAGE MORPHING
85
Container timerContainer = new JPanel(); timerLabel = new JLabel("Time taken: "); timerContainer.add(timerLabel); timerLabelSeconds = new JLabel("0 ms"); timerContainer.add(timerLabelSeconds); this.add(timerContainer, BorderLayout.SOUTH); } public BufferedImage morph() { Morph.MorphInit(fromImage, toImage, this); //based on the input argument, one of them will be called if(parallelMode) this.morphImage = Morph.parallelDoMorph(); else this.morphImage = Morph.serialDoMorph(); updateMorphImage(morphImage, true); return morphImage; } public void startTimer() {
IMAGE MORPHING
86
time0 = System.currentTimeMillis(); timeSub = 0; timeSub0 = 0; } public void stopTimer() { elapsedTime = System.currentTimeMillis() - time0 timeSub; } public void freezeTimer() { timeSub0 = System.currentTimeMillis(); } public void unFreezeTimer() { timeSub += System.currentTimeMillis() - timeSub0; } public void updateTimer() { if(!displayOff){ long t = System.currentTimeMillis() - time0; t -= timeSub;
IMAGE MORPHING
87
timerLabelSeconds.setText(Long.toString(t) + " ms"); } } public void updateDoneLabel() { if(!displayOff) timerLabel.setText("Done morphing images in: "); } public void updateMorphImage(BufferedImage image, boolean hiQuality) { if(!displayOff){ BufferedImage displayImage = ImageUtils.getScaledInstance(image, morphImageSize.width, morphImageSize.height, RenderingHints.VALUE_INTERPOLATION_BILINEAR, hiQuality); morphIcon = new ImageIcon(displayImage); morphLabel.setIcon(morphIcon); }
IMAGE MORPHING
88
} static void Usage() { System.err.println("Usage: MorphImg \n" + "[-p <n> ] switch to parallel mode with n number of threads\n" + "[-u <n> ] display frequency (default 8)\n" + "[-f <n> ] fast stepping (default 4)\n" + "[-t ] turn off display\n" + "[-i image1 image2 ]\n"); System.exit(0); } public static void main(String[] args) { JFrame frame ; String title = "Serial Morpher"; // Default image file names String fromImageStr = "from.jpeg"; String toImageStr = "to.jpg"; String file_extension = "_sm"; //serial morph
IMAGE MORPHING
89
int i = 0; int numThreads = 1 ; int updateSteps = 8; int fast_stepping = 4; while (i < args.length && args[i].startsWith("-")) { String arg = args[i++]; if (arg.equals("-i")) { if (i > (args.length-2)){ System.err.println("-i Needs 2 file arguments"); Usage(); } // 2 input files fromImageStr = args[i++];
IMAGE MORPHING
90
else if (arg.equals("-p")) { parallelMode = true; try{ numThreads =( Integer.parseInt(args[i++])); } catch (NumberFormatException Exc){ System.err.println("-p needs an int"); Usage(); } Morph.setNumThreads(numThreads); title = "Parallel Morpher"; // The output file will have the extension "_pm" added file_extension = "_pm"; //parallel morph } //display frequency for update steps else if (arg.equals("-u")) { try{ updateSteps = ( Integer.parseInt(args[i++])); Morph.setUpdateSteps(updateSteps); } catch (NumberFormatException Exc){ System.err.println("-u needs an int"); Usage();
IMAGE MORPHING
91
} } else if (arg.equals("-f")) { try{ fast_stepping = ( Integer.parseInt(args[i++])); Morph.setFastStepping(fast_stepping); } catch (NumberFormatException Exc){ System.err.println("-f needs an int"); Usage(); } // shuts off display } else if (arg.equals("-t")) { displayOff = true; System.out.println("Display turned off!"); Morph.setDisplayOff(true); } else{ System.err.println("Unknown option " + arg); Usage();
IMAGE MORPHING
92
File fromFile = new File(fromImageStr); File toFile = new File(toImageStr); assert(fromFile.exists() && fromFile.isFile()); assert(toFile.exists() && toFile.isFile()); //Read images BufferedImage fromImage = null; BufferedImage toImage = null; try { fromImage = ImageIO.read(fromFile); toImage = ImageIO.read(toFile); } catch(IOException e) {
IMAGE MORPHING
93
e.printStackTrace(); } //Initiate app and morphing MorphImg ma = new MorphImg(fromImage, toImage); if(!displayOff){ frame = new JFrame(title); frame.setContentPane(ma); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setVisible(true); frame.setSize(650, 650); } BufferedImage morphImage = ma.morph(); File output = new File(toImageStr + file_extension); try { ImageIO.write(morphImage, "JPG", output); } catch (IOException e) { // TODO Auto-generated catch block
IMAGE MORPHING
94
e.printStackTrace(); } System.out.println("Elapsed Time (ms): " + Long.toString(elapsedTime)); if(!displayOff){ try{ // last 2 sec before self destruction Thread.sleep(5000); } catch(Exception e) { System.out.println(e); } } System.exit(0); } }
IMAGE MORPHING
95
IMAGEUTILS.JAVA import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.Transparency; import java.awt.image.BufferedImage; /** * This code should not be changed * It is just image resizing for display in the GUI. */ public class ImageUtils { /** *@author http://today.java.net/pub/a/today/2007/04/03/perils-ofimage-getscaledinstance.html * Convenience method that returns a scaled instance of the * provided {@code BufferedImage} * @param img the original image to be scaled
IMAGE MORPHING
96
* @param hint one of the rendering hints that corresponds to * * {@code RenderingHints.KEY_INTERPOLATION} (e.g. {@code
RenderingHints.VALUE_INTERPOLATION_BILINEAR}, * {@code
RenderingHints.VALUE_INTERPOLATION_BICUBIC}) * @param higherQuality if true, this method will use a multi-step * scaling technique that provides higher quality than
the usual
IMAGE MORPHING
97
cases, where * * {@code targetWidth} or {@code targetHeight} is smaller than the original dimensions, and generally
* @return a scaled version of the original {@code BufferedImage} */ public static BufferedImage getScaledInstance(BufferedImage img, int targetWidth, int targetHeight, Object hint, boolean higherQuality) { int type = (img.getTransparency() == Transparency.OPAQUE) ?
IMAGE MORPHING
98
BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB; BufferedImage ret = (BufferedImage)img; int w, h; if (higherQuality) { // Use multi-step technique: start with original size, then // scale down in multiple passes with drawImage() // until the target size is reached w = img.getWidth(); h = img.getHeight(); } else { // Use one-step technique: scale directly from original // size to target size with a single drawImage() call w = targetWidth; h = targetHeight; } do {
IMAGE MORPHING
99
if (higherQuality && w > targetWidth) { w /= 2; if (w < targetWidth) { w = targetWidth; } } if (higherQuality && h > targetHeight) { h /= 2; if (h < targetHeight) { h = targetHeight; } } BufferedImage tmp = new BufferedImage(w, h, type); Graphics2D g2 = tmp.createGraphics();
IMAGE MORPHING
100
ret = tmp; } while (w != targetWidth || h != targetHeight); return ret; } public static Dimension determineSize(int origWidth, int origHeight, int maxWidth, int maxHeight){ if(origWidth < maxWidth && origHeight < maxHeight ){ return new Dimension(origWidth, origHeight); } double widthRatio = (double) maxWidth / origWidth; double heightRatio = (double) maxHeight / origHeight; if (widthRatio < heightRatio){ return new Dimension((int)(origWidth * widthRatio), (int) (origHeight * widthRatio)); }else{ return new Dimension((int)(origWidth * heightRatio), (int) (origHeight * heightRatio)); } }
IMAGE MORPHING
101
SUPPORTEDFORMATS.JAVA import javax.imageio.ImageIO; public class SupportedFormats { /** * Display file formats supported by JAI on your platform. * e.g BMP, bmp, GIF, gif, jpeg, JPEG, jpg, JPG, png, PNG, wbmp, WBMP * @param args not used */ public static void main ( String[] args ) { String[] names = ImageIO.getWriterFormatNames(); for ( int i = 0; i < names.length; i++ ) { System.out.println( names[i] ); }}}
IMAGE MORPHING
102
20.TESTING 1.
IMAGE MORPHING
103
2.
IMAGE MORPHING
104
3.
IMAGE MORPHING
105
4.
IMAGE MORPHING
106
5.
IMAGE MORPHING
107
21.BIBLIOGRAPHY
Detlef Ruprecht, Heinrich Muller, Image Warping with Scattered Data Interpolation, IEEE Computer Graphics and Applications, March 1995, pp37-43. Thaddeus Beier, Shawn Neely, Feature-Based Image Metamorphosis, SIGGRAPH 1992, pp35-42. George Wolberg, Image morphing: a survey, The Visual Computer, 1998, pp360-372. http://www.cs.rochester.edu/u/www/u/kyros/Cours es/CS290B/Lectures/lecture-18/sld002.htm Morphing Magic by Scott Anderson , 1993. Beier T. Neely S. Feature based image metamorphosis. Proceedings of SIGGRAPH92.