lorenzo373
lorenzo373

Reputation: 434

Captured photo has vertical stripes

I'm capturing a photo using the WinRT Mediacapture class, but when I take the picture it gets weird transparent stripes, it's kind of hard to explain so here's some pictures:

Before capturing picture (Previewing)

Before capturing picture (Previewing)

After taking picture

After taking picture

I have seen other people with kind of the same problem around here (like here, but the solutions to them didn't seem to work for me. (either no result, or the photo got messed up)

Code I use for setting the resolution:

System.Collections.Generic.IEnumerable<VideoEncodingProperties> available_resolutions = captureManager.VideoDeviceController.GetAvailableMediaStreamProperties(MediaStreamType.Photo).Select(x => x as VideoEncodingProperties);

foreach (VideoEncodingProperties resolution in available_resolutions)
{
     if (resolution != null && resolution.Width == 640 && resolution.Height == 480) //(resolution.Width==1920 && resolution.Height==1080) //resolution.Width==640 && resolution.Height==480)
     {
           await captureManager.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.Photo, resolution);
     }
}

Code I'm using for taking the photo:

private async Task<BitmapImage> ByteArrayToBitmapImage(byte[] byteArray)
    {
        var bitmapImage = new BitmapImage();

        using (var stream = new InMemoryRandomAccessStream())
        {
            await stream.WriteAsync(byteArray.AsBuffer());
            stream.Seek(0);

            await bitmapImage.SetSourceAsync(stream);

            await stream.FlushAsync();
        }

        return bitmapImage;
    }

    /// <summary>
    /// Relayed Execute method for TakePictureCommand.
    /// </summary>
    async void ExecuteTakePicture()
    {
        System.Diagnostics.Debug.WriteLine("Started making picture");
        DateTime starttime = DateTime.Now;

        ImageEncodingProperties format = ImageEncodingProperties.CreateJpeg();

        using (var imageStream = new InMemoryRandomAccessStream())
        {
            await captureManager.CapturePhotoToStreamAsync(format, imageStream);

            //Compresses the image if it exceedes the maximum file size
            imageStream.Seek(0);

            //Resize the image if needed
            uint maxImageWidth = 640;
            uint maxImageHeight = 480;

            if (AvatarPhoto)
            {
                maxImageHeight = 200;
                maxImageWidth = 200;

                //Create a BitmapDecoder from the stream
                BitmapDecoder resizeDecoder = await BitmapDecoder.CreateAsync(imageStream);

                if (resizeDecoder.PixelWidth > maxImageWidth || resizeDecoder.PixelHeight > maxImageHeight)
                {
                    //Resize the image if it exceedes the maximum width or height
                    WriteableBitmap tempBitmap = new WriteableBitmap((int)resizeDecoder.PixelWidth, (int)resizeDecoder.PixelHeight);
                    imageStream.Seek(0);
                    await tempBitmap.SetSourceAsync(imageStream);
                    WriteableBitmap resizedImage = tempBitmap.Resize((int)maxImageWidth, (int)maxImageHeight, WriteableBitmapExtensions.Interpolation.Bilinear);
                    tempBitmap = null;

                    //Assign to imageStream the resized WriteableBitmap
                    await resizedImage.ToStream(imageStream, BitmapEncoder.JpegEncoderId);
                    resizedImage = null;
                }

                //Converts the final image into a Base64 String
                imageStream.Seek(0);
            }

            //Converts the final image into a Base64 String
            imageStream.Seek(0);

            BitmapDecoder decoder = await BitmapDecoder.CreateAsync(imageStream);

            PixelDataProvider pixels = await decoder.GetPixelDataAsync();
            byte[] bytes = pixels.DetachPixelData();

            //Encode image
            InMemoryRandomAccessStream encoded = new InMemoryRandomAccessStream();
            BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, encoded);

            encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore, maxImageWidth, maxImageHeight, decoder.DpiX, decoder.DpiY, bytes);

            //Rotate the image based on the orientation of the camera
            if (currentOrientation == DisplayOrientations.Portrait)
            {
                encoder.BitmapTransform.Rotation = BitmapRotation.Clockwise90Degrees;
            }
            else if (currentOrientation == DisplayOrientations.LandscapeFlipped)
            {
                encoder.BitmapTransform.Rotation = BitmapRotation.Clockwise180Degrees;
            }

            if (FrontCam)
            {
                if (currentOrientation == DisplayOrientations.Portrait)
                {
                    encoder.BitmapTransform.Rotation = BitmapRotation.Clockwise270Degrees;
                }
                else if (currentOrientation == DisplayOrientations.LandscapeFlipped)
                {
                    encoder.BitmapTransform.Rotation = BitmapRotation.Clockwise180Degrees;
                }
            }

            await encoder.FlushAsync();
            encoder = null;

            //Read bytes
            byte[] outBytes = new byte[encoded.Size];
            await encoded.AsStream().ReadAsync(outBytes, 0, outBytes.Length);

            encoded.Dispose();
            encoded = null;

            //Create Base64
            image = await ByteArrayToBitmapImage(outBytes);
            System.Diagnostics.Debug.WriteLine("Pixel width: " + image.PixelWidth + " height: " + image.PixelHeight);
            base64 = Convert.ToBase64String(outBytes);

            Array.Clear(outBytes, 0, outBytes.Length);
            await imageStream.FlushAsync();
            imageStream.Dispose();
        }

        DateTime endtime = DateTime.Now;
        TimeSpan span = (endtime - starttime);

        //Kind of a hacky way to prevent high RAM usage and even crashing, remove when overal RAM usage has been lowered
        GC.Collect();

        System.Diagnostics.Debug.WriteLine("Making the picture took: " + span.Seconds + " seconds");

        if (image != null)
        {
            RaisePropertyChanged("CapturedImage");

            //Tell both UsePictureCommand and ResetCommand that the situation has changed.
            ((RelayedCommand)UsePictureCommand).RaiseCanExecuteChanged();
            ((RelayedCommand)ResetCommand).RaiseCanExecuteChanged();
        }
        else
        {
            throw new InvalidOperationException("Imagestream is not valid");
        }
    }

If there is any more information needed feel free to comment, I will try to put it out as fast as possible, thanks for reading.

Upvotes: 1

Views: 237

Answers (1)

Mike
Mike

Reputation: 2260

The aspect ratio of the preview has to match the aspect ratio of the captured photo, or you'll get artifacts like that one in your capture (although it actually depends on the driver implementation, so it may vary from device to device).

  1. Use the MediaCapture.VideoDeviceController.GetMediaStreamProperties() method on the MediaStream.VideoPreview.
  2. Get the resulting VideoEncodingProperties, and use the Width and Height to figure out the aspect ratio.
  3. Call MediaCapture.VideoDeviceController.GetAvailableMediaStreamProperties() on the MediaStream.Photo, and find out which ones have an aspect ratio that matches (I recommend using a tolerance value, something like 0.015f might be good)
  4. Out of the ones that do, choose which one better suits your needs, e.g. by paying attention to the total resolution W * H
  5. Apply your selection by calling MediaCapture.VideoDeviceController.SetMediaStreamPropertiesAsync() on the MediaStream.Photo and passing the encoding properties you want.

More information in this thread. An SDK sample is available here.

Upvotes: 2

Related Questions