I am using MediaCapture along with CapturePreview to create a preview stream on a Surface Pro tablet. I am taking photos periodically with a timer using
CapturePhotoToStreamAsync()
Whenever a photo is taken though, the preview stream zooms out slightly as if it is changing the resolution or aspect ratio or something similar.
I found this issue, which sounds exactly the same, but I can't seem to resolve it regardless of the resolutions set.
This is the bit of code I have to grab Bitmap objects from the stream:
using (var randomAccessStream = new InMemoryRandomAccessStream())
{
await mediaCap.CapturePhotoToStreamAsync(imageProps, randomAccessStream);
randomAccessStream.Seek(0);
using (var ioStream = randomAccessStream.AsStream())
{
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = ioStream;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
bitmapImage.Freeze();
}
}
imageProps is being set to 1280x720, the same resolution as the preview object. This wouldn't be a huge issue if it only happened occasionally, but I need to grab frames frequently (multiple times per second), and it looks very jarring.
Edit:
Something to note is that the issue only occurs on the mentioned Surface Pro tablet (2736x1824 #200%), it doesn't appear to occur on a 1920x1080 screen.
The solution I ended up finding was this change:
From:
await _mediaCaptureObject.InitializeAsync(new MediaCaptureInitializationSettings
{
VideoDeviceId = deviceList?.FirstOrDefault(x => x.Name.Contains("Camera Name"))?.Id,
StreamingCaptureMode = StreamingCaptureMode.Video
});
To:
await _mediaCaptureObject.InitializeAsync(new MediaCaptureInitializationSettings
{
VideoDeviceId = deviceList?.FirstOrDefault(x => x.Name.Contains("Camera Name"))?.Id,
StreamingCaptureMode = StreamingCaptureMode.Video,
PhotoCaptureSource = PhotoCaptureSource.VideoPreview
});
This doesn't really explain the issue I was seeing, since I was setting the ImageEncodingProperties to the same resolution as the stream when capturing the photo. Anyway, the default PhotoCaptureSource is Auto, which must've been using the Photo mode instead of VideoPreview. Once I changed this, the issue stopped appearing.
Related
Is there any way of converting Stream to Image?
I tried Bitmap, but it states that I don't have System.Drawing... so I tried this:
var bitMap = new BitmapImage();
bitMap.SetSourceAsync(stream);
image.Source = bitMap;
EDIT:
I am trying to build UWP app + using VS 2015.
2 - It just states that System.Drawing does not exist in the namespace.
EDIT2:
Ok, I might have explained it wrong. The idea is: I have an Image, and I want to change its source to something different and then for it to reload, so I can see the image.
The image is effectively a "Stream", so I assume I need to convert it to Bitmap and then load somehow.
EDIT3:
Ok, so I think it will be easier to describe and then use the code above:
There is a picture box and I am using:
var stream = new InMemoryRandomAccessStream();
await mediaCapture.CapturePhotoToStreamAsync(ImageEncodingProperties.CreateJpeg(), stream);
Now I would like this Captured Photo to be displayed as an Image. ( Image "box" is created at the start, so the idea is to change source).
Right, so I managed to fix it:
var bitMap = new BitmapImage();
stream.seek(0); // LINE ADDED
bitMap.SetSourceAsync(stream);
image.Source = bitMap;
I turned out that the error that was being produced was : "The component cannot be found.", so I managed to fix it by using this trick.
I am not sure if this is what you are looking for but if you would like to use stream with BitMapImage you should use:
var image = new BitmapImage();
await image.SetSourceAsync(stream);
For instance when you have your photo stored as a byte[] array you can use the stream to convert it to image:
using (InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream())
{
DataWriter writer = new DataWriter(stream.GetOutputStreamAt(0))
writer.WriteBytes(<<here your byte[] array>>);
await writer.StoreAsync();
var image = new BitmapImage();
await image.SetSourceAsync(stream);
}
Is that what you need?
Can any of you provide an actual working sample of how to take and save a photo using the MediaCapture element. I've tried looking for an actual solution in MSDN but none of those explanations or code actually describe the process in a simple way.
I need to take a picture and save it to my library (i need to show the correct preview for this), however right now it is rotated 90 degrees and i can't adjust it. I've tried setting the rotation of the video preview and it works for the preview however when i do this the aspect ratio its all wrong and the saved image its not correct.
The examples from channel 9 kind of suck too. I just need a simple implementation...
Im using a Runtime app NOT a silverlight app for Windows Phone 8.1.
I have had the same issue, SetRecordRotation doesn't work for me. I found workaround - take photo and rotate an image, it works great. I use method like that:
private async void CapturePhoto()
{
string photoPath = string.Empty;
ImageEncodingProperties format = ImageEncodingProperties.CreateJpeg();
using (var imageStream = new InMemoryRandomAccessStream())
{
await MediaCapture.CapturePhotoToStreamAsync(format, imageStream);
BitmapDecoder dec = await BitmapDecoder.CreateAsync(imageStream);
BitmapEncoder enc = await BitmapEncoder.CreateForTranscodingAsync(imageStream, dec);
enc.BitmapTransform.Rotation = BitmapRotation.Clockwise90Degrees;
await enc.FlushAsync();
StorageFolder folder = ApplicationData.Current.LocalFolder;
StorageFile capturefile = await folder.CreateFileAsync("photo.jpg", CreationCollisionOption.GenerateUniqueName);
photoPath = capturefile.Name;
using (var fileStream = await capturefile.OpenAsync(FileAccessMode.ReadWrite))
{
try
{
await RandomAccessStream.CopyAsync(imageStream, fileStream);
}
catch {}
}
}
}
I modified sample of code from article How to capture a photo in your Windows Phone 8.1 Runtime app by Marco Siccardi
http://dotnet.dzone.com/articles/how-capture-photo-your-windows-0
There are two samples posted on the Microsoft github page that are relevant, although they target Windows 10. Still, the APIs should work on 8/8.1.
GetPreviewFrame: This sample will not lock the page rotation, and apply a corrective rotation to the preview stream. It does not use SetPreviewRotation, as that method is more resource-heavy than using the metadata approach. This sample doesn't capture photos (just preview frames)
UniversalCameraSample: This one does capture photos, and supports portrait and landscape orientations. Here is the relevant part:
var stream = new InMemoryRandomAccessStream();
try
{
await _mediaCapture.CapturePhotoToStreamAsync(ImageEncodingProperties.CreateJpeg(), stream);
var photoOrientation = ConvertOrientationToPhotoOrientation(GetCameraOrientation());
await ReencodeAndSavePhotoAsync(stream, photoOrientation);
}
catch (Exception ex)
{
Debug.WriteLine("Exception when taking a photo: {0}", ex.ToString());
}
With:
private static async Task ReencodeAndSavePhotoAsync(IRandomAccessStream stream, PhotoOrientation photoOrientation)
{
using (var inputStream = stream)
{
var decoder = await BitmapDecoder.CreateAsync(inputStream);
var file = await KnownFolders.PicturesLibrary.CreateFileAsync("SimplePhoto.jpeg", CreationCollisionOption.GenerateUniqueName);
using (var outputStream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
var encoder = await BitmapEncoder.CreateForTranscodingAsync(outputStream, decoder);
var properties = new BitmapPropertySet { { "System.Photo.Orientation", new BitmapTypedValue(photoOrientation, PropertyType.UInt16) } };
await encoder.BitmapProperties.SetPropertiesAsync(properties);
await encoder.FlushAsync();
}
}
}
Have a closer look at the sample to see how to get the orientation of the camera in the first place (a call to it is being made in the first snippet I posted).
Or, if you prefer a video, you can watch the camera session from the recent //build/ conference, which includes a little bit of a walkthrough through some camera samples.
you can change the aspect ratio for your video preview & captured photo by setting in the MediaCapture.VideoDeviceController.
Also, you can set your video preview upright by using the following code.
MediaCapture.SetPreviewRotation(VideoRotation.Clockwise90Degrees);
I have answered a similar questions in the another post in the link below. Hope it helps.
https://stackoverflow.com/a/29875992/4672579
I have written a little game using IronPython and WPF for didactic purpose and now I want to translate the project to Metro APP for test Shared Projects.
The guilty code is:
def LoadImage(name, sourceRect):
bmp = BitmapImage()
bmp.BeginInit()
bmp.UriSource = Uri("./data/images/" + name, UriKind.Relative)
bmp.SourceRect = sourceRect
bmp.EndInit()
image = Image()
image.Source = bmp
return image
How on the earth I can obtain the same result in a Metro app (using C#)? There must be a way to do this in a simple manner like old BitmapImage. I need this because I have tiled images and I want a portion of it to display. WriteableBitmap work but ignore transparency of the image, so it's useless.
I've been using this code to load an image scaled:
public static async Task SetSourceAsync(
this WriteableBitmap writeableBitmap,
IRandomAccessStream streamSource,
uint decodePixelWidth,
uint decodePixelHeight)
{
var decoder = await BitmapDecoder.CreateAsync(streamSource);
using (var inMemoryStream = new InMemoryRandomAccessStream())
{
var encoder = await BitmapEncoder.CreateForTranscodingAsync(inMemoryStream, decoder);
encoder.BitmapTransform.ScaledWidth = decodePixelWidth;
encoder.BitmapTransform.ScaledHeight = decodePixelHeight;
await encoder.FlushAsync();
inMemoryStream.Seek(0);
await writeableBitmap.SetSourceAsync(inMemoryStream);
}
}
You might use something similar, but you'd specify encoder.BitmapTransform.Bounds instead of ScaledWidth/Height to do the cropping. If you have more specific questions - please clarify.
i m creating a app in which I need to change the source of the image on button click.eg if the images are:
sample1.png, sample2.png, sample3.png ...
I have written this code on button click:
int count=1;
imagename.Source=new BitmapImage(new uri("/sample"+count+".png",uriKind.Relative));
but the problem is when I run the app on a device it takes some time to load the image source everytime the button is clicked whereas on the emulator it changes properly.is there any way to reduce the loading time on device?
is there any way to reduce the loading time on device?
As far as I know: no. If performance is unsatisfactory, you may want to try with some caching. Basically, instead of creating a new BitmapImage each time, re-use the old ones.
First, pre-load the bitmaps. Don't forget to set the CreateOptions property, otherwise the picture won't be loaded until you assign it to an actual Image control:
var bitmaps = new List<BitmapImage>(count);
for (int i = 0; i < count; i++)
{
var bitmap = new BitmapImage(new uri("/sample" + i + ".png",uriKind.Relative));
bitmap.CreateOptions = BitmapCreateOptions.None;
bitmaps.Add(bitmap);
}
Then, re-use them as needed:
imagename.Source = bitmaps[1];
Please be aware that it will increase the memory usage of your app, so don't do that with large pictures. Performance is often a compromise between CPU time and memory usage.
Like KooKiz said you can prefetch the image but to force the load of the images, I believe you will need to use SetSourceAsync, here is an example:
StorageFile file= await StorageFile.GetFileFromApplicationUriAsync(new Uri("appx-data:////sample" + i + ".png"));
using (var stream = await file.OpenReadAsync())
{
bitmap.SetSourceAsync(stream);
}
Also what could be possible to do is for you to preload a thumbnail version of the image first (by using file.GetThumbnailAsync for example) and then the full image latter.
Finally if the images you are loading are actually bigger than the resolution of the surface you are loading it on, another parameter you can set on the Bitmap object is DecodePixelHeight and DecodePixelWidth.
int count=1;
BitmapImage bmp=new BitmapImage();
StorageFile sFile = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync(#"Assets\Images\img" + count + ".png");
var fileStream = await sFile.OpenReadAsync();
await bmp.SetSourceAsync(fileStream);
I'm working on a Metro application where I need to generate an animated GIF image.
I've found this tutorial, witch seems to be the one and only resource on animated GIFs for Metro apps.
When running this code, an Exception is thrown on the SetPixelData method, telling me that the allocated buffer memory is insufficient (The message is in my OS language even though my Visual Studio environnement is in English, I think it might be relevant).
I've reduced image size (source and output) and frame number, but I still get this error. (I manipulate way bigger images and byte array in the same application).
Any idea where this memory problem can come from ? A problem with my StorageFile maybe ?
I was seeing this exception when the frameWidth\Height being passed into SetPixelData didn't match the pixel data.
I ended up with this example below. I was seeing the exception you mentioned when the dimensions didn't match the pixelData.
I think this is more stable in Windows 8.1 as it doesn't repro on it.
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(sourceStream);
BitmapTransform transform = new BitmapTransform()
{
ScaledHeight = 900,
ScaledWidth = 600
};
PixelDataProvider pixelData = await decoder.GetPixelDataAsync(BitmapPixelFormat.Rgba8,
BitmapAlphaMode.Straight,
transform,
ExifOrientationMode.RespectExifOrientation,
ColorManagementMode.DoNotColorManage);
StorageFile destinationFile = await ApplicationData.Current.LocalFolder.CreateFileAsync(Path.Combine(Database.rootMoviesFoldersPaths, movie.LocalId + ".jpg"));
using (var destinationStream = await destinationFile.OpenAsync(FileAccessMode.ReadWrite))
{
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, destinationStream);
encoder.SetPixelData(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Premultiplied, 600, 900, 96, 96, pixelData.DetachPixelData());
await encoder.FlushAsync();
movie.HasFolderImage = true;
return true;
}
}
Multiply the buffersize by the bitdepth.