How to reduce size of image captured from webcam - silverlight

I am using a webcam to capture images in Silverlight. Is there a way to configure the webcam to catch images with a lower resolution?

http://programmerpayback.com/2010/01/21/use-silverlight-to-resize-images-and-increase-compression-before-uploading/
public static WriteableBitmap GetImageSource(Stream stream, double maxWidth, double maxHeight)
{
BitmapImage bmp = new BitmapImage();
bmp.SetSource(stream);
Image img = new Image();
img.Effect = new DropShadowEffect() { ShadowDepth = 0, BlurRadius = 0 };
img.Source = bmp;
double scaleX = 1;
double scaleY = 1;
if (bmp.PixelHeight > maxHeight)
scaleY = maxHeight / bmp.PixelHeight;
if (bmp.PixelWidth > maxWidth)
scaleX = maxWidth / bmp.PixelWidth;
// maintain aspect ratio by picking the most severe scale
double scale = Math.Min(scaleY, scaleX);
return new WriteableBitmap(img, new ScaleTransform() { ScaleX = scale, ScaleY = scale });
}

Related

Programmatically setting the icon size

On WPF I am creating a menu item dynamically at run time.
I set the icon from a StreamGeometry that's stored on a ResourceDictionary. Everything works OK but: how do I set the size of the icon?
MenuItem menExit = new MenuItem();
menExit.Header = "Exit"; // will be changedlater
menExit.Command = UICommands.CmdExit;
menExit.CommandBindings.Add(new CommandBinding(UICommands.CmdExit, CmdExitExecute, CmdExitCanExecute));
menExit.Icon = (StreamGeometry)FindResource("ImgExit");
//SET THE SIZE HERE????????
// Eventually, how do I set the fill color?
menu.Items.Add(menExit);
Note, I am doing all this at run time and not in xalm
I suggest you to create a path on which you can specify Height, Width and Fill and set your StreamGeometry as the Data of the path. Then put this Path as the icon of the MenuItem.
var path = new Path
{
Height = 20,
Width = 20,
Fill = new SolidColorBrush(Colors.Blue),
Data = (StreamGeometry) FindResource("ImgExit")
};
menExit.Icon = path;
You can always try this:
/// <summary>
/// Convert Geometry to ImageSource, Draws the Geometry on a bitmap surface and centers it.
/// </summary>
/// <param name="geometry"></param>
/// <param name="TargetSize"></param>
/// <returns></returns>
ImageSource Geometry_To_ImageSource(Geometry geometry, int TargetSize)
{
var rect = geometry.GetRenderBounds(new Pen(Brushes.Black, 0));
var bigger = rect.Width > rect.Height ? rect.Width : rect.Height;
var scale = TargetSize / bigger;
Geometry scaledGeometry = Geometry.Combine(geometry, geometry, GeometryCombineMode.Intersect, new ScaleTransform(scale, scale));
rect = scaledGeometry.GetRenderBounds(new Pen(Brushes.Black, 0));
Geometry transformedGeometry = Geometry.Combine(scaledGeometry, scaledGeometry, GeometryCombineMode.Intersect, new TranslateTransform(((TargetSize - rect.Width) / 2) - rect.Left, ((TargetSize - rect.Height) / 2) - rect.Top));
RenderTargetBitmap bmp = new RenderTargetBitmap(TargetSize, TargetSize, 96, 96, PixelFormats.Pbgra32);
DrawingVisual viz = new DrawingVisual();
using (DrawingContext dc = viz.RenderOpen())
{
dc.DrawGeometry(Brushes.Black, null, transformedGeometry);
}
bmp.Render(viz);
var mem = new MemoryStream();
PngBitmapEncoder pngEncoder = new PngBitmapEncoder();
pngEncoder.Frames.Add(BitmapFrame.Create(bmp));
pngEncoder.Save(mem);
var itm = GetImg(mem);
return itm;
}
BitmapImage GetImg(MemoryStream ms)
{
var bmp = new BitmapImage();
bmp.BeginInit();
bmp.StreamSource = ms;
bmp.EndInit();
return bmp;
}

Scaling and moving an item at the same time

I'm trying to create a storyboard that scales and moves an element to a specific position. The basic idea is there are a row of 6 small cards, clicking on one will zoom into a more detailed image, starting from the position and size of the small card and ending up with a large centered card.
The card doesn't end up in the X,Y position I specify, I believe because the scaletransform is also moving it. I saw that a matrixtransform may work, but couldn't get it to work, at least in WP8. Here is my original code, card is the small card for starting location, item is the large detailed card to be scaled and moved
private void ZoomIn(UIElement item, UIElement card)
{
// setup
var _Scale = new ScaleTransform
{
ScaleX = 1,
ScaleY = 1,
CenterX = item.RenderSize.Width / 2,
CenterY = item.RenderSize.Height / 2,
};
var transform = card.TransformToVisual(Application.Current.RootVisual as FrameworkElement);
Point absolutePosition = transform.Transform(new Point(0, 0));
var _Translate = new TranslateTransform();
var _Group = new TransformGroup();
_Group.Children.Add(_Scale);
_Group.Children.Add(_Translate);
item.RenderTransform = _Group;
// animate
var _Storyboard = new Storyboard { };
// scale X
var _ScaleAnimateX = new DoubleAnimation
{
To = 1.0,
From=0.5,
Duration = TimeSpan.FromSeconds(.25)
};
_Storyboard.Children.Add(_ScaleAnimateX);
Storyboard.SetTarget(_ScaleAnimateX, _Scale);
Storyboard.SetTargetProperty(_ScaleAnimateX,
new PropertyPath(ScaleTransform.ScaleXProperty));
// scale Y
var _ScaleAnimateY = new DoubleAnimation
{
To = 1.0,
From = 0.5,
Duration = TimeSpan.FromSeconds(.25)
};
_Storyboard.Children.Add(_ScaleAnimateY);
Storyboard.SetTarget(_ScaleAnimateY, _Scale);
Storyboard.SetTargetProperty(_ScaleAnimateY,
new PropertyPath(ScaleTransform.ScaleYProperty));
// translate (location X)
var _TranslateAnimateX = new DoubleAnimation
{
From = absolutePosition.X,
To = 300,
Duration = TimeSpan.FromSeconds(.25)
};
_Storyboard.Children.Add(_TranslateAnimateX);
Storyboard.SetTarget(_TranslateAnimateX, _Translate);
Storyboard.SetTargetProperty(_TranslateAnimateX,
new PropertyPath(TranslateTransform.XProperty));
// translate (location Y)
var _TranslateAnimateY = new DoubleAnimation
{
From = absolutePosition.Y,
To = 40,
Duration = TimeSpan.FromSeconds(.25)
};
_Storyboard.Begin();
}
I found the solution, the panel with the zoomed card didn't have a horizontal/vertical alignment, I set it to left/top, used CenterX/CenterY of 0, and movements are now pixel perfect

Add TransformGroup to a FramworkElement when rendering WPF to a PNG

I've got an app that turns some XAML Usercontrols into PNGs - this has worked really well up to now, unfortunately I now need to double the size of the images.
My method (that doesn't work!) was to add a ScaleTransform to the visual element after I've loaded it ...
This line is the new line at the top of the SaveUsingEncoder method.
visual.RenderTransform = GetScaleTransform(2);
The PNG is the new size (3000 x 2000) - but the XAML is Rendered at 1500x1000 in the centre of the image.
Can anyone assist please?
private void Load(string filename)
{
var stream = new FileStream(filename), FileMode.Open);
var frameworkElement = (FrameworkElement)(XamlReader.Load(stream));
var scale = 2;
var encoder = new PngBitmapEncoder();
var availableSize = new Size(1500 * scale, 1000 * scale);
frameworkElement.Measure(availableSize);
frameworkElement.Arrange(new Rect(availableSize));
name = name.Replace(" ", "-");
SaveUsingEncoder(frameworkElement, string.Format(#"{0}.png", name), encoder, availableSize);
}
private TransformGroup GetScaleTransform(int scale)
{
var myScaleTransform = new ScaleTransform {ScaleY = scale, ScaleX = scale};
var myTransformGroup = new TransformGroup();
myTransformGroup.Children.Add(myScaleTransform);
return myTransformGroup;
}
private void SaveUsingEncoder(FrameworkElement visual, string fileName, BitmapEncoder encoder, Size size)
{
visual.RenderTransform = GetScaleTransform(2);
var bitmap = new RenderTargetBitmap(
(int) size.Width,
(int) size.Height,
96,
96,
PixelFormats.Pbgra32);
bitmap.Render(visual);
var frame = BitmapFrame.Create(bitmap);
encoder.Frames.Add(frame);
using (var stream = File.Create(fileName))
{
encoder.Save(stream);
}
}
Called visual.UpdateLayout before rendering into the RenderTargetBitmap
(Thanks to Clemens for this answer - but he put it as a comment!)

Merge XPS landscape orientation

I've implemented this solution and it worked for me:
Can multiple xps documents be merged to one in WPF?
My problem is that the pages I want to merge are in landscape orientation. When the ContainerVisual is added it creates by default a page in vertical orientation. How can I change the orientation to ContainerVisual?
private void AddXPSDocument(string sourceDocument, SerializerWriterCollator vxpsd)
{
XpsDocument xpsOld = new XpsDocument(sourceDocument, FileAccess.Read);
FixedDocumentSequence seqOld = xpsOld.GetFixedDocumentSequence();
foreach (DocumentReference r in seqOld.References)
{
FixedDocument d = r.GetDocument(false);
foreach (PageContent pc in d.Pages)
{
FixedPage fixedPage = pc.GetPageRoot(false);
double width = fixedPage.Width;
double height = fixedPage.Height;
Size sz = new Size(width, height);
fixedPage.Width = width;
fixedPage.Height = height;
fixedPage.Measure(sz);
fixedPage.Arrange(new Rect(new Point(), sz));
//fixedPage.UpdateLayout();
ContainerVisual newPage = new ContainerVisual();
newPage.Children.Add(fixedPage);
vxpsd.Write(newPage);
}
}
xpsOld.Close();
}
You need to add a RotateTransform to the page visual.
Visual originalPage = Paginator.GetPage(pageNumber).Visual;
var pageContentVisual = new ContainerVisual();
TransformGroup group = new TransformGroup();
group.Children.Add(new RotateTransform { Angle = 90.0 });
pageContentVisual.Transform = group;
pageContentVisual.Children.Add(originalPage);
Note: The above was copied from a custom DocumentPaginator however you should be able to apply it your situation.

Convert WPF Control to BitmapSource

This is kind of a two part question- First, why doesn't this code work?
Canvas canvas = new Canvas { Width = 640, Height = 480 };
System.Windows.Size size = new System.Windows.Size( canvas.Width, canvas.Height);
//Measure and arrange the surface
canvas.Measure( size );
canvas.Arrange( new Rect( size ) );
canvas.Background = new SolidColorBrush( Colors.Purple );
RenderTargetBitmap bitmap = new RenderTargetBitmap( (int)canvas.Width, (int)canvas.Height, 96d, 96d, PixelFormats.Pbgra32 );
bitmap.Render( canvas );
BitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add( BitmapFrame.Create( bitmap ) );
using ( MemoryStream outStream = new MemoryStream() )
{
encoder.Save( outStream );
outStream.Seek( 0, SeekOrigin.Begin );
BitmapImage bmp = new BitmapImage { CacheOption = BitmapCacheOption.OnLoad };
bmp.BeginInit();
bmp.StreamSource = outStream;
bmp.EndInit();
}
When I write the image to disk, all I see is a black image- I've done this before and had no problems, but now something is escaping me... I've checked the width and height and the buffer data in the MemoryStream and everything look okay...
This is just a test, the real goal would be to create a BitmapSource from the Canvas visual image. The Canvas is getting drawn on with Shapes (polylines etc) in code. I then need to pass this BitmapSource to an Image in xaml, at a rate of about 60 frames per second. I noticed that the Image.Source is using CachedBitmap if I create a mock BitmapSource, but it is rebinding to my BitmapImage everytime I update my (black) Bitmap.
Suggestions on how to create a Canvas in memory at 60fps and create a BitmapSource from it that is seen by Image.Source as a CachedBitmap?
Makubex is correct - you need to wait until things get loaded up before the visuals are in a state where they've actually rendered anything capable of being copied; that said, while I'm not on a computer where I've got Studio installed, I do have LINQPad installed....
void Main()
{
mainWindow = new Window(){ Width = 640, Height = 480, Title = "Main Window" };
canvas = new Canvas { Width = 640, Height = 480 };
System.Windows.Size size = new System.Windows.Size( canvas.Width, canvas.Height );
// Measure and arrange the surface
canvas.Measure( size );
canvas.Arrange( new Rect( size ) );
canvas.Background = new SolidColorBrush( Colors.Purple );
mirrorTimer = new DispatcherTimer(
TimeSpan.FromMilliseconds(1000.0 / 60.0),
DispatcherPriority.Background,
CopyToMirror,
Dispatcher.CurrentDispatcher);
updateTimer = new DispatcherTimer(
TimeSpan.FromMilliseconds(1000.0 / 60.0),
DispatcherPriority.Background,
DrawSomething,
Dispatcher.CurrentDispatcher);
mainWindow.Loaded +=
(o,e) =>
{
mirrorWindow = new Window { Width = 640, Height = 480, Title = "Mirror Window" };
mirrorWindow.Show();
mirrorWindow.Loaded +=
(o2,e2) =>
{
mirrorTimer.Start();
};
};
mainWindow.Closed +=
(o,e) =>
{
if(mirrorTimer != null)
{
mirrorTimer.Stop();
mirrorWindow.Close();
}
};
mainWindow.Content = canvas;
mainWindow.Show();
}
Window mainWindow;
Window mirrorWindow;
Canvas canvas;
DispatcherTimer mirrorTimer;
DispatcherTimer updateTimer;
Random rnd = new Random();
private void DrawSomething(object sender, EventArgs args)
{
canvas.Children.Clear();
canvas.Background = Brushes.White;
var blob = new Ellipse() { Width = rnd.Next(0, 20), Height = rnd.Next(0, 20) };
blob.Fill = new SolidColorBrush(Color.FromArgb(255, (byte)rnd.Next(0,255), (byte)rnd.Next(0,255), (byte)rnd.Next(0,255)));
Canvas.SetLeft(blob, (int)rnd.Next(0, (int)canvas.ActualWidth));
Canvas.SetTop(blob, (int)rnd.Next(0, (int)canvas.ActualHeight));
canvas.Children.Add(blob);
}
private void CopyToMirror(object sender, EventArgs args)
{
var currentImage = (mirrorWindow.Content as Image);
if(currentImage == null)
{
currentImage = new Image(){ Width = 640, Height = 480 };
mirrorWindow.Content = currentImage;
}
RenderTargetBitmap bitmap = new RenderTargetBitmap( (int)canvas.Width, (int)canvas.Height, 96d, 96d, PixelFormats.Pbgra32 );
bitmap.Render( canvas );
BitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add( BitmapFrame.Create( bitmap ) );
BitmapImage bmp = new BitmapImage() { CacheOption = BitmapCacheOption.OnLoad };
MemoryStream outStream = new MemoryStream();
encoder.Save(outStream);
outStream.Seek(0, SeekOrigin.Begin);
bmp.BeginInit();
bmp.StreamSource = outStream;
bmp.EndInit();
currentImage.Source = bmp;
}
If you are still having issues with the black image move your CacheOption set to just after the BeingInit call:
bmp.BeginInit();
bmp.CacheOption = BitmapCacheOption.OnLoad;
bmp.StreamSource = outStream;
bmp.EndInit();

Resources