/// <summary> /// Takes a .vr.jpg file, extracts the individual eye images and recombine them into a full omni-stereo image. /// </summary> public static Bitmap CreateStereoEquirectangular(string filename, EyeImageGeometry geometry = EyeImageGeometry.OverUnder, bool fillPoles = true, int maxWidth = 0) { var xmpDirectories = VrJpegMetadataReader.ReadMetadata(filename); GPanorama pano = new GPanorama(xmpDirectories.ToList()); Bitmap left = new Bitmap(filename); Bitmap right = ExtractRightEye(pano); if (right == null) { left.Dispose(); return(null); } Bitmap leftEquir = Equirectangularize(left, pano, fillPoles, maxWidth); Bitmap rightEquir = Equirectangularize(right, pano, fillPoles, maxWidth); Bitmap composite = ComposeEyes(leftEquir, rightEquir, geometry); left.Dispose(); right.Dispose(); leftEquir.Dispose(); rightEquir.Dispose(); return(composite); }
/// <summary> /// Extracts the embedded image from the panorama and returns it as a Bitmap. /// Note: the left eye can be extracted simply by loading the original file into a regular Bitmap. /// </summary> public static Bitmap ExtractRightEye(GPanorama pano) { if (pano.ImageData == null) { return(null); } Bitmap bmp = null; using (var stream = new MemoryStream(pano.ImageData)) bmp = new Bitmap(stream); return(bmp); }
/// <summary> /// Takes a single eye Bitmap and creates a fully spherical equirectangular image, /// with the pano painted at the correct latitude-longitude. /// Optionally fills in the poles. /// </summary> public static Bitmap Equirectangularize(Bitmap bitmap, GPanorama pano, bool fillPoles = true, int maxWidth = 0) { if (bitmap == null) { return(null); } Rectangle crop = new Rectangle(pano.PanoCroppedAreaLeftPixels, pano.PanoCroppedAreaTopPixels, pano.PanoCroppedAreaImageWidthPixels, pano.PanoCroppedAreaImageHeightPixels); int imgWidth = pano.PanoFullPanoWidthPixels; int imgHeight = pano.PanoFullPanoHeightPixels; if (maxWidth > 0 && imgWidth > maxWidth) { float ratio = (float)maxWidth / imgWidth; imgWidth = maxWidth; imgHeight = imgWidth / 2; // In case of subpixel coordinate, expand the rectangle. crop = new Rectangle((int)Math.Floor(crop.Left * ratio), (int)Math.Floor(crop.Top * ratio), (int)Math.Ceiling(crop.Width * ratio), (int)Math.Ceiling(crop.Height * ratio)); } Bitmap result = new Bitmap(imgWidth, imgHeight, bitmap.PixelFormat); Graphics g = Graphics.FromImage(result); g.DrawImage(bitmap, crop); g.Dispose(); if (fillPoles) { PoleFiller.Fill(result, crop); } return(result); }