Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

Blu-ray/DVD Comparison Utility

In this article, Giovanni Montrone will provide an overview on how to create an image comparison application for DVD and Blu-ray screen captures.
ASPSOFT, Inc.

Difficulty: Beginner
Time Required: 2-3 hours
Cost: Free
Software: Visual Web Developer Express SP1 (or Visual Studio 2008 SP1), Silverlight 2 Tools
Hardware: None
Application: Run the application
Source Download: Download the source

 

Introduction

As a fan of High Definition movies and television, I often find it difficult to explain the benefits of upgrading from DVD to blu-ray to family and friends.  Many people believe that DVDs look good enough and/or that you need a really big television to notice the difference, and, even then, feel the improvement is not good enough to justify the upgrade.  The best way to convince someone is to show him/her the same movie playing side by side on blu-ray and dvd using the same sized screens.  Since it's difficult to do, the next best thing is to look at still images from both the DVD and the blu-ray and compare them.  The images should be from the exact same scene.  Fortunately there are places on the web such as AVSForum where members take the time to do this.  They usually do this to help people determine what kind of an upgrade they can expect when viewing the blu-ray compared to the DVD.  Sometimes the differences are easy to see, and other times they are not as clear.  I built this comparison tool as a way to see what differences exist, and provided a few ways to make those differences easier to see. 

This utility will allow the user to compare two different images using 3 different modes: dual view, swap view, and Rectangle View. Dual view will allow a side by side comparison where you see part of the first image, and the remaining part of the second image. Moving the mouse left or right will show more of one image, and less of the other. The swap mode will allow the user to see one image in its entirety, and then, when placing the mouse over the image, the application will swap the first image with the second one. The last mode will allow the user to specify a region that he or she wishes to compare. They can draw a rectangle using the mouse, and the application will show the first image with a chunk cut out, replacing the rectangle area with the equivalent area on the second image. The image below shows an example of the rectangle view.

 

bddvdcompare1

Notes

The application is set to handle images that are 1920x1080 in resolution (the standard resolution of blu-ray movies).  DVDs have a resolution of 720x480, but are typically up-converted to 1920x1080 in order to do a direct comparison.  This upconversion is similar to what happens when you play a DVD on a 1080p high definition television.  While it wouldn't be too difficult to handle other image sizes, to keep things simple, the application assumes the user will be providing images at the proper resolution.  Using different images with different resolutions will result in incorrect behavior.

At the time of this writing, Silverlight 3 beta has just recently been released, but has not been tested with this application.

Setup

Before beginning, you must have Visual Studio 2008 SP1 or Visual Web Developer Express SP1.  Be sure to install Silverlight 2 Tools in order to create and run the project.  Once you have that, start a new Silverlight Application and call it BlurayDVDCompare, or whatever you desire.  As shown in figure 1, choose the option to host Silverlight with an ASP.NET web application. This will create a web project and a separate Silverlight project.    

 

figure1

 

Creating the UI

Since we will be displaying HD images (1920x1080), our next step is to ensure that the browser adds scrollbars so that the entire Silverlight control can be seen.  To do this, we need to open and edit the hosting web page (BlurayDVDCompareTestPage.aspx) and set the Width and Height properties of the Silverlight control to 1920 x 1200 as shown below.  The 1200 is extra room for other controls that we will add.

<asp:Silverlight ID="Xaml1" runat="server"
Source="~/ClientBin/BlurayDVDCompare.xap"
MinimumVersion="2.0.31005.0" Width="1920" Height="1200" />

Our UI will be pretty simple.  We will have a couple of textboxes which will be used so that a user can specify a URLs to images they wish to compare.  We will also add a couple of progress indicators so that we can show progress when the images are being loaded.   We will also have three radio buttons to allow the user to select which comparison mode they wish to use. 

The main part of our UI, will be the canvas that holds the two images that we are comparing.  Each image has a clip region which will be used to display parts of each image or to hide/show an image.  For example, in DualView mode, we want to display part of one image, and the remaining part of the second image.  If no clip regions were set, the default behavior would force the second image to cover the first.  The final thing we add is a border which we will be used to draw a line as a separator between the two images in DualView.  In Rectangle view, the border is used to draw the rectangle.

<StackPanel>
<Canvas x:Name="canvas" Background="White" Width="1920" Height="1080" Margin="0,5,0,0">
<Image x:Name="img1" Stretch="None" >
<Image.Clip>
<RectangleGeometry Rect="0,0,960,1080" x:Name="rcImg1" />
</Image.Clip>
</Image>

<Image x:Name="img2" Stretch="None" >
<Image.Clip >
<RectangleGeometry Rect="960,0,1920,1080" x:Name="rcImg2" />
</Image.Clip>
</Image>

<Border x:Name="border" Width="2" Height="1920" Margin="0,0,0,0" BorderBrush="Bisque" BorderThickness="0" />
</Canvas>
</StackPanel>

 

Implementation

Since we want to allow the application to load images from the web, we define the LoadImages() method which accepts two URLs.  In order to do this, we create new BitmapImage objects with the given URLs.  This will load the images asynchronously and we will assign them as the image Source property for the two UIElements.  Once loading has finished, they will automatically appear in the canvas. 
 
C#
private void LoadImages(string s1, string s2)
{
brLoading.Visibility = Visibility.Visible;
spLoadingError.Visibility = Visibility.Collapsed;

if (string.IsNullOrEmpty(s1) || string.IsNullOrEmpty(s2))
{
txtLoadingError.Text = "Invalid image location given";
brLoading.Visibility = Visibility.Collapsed;
spLoadingError.Visibility = Visibility.Visible;
return;
}

prgLoading1.Value = 0;
prgLoading2.Value = 0;

_bmi1 = new BitmapImage();
_bmi2 = new BitmapImage();
_bmi1.DownloadProgress += new EventHandler<DownloadProgressEventArgs>(bmi1_DownloadProgress);
_bmi2.DownloadProgress += new EventHandler<DownloadProgressEventArgs>(bmi2_DownloadProgress);

_bmi1.UriSource = new Uri(s1, UriKind.Absolute);
_bmi2.UriSource = new Uri(s2, UriKind.Absolute);

img1.Source = _bmi1;
img2.Source = _bmi2;
}

 
VB
Private Sub LoadImages(ByVal s1 As String, ByVal s2 As String)
brLoading.Visibility = Visibility.Visible
spLoadingError.Visibility = Visibility.Collapsed

If String.IsNullOrEmpty(s1) OrElse String.IsNullOrEmpty(s2) Then
txtLoadingError.Text = "Invalid image location given"
brLoading.Visibility = Visibility.Collapsed
spLoadingError.Visibility = Visibility.Visible
Return
End If

prgLoading1.Value = 0
prgLoading2.Value = 0

_bmi1 = New BitmapImage()
_bmi2 = New BitmapImage()
AddHandler _bmi1.DownloadProgress, AddressOf bmi1_DownloadProgress
AddHandler _bmi2.DownloadProgress, AddressOf bmi2_DownloadProgress

_bmi1.UriSource = New Uri(s1, UriKind.Absolute)
_bmi2.UriSource = New Uri(s2, UriKind.Absolute)

img1.Source = _bmi1
img2.Source = _bmi2
End Sub

Now that we have a way to load images, the next step is to handle the different type of comparison modes.   We will start with the dual view since that will be our default mode.  As mentioned earlier, the dual view basically shows both images side by side in such a way where both images are presented as one. The first image is partially shown, and the second is shown as a continuation of the first.  In order to do this, we track the mouse movement when it is inside of the canvas by setting up a MouseMove event handler.  Every time the mouse moves, we grab the new x,y coordinates.  Since dual view is only concerned with left/right movement, we only pass in the x coordinate.   
 
C#
void canvas_MouseMove(object sender, MouseEventArgs e)
{
double x = e.GetPosition(canvas).X;
double y = e.GetPosition(canvas).Y;

if (_comparisonMode == ComparisonMode.DualView)
DisplayDualView(x);
else if (_comparisonMode == ComparisonMode.Rectangle)
{
// ...
}
}

VB
Private Sub canvas_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
Dim x As Double = e.GetPosition(canvas).X
Dim y As Double = e.GetPosition(canvas).Y

If _comparisonMode = ComparisonMode.DualView Then
DisplayDualView(x)
ElseIf _comparisonMode = ComparisonMode.Rectangle Then ' .....
End If
End Sub

C#
private void DisplayDualView(double x)
{
rcImg1.Rect = new Rect(0, 0, x, IMGHEIGHT); // Show the first half (left side), up to xCoord
rcImg2.Rect = new Rect(x, 0, IMGWIDTH, IMGHEIGHT); // Show the second-half (right side), starting from xCoord

Canvas.SetLeft(border, x); // make the separator start at x
Canvas.SetTop(border, 0); // set the separator to the top
}

VB
Private Sub DisplayDualView(ByVal x As Double)
rcImg1.Rect = New Rect(0, 0, x, IMGHEIGHT) ' Show the first half (left side), up to xCoord
rcImg2.Rect = New Rect(x, 0, IMGWIDTH, IMGHEIGHT) ' Show the second-half (right side), starting from xCoord

Canvas.SetLeft(border, x) ' make the separator start at x
Canvas.SetTop(border, 0) ' set the separator to the top
End Sub

 
The DisplayDualView() method sets the image clip region of the first image to be a rectangle starting from pixel 0 to pixel x, then sets the clip region of the second image to be the remaining pixels.  It uses our border to represent a break in the two images so that it is clear where our mouse is. Note that the border is defined as having a width of 2 and a height of 1080 in order to display as a single line separating the two images.  The border size is defined when the comparison mode is selected.
 
For the next comparison, we will focus on is the swap view mode.  This is the easiest one to implement.  In this, we show the first image in its entirety until the user places the mouse inside the canvas.  When that happens, the second image is displayed until the user moves the mouse outside of the canvas.
 
C#
private void DisplaySwapView(bool mouseOver)
{
if (!mouseOver)
{
rcImg1.Rect = new Rect(0, 0, IMGWIDTH, IMGHEIGHT);
rcImg2.Rect = new Rect(0, 0, 0, 0);
}
else
{
rcImg1.Rect = new Rect(0, 0, 0, 0);
rcImg2.Rect = new Rect(0, 0, IMGWIDTH, IMGHEIGHT);
}
}

VB
Private Sub DisplaySwapView(ByVal mouseOver As Boolean)
If (Not mouseOver) Then
rcImg1.Rect = New Rect(0, 0, IMGWIDTH, IMGHEIGHT)
rcImg2.Rect = New Rect(0, 0, 0, 0)
Else
rcImg1.Rect = New Rect(0, 0, 0, 0)
rcImg2.Rect = New Rect(0, 0, IMGWIDTH, IMGHEIGHT)
End If
End Sub

Our DisplaySwapView() method is very basic in that it basically uses the clip region to show an entire image, or to show none of the image depending on the value of the passed in bool, mouseOver.  When mouseOver is false, we show the first image, when true we show the second image.  While we could have used the Visibility property to show/hide the images, we use clipping in order to be consistent with our other modes.

The rectangle view is the last and most involved.  Since the user will be drawing a rectangle with the mouse, we must keep track of mouse movement, the state of the mouse button, and the coordinates for the rectangle.  We also need to display the rectangle as the user is drawing it and update clipped area of the image in real time in.  In order to make this easier, we create a class called MouseRectangle will contain the important information about the mouse state and rectangle size and location. 

C#
public class MouseRectangle
{
public bool MouseLeftButtonDown { get; set;}
private Rect _rtg;

private double? _startX;
private double? _startY;

public bool RectangleIsDrawn { get; set; }
public Rect Rectangle { get { return _rtg;} }

public MouseRectangle()
{
RectangleIsDrawn = false;
}

public void Reset()
{
_startX = null;
_startY = null;
RectangleIsDrawn = false;
}

public void CalculateRectangle(double x, double y)
{
if (_startX == null)
_startX = x;
if (_startY == null)
_startY = y;

// pick the smaller of the two
double left = Math.Min(_startX.Value, x);
double top = Math.Min(_startY.Value, y);

_rtg = new Rect(left, top, Math.Abs(_startX.Value - x), Math.Abs(_startY.Value - y));
}

public bool InBoundsOfRect(double x, double y)
{
if (x > _rtg.Left && x < _rtg.Right &&
y > _rtg.Top && y < _rtg.Bottom)
return true;
return false;
}

}

VB
Public Class MouseRectangle
Private privateMouseLeftButtonDown As Boolean
Public Property MouseLeftButtonDown() As Boolean
Get
Return privateMouseLeftButtonDown
End Get
Set(ByVal value As Boolean)
privateMouseLeftButtonDown = value
End Set
End Property
Private _rtg As Rect

Private _startX? As Double
Private _startY? As Double

Private privateRectangleIsDrawn As Boolean
Public Property RectangleIsDrawn() As Boolean
Get
Return privateRectangleIsDrawn
End Get
Set(ByVal value As Boolean)
privateRectangleIsDrawn = value
End Set
End Property
Public ReadOnly Property Rectangle() As Rect
Get
Return _rtg
End Get
End Property

Public Sub New()
RectangleIsDrawn = False
End Sub

Public Sub Reset()
_startX = Nothing
_startY = Nothing
RectangleIsDrawn = False
End Sub

Public Sub CalculateRectangle(ByVal x As Double, ByVal y As Double)
If Not _startX.HasValue Then
_startX = x
End If
If Not _startY.HasValue Then
_startY = y
End If

' pick the smaller of the two
Dim left As Double = Math.Min(_startX.Value, x)
Dim top As Double = Math.Min(_startY.Value, y)

_rtg = New Rect(left, top, Math.Abs(_startX.Value - x), Math.Abs(_startY.Value - y))
End Sub

Public Function InBoundsOfRect(ByVal x As Double, ByVal y As Double) As Boolean
If x > _rtg.Left AndAlso x < _rtg.Right AndAlso y > _rtg.Top AndAlso y < _rtg.Bottom Then
Return True
End If
Return False
End Function

End Class

The MouseLeftButtonDown property keeps track of whether the left button is currently being held down.  There is also a Rect (_rtg) object which contains the location and dimension of the rectangle that has been drawn.  The _startX and _startY member variables are nullable doubles used to keep track of the initial location where the mouse button was pressed. 
 
Our CalculateRectangle() method is what we use to create the dimensions and location of the rectangle.  We start by looking at the inital x,y coordinates, and if they are not set, it means the rectangle hasn't been drawn.  In that case, a 0 pixel rectangle will be created.  If those values are already set, we create the rectangle based on the x and y values passed in.
 
The InBoundsOfRect() method is a simple helper function that will tell us whether or not an x,y coordinate is within the bounds of the rectangle.
 
When in rectangle mode, the rectangle drawing begins when the mouse's left button is pressed down, and when the mouse is de-pressed, the drawing ends, and we consider the rectangle complete.
 
C#
void canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_mouseRectangle.Reset();
_mouseRectangle.MouseLeftButtonDown = true;
}

void canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
_mouseRectangle.MouseLeftButtonDown = false;
_mouseRectangle.RectangleIsDrawn = true;
}

VB
Private Sub canvas_MouseLeftButtonDown(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
_mouseRectangle.Reset()
_mouseRectangle.MouseLeftButtonDown = True
End Sub

Private Sub canvas_MouseLeftButtonUp(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
_mouseRectangle.MouseLeftButtonDown = False
_mouseRectangle.RectangleIsDrawn = True
End Sub

While in rectangle mode, when the mouse moves over the canvas, there are two different states.  If the mouse button is pressed, it means the user is drawing, and our goal is to update the rectangle to match the location where the user's mouse is in relation to the canvas.
 
C#
...
else if (_comparisonMode == ComparisonMode.Rectangle)
{
if (_mouseRectangle.MouseLeftButtonDown)
DrawRectangle(x,y);
else if (_mouseRectangle.RectangleIsDrawn)
DisplayRectangleView(x,y);
}
...

VB
...
ElseIf _comparisonMode = ComparisonMode.Rectangle Then
If _mouseRectangle.MouseLeftButtonDown Then
DrawRectangle(x,y)
ElseIf _mouseRectangle.RectangleIsDrawn Then
DisplayRectangleView(x,y)
End If
End If
...

The DrawRectangle() method starts out by calculating the rectangle based on the current x,y coordinates.  We then set the border to represent our rectangle outline by sitting its location based on the _mouseRectangle's Rectangle property.  Finally we draw the first image in its entirety, and the second image will only display the portion of the image based on the rectangle's location and width/height information.  This will result in seeing the the first image with the rectangular portion showing the corresponding 2nd image.

C#

private void DrawRectangle(double x, double y)
{
_mouseRectangle.CalculateRectangle(x,y);

Canvas.SetLeft(border, _mouseRectangle.Rectangle.Left);
Canvas.SetTop(border, _mouseRectangle.Rectangle.Top);
border.Width = _mouseRectangle.Rectangle.Width;
border.Height = _mouseRectangle.Rectangle.Height;

rcImg1.Rect = new Rect(0, 0, IMGWIDTH, IMGHEIGHT);
rcImg2.Rect = _mouseRectangle.Rectangle;
}

VB
Private Sub DrawRectangle(ByVal x As Double, ByVal y As Double)
_mouseRectangle.CalculateRectangle(x, y)

Canvas.SetLeft(border, _mouseRectangle.Rectangle.Left)
Canvas.SetTop(border, _mouseRectangle.Rectangle.Top)
border.Width = _mouseRectangle.Rectangle.Width
border.Height = _mouseRectangle.Rectangle.Height

rcImg1.Rect = New Rect(0, 0, IMGWIDTH, IMGHEIGHT)
rcImg2.Rect = _mouseRectangle.Rectangle
End Sub

When the mouse button has been de-pressed, and the rectangle has been completed, we add the ability to do a mouse over effect for the rectangular region.  While the mouse is located outside of the drawn rectangle, we leave it as is.  If the user moves the mouse within the rectangle, we want them to see what the original looked like, so we display only the first image. 

C#

private void DisplayRectangleView(double x, double y)
{
rcImg1.Rect = new Rect(0, 0, IMGWIDTH, IMGHEIGHT);

if (_mouseRectangle.InBoundsOfRect(x,y))
rcImg2.Rect = new Rect(0, 0, 0, 0);
else
rcImg2.Rect = _mouseRectangle.Rectangle;

}

VB
Private Sub DisplayRectangleView(ByVal x As Double, ByVal y As Double)
rcImg1.Rect = New Rect(0, 0, IMGWIDTH, IMGHEIGHT)

If _mouseRectangle.InBoundsOfRect(x, y) Then
rcImg2.Rect = New Rect(0, 0, 0, 0)
Else
rcImg2.Rect = _mouseRectangle.Rectangle
End If

End Sub

 

Conclusion & Future Work

Overall, the blu-ray DVD comparison utility is a pretty straight-forward Silverlight application that you can use to see the differences between two images.  These images don't even need to be from blu-ray or DVD sources, but as long as they are the right dimension, a user can use it to see subtle differences between the two by allowing three different comparison modes.  For blu-ray purchases when you already own the DVD, I think it helps to see what kind of benefit you will get by moving to blu-ray.  It has been my experience that almost every blu-ray offers some type of picture upgrade over its DVD counterpart (and sound upgrade as well).  Sometimes the differences are subtle, sometimes they are incredibly better.  There are a lot of factors involved, but 

Some future work that can be done to this is adding the ability to zoom in and out on the images in order to be able to see differences in a closer view.   Soon there will be more movies which will be re-released on blu-ray and you can use the tool to compare the old release to the new one.  This will require zooming to see some of the more finer differences.  Other work can be done by creating a database to hold links, and have a control that allows a user to navigate to different movies and view different comparisons.  I have started doing both of these, but they were not ready for this article.

One definite area that needs work is the UI.  I am not good at laying out user interfaces or creating new xaml styles, so I left them default. 

What would be really useful is a full motion comparison where you can use similar modes to see how two movies compare while in motion, but the biggest problem would be finding or creating the comparison material.  Playback would be limiting as well since it requires higher end CPUs/video cards.  

Comparisons

Here are a few URLs you can use to play with the comparison utility:

  URL of Standard Def image (DVD) / HD (Blu-ray)
Prince Caspian http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9515699/pc_1_sd.png
http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9515699/pc_1_hd.png
Prince Caspian http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9515699/pc_2_sd.png
http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9515699/pc_2_hd.png
Iron Man http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9515699/im_1_sd.png
http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9515699/im_1_hd.png
Iron Man http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9515699/im_2_sd.png
http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9515699/im_2_hd.png
Entrapment http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9515699/Ent_4_SD.png
http://ecn.channel9.msdn.com/o9/c4fcontent/migration/9515699/Ent_4_HD.png

Note: Images were copied from the blu-ray threads of A/V Science Forum: www.avsforum.com .

Thanks

I would like to thank Brian Peek for introducing me to Coding4Fun, and taking the time to review my article and test the code.

Tags:

Follow the Discussion

  • orviorvi

    I didn't know that it is so different.

    nice one Smiley

  • Clint RutkasClint I'm a "developer"

    @orvi just remember, the difference is there if you have a high def TV with Componet or HDMI cords.  If you don't have either, High Def DVDs (bluray or HD DVD) or HD streaming won't look as clear.

Remove this comment

Remove this thread

close

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.