Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Stereoscopy

0.00/5 (No votes)
18 Mar 2009 1  
This is a simple program to explain how 3D stereoscopic systems work, also with some fun!
Sample Image

Contents

Introduction

Do you like some fun? If you had a busy day and want some distraction from your work, this is for you!

The logic behind Stereoscopy is very simple, but the results are amazing and amusing! Especially when you realize that you can make your own 3D environments with only 2D objects without any difficulties.

When you work with professional 3D software like 3D Studio or Maya, you work on a 3D environment, and see the results on your monitor in two dimensions (usually), but here it's different. It means that you work on a two dimensional environment, but the results are totally in 3D.

What is Stereoscopy?

Stereoscopy is a technique for viewing pictures in three dimensions; when you are looking at a stereogram, you can imagine that you are viewing the real scene from a window. Size, depth, and distance are perceptible as when viewing the original.

How Is It Possible?

Our eyes are separated by a distance of about 6-7 cm. It makes a difference in the point of view of each eye, and therefore the aspect of every scene is slightly different in the eyes. When these two different pictures fuse in the brain, it makes a 3D scene.

Whereas 3D viewing is based on differences, you need two shots of the subject to realize the original scene. These shots must be captured in a configuration like in normal eyes, from two positions on the same surface with about 5-10 cm distance and also parallel.

I simulate the position of eyes and the view of each eye by using two cameras, as shown below:

And, you can see the results in the following figures, the shots are slightly different:

Left eye view Right eye view

How It Works?

What's the logic behind these pair images that our brain could realize the 3D depth?

To find some reasons, I wanted to work on a real subject. I prepared a pair of stereo images that you can see here:

And then, I put the right picture on the left (with 50% transparency), and the result is shown in the following figure:

As you can see, some shapes are in the same places. The mushroom in both the pictures is in the same place, but others are in different places. Some of them are nearer, and some of them are farther from each other.

The mushroom is on the basement of the 3D scene, because it is in the same place in both pictures; it means that if you have some objects in the same place in both pictures, they are in the basement of the scene.

The globe in the left picture has more distance from the border (the red box shows the amount of difference). This will be shown closer to you than the basement objects when you see the depth.

The difference between the hearts is more than that of the globes (compare the red boxes), so the heart is closer to you than others.

What about the fish? There are differences in the places of the fishes, but something is different. The red box is in the right picture. It means, the right fish is farther from the border than the left one. So, what will happen in the 3D scene?

In this situation, the object is placed behind the basement; it means the fish is the farthest object in this scene.

We have found the logic. Now, we can make any 3D scene with different objects in different depths, just by making them horizontally different in their places on the pair images; and, this is the logic that I used in this software.

Using the Code

The algorithm of the program is very simple, and there is no complexity. If I want to show it in a flowchart, it'd be something like this:

Now, I will try to explain some parts of the source code.

At the start of the program, I declare some variables:

private frmBackgrounds myfrmBackgrounds = new frmBackgrounds();
private List<string> imageTypes =
  new List<string>(new string[] { "*.gif", "*.png", "*.jpg", "*.bmp" });
private List<string> someWords;
private List<img> picsBackground;
private List<img> picsSmily;
private List<img> picsSimple;
private List<img> pics3DLeft;
private List<img> pics3DRight;

private class  picCollection
{
    internal  Image myImage=null ;
    internal  Image myImage2=null ;
    internal  int myX=0;
    internal  int myY=0;
    internal  float myScale=0;
    internal  int myDepth=0;
}
private picCollection myPicInfo;
private List<piccollection> myAllPicCollection;

Random myRandom = new Random();
int xChange = 1;

Then, I load some pictures from internal resources:

// add some pictures from internal resources
picsBackground.Add(Properties.Resources.back1_150_L);
picsBackground.Add(Properties.Resources.back4_100_L );
picsBackground.Add(Properties.Resources.back6_100_L );
...

And, also some words:

// add some words
someWords = new List<string>(new string[] { "I", "You", "He", "She", "We", "They" });
someWords.AddRange(new string[] { "My", "Your", "His", "Her", "Our", "Their" });
...

I now add extra pictures from the hard disk:

// load pictures from the hard disk
private void loadFromHard(string myDir, List<img > myPicList, List<img > myPicList2)
{
    DirectoryInfo myFileDir = new DirectoryInfo(Application.StartupPath +"\\" + myDir);
    if (myFileDir.Exists)
    {
        // For each image extension (.jpg, .bmp, etc.)
        foreach (string imageType in imageTypes)
        {
            // all graphic files in the directory
            foreach (FileInfo myFile in myFileDir.GetFiles(imageType))
            {
                // add image
                try
                {
                    if (myPicList2 != null)
                    {
                        Image image2 = Image.FromFile(myFile.FullName + ".right");
                        myPicList2.Add(image2);
                    }

                    Image image = Image.FromFile(myFile.FullName);
                    myPicList.Add(image);
                }
                catch (OutOfMemoryException)
                {
                    continue;
                }
            }
        }
    }
}

Now, the program is ready. You can just click on the Run button, or change some controls and then press the button.

The GUI is very simple.

And, when you Run, initially, it declares a graphic situation:

Graphics gLeft;
gLeft = picLeft3D.CreateGraphics();
gLeft.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
gLeft.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
gLeft.Clear(lblBackgroundColor.BackColor);
...

FontStyle myFontStyle = FontStyle.Regular ;
if(chkFontBold.Checked ) myFontStyle =  FontStyle.Bold;
if (chkFontItalic.Checked) myFontStyle = myFontStyle |FontStyle.Italic;
Font myFont = new Font(txtFont.Text.ToString(), (float)updFontSize.Value,
myFontStyle, GraphicsUnit.Pixel);
Brush myBrush, myBrushShadow;
...

And then, it shows a background texture or pure color:

if (chkRandomize.Checked)
{
    if (rdoBackgroundPic.Checked)
    {
        picBackground.BackgroundImage =
          picsBackground[myRandom.Next(picsBackground.Count)];
        picLeft3D.BackgroundImage = picBackground.BackgroundImage;
    }
    else
        lblBackgroundColor.BackColor = Color.FromArgb(myRandom.Next(255),
                                       myRandom.Next(255), myRandom.Next(255));
}
else
{
    if (rdoBackgroundPic.Checked)
        picLeft3D.BackgroundImage = picBackground.BackgroundImage;
}
picLeft3D.BackColor = lblBackgroundColor.BackColor;

After that, the program makes several random objects and words, and collects their information in a List. Consider that the myDepth property is the important part, and the only difference that it makes is in the X coordinates of the objects.

...
// selecting Simple pictures with different size for painting on the scene
if (chkSimpleObjects.Checked)
{
    if (picsSimple.Count > 0)
    {
        for (int i = 1; i <= updSimpleObject.Value; i++)
        {
            myPicInfo = new picCollection();
            myPicInfo.myImage = picsSimple[myRandom.Next(picsSimple.Count)];
            myPicInfo.myImage2 = myPicInfo.myImage;
            myPicInfo.myDepth = myRandom.Next(1, trbDepthShapes.Value / 2);
            myPicInfo.myScale = (float)(myRandom.NextDouble() * .7 + .4);
            myPicInfo.myX = (myRandom.Next(myPicInfo.myDepth, picLeft3D.Width -
                             myPicInfo.myImage.Width - myPicInfo.myDepth) %
                             picLeft3D.Width) + myPicInfo.myDepth;
            myPicInfo.myY = myRandom.Next(picLeft3D.Height - 100  - limitY);
            myAllPicCollection.Add(myPicInfo);
        }
    }
}
...

And finally, the program shows the objects and words on the scene in the order of depth:

// drawing all objects of the collection in order by depth on the scene
for (int d = 0; d < 120; d++)
{
    foreach (picCollection myP in myAllPicCollection)
    {
        if (d == myP.myDepth) DrawPicture(gLeft,gRight , myP);
    }

        if (chkShadow.Checked && chkShadow.Enabled && d ==
				(int)(trbDepthText.Value / 2 - 2))
            DrawText(txtMain.Text, gLeft,gRight  ,
                     10 + d, 20, d, new SolidBrush(lblShadow.BackColor), myFont, 4);
        if (chkText.Checked && d == (int)(trbDepthText.Value / 2))
            DrawText(txtMain.Text, gLeft, gRight , 10 + d, 20, d,
                     new SolidBrush(lblTextColor.BackColor), myFont, 0);
    }

...

// Draw Graphics on the scene
private void DrawPicture(Graphics gLeft,Graphics gRight, picCollection myP)
{
    gLeft.DrawImage(myP.myImage ,myP. myX,myP.  myY,myP.myImage.Width *myP. myScale,
                    myP. myImage.Height *myP. myScale);
    gRight.DrawImage(myP.myImage2 ,myP. myX -myP.myDepth,myP. myY,
                     myP. myImage2.Width *myP. myScale,
			myP. myImage2.Height *myP. myScale);
}

// Draw Text on the scene
private void DrawText(string myText,Graphics gLeft,Graphics gRight,
                      int myX, int myY,int myDepth, Brush myBrush,
                      Font myFont,int shaDow)
{
    gLeft.DrawString(myText , myFont, myBrush, myX + shaDow, myY  + shaDow);
    gRight.DrawString(myText , myFont, myBrush, myX+ shaDow - myDepth, myY + shaDow  );
}

That's it, enjoy!

Points of Interest

You can use your own pictures and textures in this program. There are four directories in the place of the executable file, as shown below:

  • PicBackground: The files in this directory will be used as the background texture.
  • PicSmily: The files in this directory will be used in different depths of the 3D scene with the original size, so I used only small pictures in this directory.
  • PicSimple: These files will appear in different sizes on the scene, files with greater than 120x120 pixels are not recommended.
  • Pic3D: In this category, we have two pictures for each object, one for the left and one for the right. These pictures are slightly different that makes the subject appear 3D.

How Can You View Stereoscopic Pictures?

There are different methods for this, but this article is based on Parallel View and Cross Eye View.

1. Parallel View

This is a very simple method, and you don't need any devices. If you are new to the stereoscopic subject, start with this method with original window size of program (don't maximize it!). Pair images must be placed side by side (left picture in the left and the right one in the right), and when you are looking at them, you don't have to focus, just relax your eyes like you want to see far (your eyes must be almost parallel). After a few seconds, you see a blur picture, and gradually, it becomes sharp, and then you see the 3D scene with full depth.

Normal view Parallel view

In this software, there are 2 red bullets on top of the pair images to help you in faster viewing.

When you are looking at the pair images (without focusing on them), after a few seconds, the bullets move together and finally make only one. At this time, you will see three images, one sharp and clean image in the center, and two nebulous ones in the corners. Our 3D scene is the center one.

This method has some limits, I don't think you can see a full screen parallel pair images, because viewing subjects wider than your inter-ocular distance is very difficult and you have to try other methods.

2. Cross Eye View

This is much better than the Parallel, but for the first time, it seems difficult. In this method, you can see a pair images the size of a wall! Also, depth perception is further and faster. I recommend this method and making the program full screen.

In this method, you must change left and right pictures (left picture in the right and right one in the left), your focus point is somewhere between images and your eyes.

Initially maximize the window size and click the main button to make some new objects.

Hold a pen in front of the monitor screen, on the middle line and a few lower than the top bullets.

Focus on the tip of the pen, move it gently towards your face and hold your focus on it. At the same time, consider the behind images (especially 2 blue bullets).

You will see that 2 bullets move together and became 4 bullets.

In a special distance of the pen between monitor and your eyes, the central bullets became one.

This new bullet is not sharp. Stop the pen and after a few seconds, look at the new bullet and other pictures around it, initially they are blur. Don't hurry, let your eyes adapt gently. It will be sharp and 3D soon. If you lost your new focal point and your eyes returned to normal view, then start again.

There is a noticeable condition. You have to see both bullets horizontally equal. If one of them is above the other (see figure), then turn your head slightly to view them in the same horizon.

Although cross eye view is difficult for the first time, with a bit of experience, this is the fastest and the best method.

3. Anaglyph Method

This method needs special Red-Cyan glasses. I will explain it in the future.

History

  • First release (Dec 2, 2008): Based on the Parallel view method
  • Update 1 (Dec 12, 2008): Added Cross eye method and support for different sizes of program window
  • Update 2 (Jan 15, 2009): Updated source and demo project
  • Update 3 (Feb 11, 2009): Added Timer
  • Update 4 (Feb 20, 2009): Added Save and AutoSave options
  • Update 5 (Mar 18, 2009): Updated source and demo project

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here