Wiimote Virtual Reality Desktop

In this article, Giovanni Montrone will provide an overview on how to create an image comparison application for DVD and Blu-ray screen captures. | |
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
|
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.
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.
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.
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" />
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>
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;
}
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
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)
{
// ...
}
}
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
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
}
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
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);
}
}
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
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.
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;
}
}
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
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;
}
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
...
else if (_comparisonMode == ComparisonMode.Rectangle)
{
if (_mouseRectangle.MouseLeftButtonDown)
DrawRectangle(x,y);
else if (_mouseRectangle.RectangleIsDrawn)
DisplayRectangleView(x,y);
}
...
...
ElseIf _comparisonMode = ComparisonMode.Rectangle Then
If _mouseRectangle.MouseLeftButtonDown Then
DrawRectangle(x,y)
ElseIf _mouseRectangle.RectangleIsDrawn Then
DisplayRectangleView(x,y)
End If
End If
...
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;
}
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
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;
}
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
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.
Here are a few URLs you can use to play with the comparison utility:
Note: Images were copied from the blu-ray threads of A/V Science Forum: www.avsforum.com .
I would like to thank Brian Peek for introducing me to Coding4Fun, and taking the time to review my article and test the code.