c# - Clip BitmapImage using Strokes from a InkCanvas -


i'm tasked create "cinemagraph" feature, user must select desired area using inkcanvas draw selected pixels should remain untouched rest of animation/video (or, select pixels should "alive").

example: from johan blomström

i'm thinking getting stroke collection inkcanvas , use clip image , merge untouched one.

how can that? can load images disk, how can clip image based on stroke?

more details:

after drawing , selecting pixels should remain static, have stroke collection. can geometry of each individual stroke, need merge geometries.

based on merged geometry, need invert (the geometry) , use clip first frame, later clipped image ready, need merge other frames.

my code far:

//gets bitmapsource string path: var image = listframes[0].imagelocation.sourcefrom(); var rectangle = new rectanglegeometry(new rect(new system.windows.point(0, 0), new system.windows.size(image.width, image.height))); geometry geometry = geometry.empty;  foreach(stroke stroke in cinemagraphinkcanvas.strokes) {     geometry = geometry.combine(geometry, stroke.getgeometry(), geometrycombinemode.union, null); }  //inverts geometry, clip other unselect pixels of bitmapimage. geometry = geometry.combine(geometry, rectangle, geometrycombinemode.exclude, null);  //this here uielement, can't use control, need way clip image without using ui. var clippedimage = new system.windows.controls.image(); clippedimage.source = image; clippedimage.clip = geometry;  //i can't render of clippedimage control because i'm not displaying control. 

is there way clip bitmapsource without using uielement?

maybe, maybe

i'm thinking opacitymask , brush... can't use uielement, need apply opacitymask directly bitmapsource.

i made it! (you can see result here, screentogif > editor > image tab > cinemagraph)


code

sourcefrom() , dpiof() , scaledsize():

/// <summary> /// gets bitmapsource source , closes file usage. /// </summary> /// <param name="filesource">the file open.</param> /// <param name="size">the maximum height of image.</param> /// <returns>the open bitmapsource.</returns> public static bitmapsource sourcefrom(this string filesource, int32? size = null) {     using (var stream = new filestream(filesource, filemode.open))     {         var bitmapimage = new bitmapimage();         bitmapimage.begininit();         bitmapimage.cacheoption = bitmapcacheoption.onload;          if (size.hasvalue)             bitmapimage.decodepixelheight = size.value;          //dpiof() , scaledsize() uses same principles of extension.          bitmapimage.streamsource = stream;         bitmapimage.endinit();          //just in case want load image in thread.         bitmapimage.freeze();                      return bitmapimage;     } } 

getrender():

/// <summary> /// gets render of current uielement /// </summary> /// <param name="source">uielement screenshot</param> /// <param name="dpi">the dpi of source.</param> /// <returns>an imagesource</returns> public static rendertargetbitmap getrender(this uielement source, double dpi) {     rect bounds = visualtreehelper.getdescendantbounds(source);      var scale = dpi / 96.0;     var width = (bounds.width + bounds.x) * scale;     var height = (bounds.height + bounds.y) * scale;      #region if no bounds      if (bounds.isempty)     {         var control = source control;          if (control != null)         {             width = control.actualwidth * scale;             height = control.actualheight * scale;         }          bounds = new rect(new system.windows.point(0d, 0d),                            new system.windows.point(width, height));     }      #endregion      var roundwidth = (int)math.round(width, midpointrounding.awayfromzero);     var roundheight = (int)math.round(height, midpointrounding.awayfromzero);      var rtb = new rendertargetbitmap(roundwidth, roundheight, dpi, dpi,                                       pixelformats.pbgra32);      drawingvisual dv = new drawingvisual();     using (drawingcontext ctx = dv.renderopen())     {         visualbrush vb = new visualbrush(source);          var locationrect = new system.windows.point(bounds.x, bounds.y);         var sizerect = new system.windows.size(bounds.width, bounds.height);          ctx.drawrectangle(vb, null, new rect(locationrect, sizerect));     }      rtb.render(dv);     return (rendertargetbitmap)rtb.getasfrozen(); } 

gets imagesource , geometry:

//custom extensions, using path of image, provide //dpi (of image) , scaled size (pixelwidth , pixelheight). var dpi = listframes[0].imagelocation.dpiof(); var scaledsize = listframes[0].imagelocation.scaledsize();  //custom extension loads first frame. var image = listframes[0].imagelocation.sourcefrom();  //rectangle same size of image. used within xor operation. var rectangle = new rectanglegeometry(new rect(     new system.windows.point(0, 0),      new system.windows.size(image.pixelwidth, image.pixelheight))); geometry geometry = geometry.empty;  //each stroke transformed geometry , combined union operation. foreach(stroke stroke in cinemagraphinkcanvas.strokes) {     geometry = geometry.combine(geometry, stroke.getgeometry(),          geometrycombinemode.union, null); }  //the rectangle same size of image combined of  //the strokes using xor operation, inverts geometry. geometry = geometry.combine(geometry, rectangle, geometrycombinemode.xor, null); 

applying geometry image element:

//uielement used hold bitmapsource clipped. var clippedimage = new system.windows.controls.image {     height = image.pixelheight,     width = image.pixelwidth,     source = image,     clip = geometry }; clippedimage.measure(scaledsize); clippedimage.arrange(new rect(scaledsize));  //gets render of image element, clipped. var imagerender = clippedimage.getrender(dpi, scaledsize);  //merging frames: overlay(imagerender, dpi, true);    

overlay(), merges frames:

private void overlay(rendertargetbitmap render, double dpi, bool forall = false) {     //gets selected frames based on selection of listview,      //in case, every frame should selected.     var framelist = forall ? listframes : selectedframes();      int count = 0;     foreach (frameinfo frame in framelist)     {         var image = frame.imagelocation.sourcefrom();          var drawingvisual = new drawingvisual();         using (drawingcontext drawingcontext = drawingvisual.renderopen())         {             drawingcontext.drawimage(image, new rect(0, 0, image.width, image.height));             drawingcontext.drawimage(render, new rect(0, 0, render.width, render.height));         }          //converts visual (drawingvisual) bitmapsource         var bmp = new rendertargetbitmap(image.pixelwidth, image.pixelheight, dpi, dpi, pixelformats.pbgra32);         bmp.render(drawingvisual);          //creates bmpbitmapencoder , adds bitmapsource frames of encoder         var encoder = new bmpbitmapencoder();         encoder.frames.add(bitmapframe.create(bmp));          //saves image file using encoder         using (stream stream = file.create(frame.imagelocation))             encoder.save(stream);     } } 

example:

clean, unedited animation.

animation

selected pixels should animated.

green pixels should move

image clipped (black transparent).

clipped image

cinemagraph done!

only selected pixels move

as can see, selected pixels can change, others remain static.


Comments

Popular posts from this blog

routing - AngularJS State management ->load multiple states in one page -

python - GRASS parser() error -

json - Gson().fromJson(jsonResult, Myobject.class) return values in 0's -