MSDN.WhiteKnight - Stack Overflow answers
Ответ на "Выполнить 3d-трансформацию картинки"
Answer 870886
Способ 1 - С использованием WPF
Можно создать такой класс на основе Viewport3D из WPF:
//References: PresentationCore, PresentationFramework, WindowsBase, System.XAML using System; using System.IO; using System.Collections.Generic; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Media3D; using System.Windows.Media.Imaging; namespace WindowsFormsTest { public class ImageTransformer { /*Применяет произвольное 3D-преобразование к изображению*/ public static void ApplyTransform(string input, string output, Matrix3D matr) { Viewport3D myViewport3D; BitmapImage image = new BitmapImage(new Uri(input)); // Declare scene objects. myViewport3D = new Viewport3D(); Model3DGroup myModel3DGroup = new Model3DGroup(); GeometryModel3D myGeometryModel = new GeometryModel3D(); ModelVisual3D myModelVisual3D = new ModelVisual3D(); // Defines the camera used to view the 3D object. In order to view the 3D object, // the camera must be positioned and pointed such that the object is within view // of the camera. PerspectiveCamera myPCamera = new PerspectiveCamera(); // Specify where in the 3D scene the camera is. myPCamera.Position = new Point3D(0, 0, Math.Max(image.PixelWidth,image.PixelHeight)*2); // Specify the direction that the camera is pointing. myPCamera.LookDirection = new Vector3D(0, 0, -1); // Define camera's horizontal field of view in degrees. myPCamera.FieldOfView = 60; // Asign the camera to the viewport myViewport3D.Camera = myPCamera; // Define the lights cast in the scene. Without light, the 3D object cannot // be seen. Note: to illuminate an object from additional directions, create // additional lights. AmbientLight al = new AmbientLight(Colors.White); myModel3DGroup.Children.Add(al); // The geometry specifes the shape of the 3D plane. In this sample, a flat sheet // is created. MeshGeometry3D myMeshGeometry3D = new MeshGeometry3D(); // Create a collection of normal vectors for the MeshGeometry3D. Vector3DCollection myNormalCollection = new Vector3DCollection(); myNormalCollection.Add(new Vector3D(0, 0, 1)); myNormalCollection.Add(new Vector3D(0, 0, 1)); myNormalCollection.Add(new Vector3D(0, 0, 1)); myNormalCollection.Add(new Vector3D(0, 0, 1)); myNormalCollection.Add(new Vector3D(0, 0, 1)); myNormalCollection.Add(new Vector3D(0, 0, 1)); myMeshGeometry3D.Normals = myNormalCollection; // Create a collection of vertex positions for the MeshGeometry3D. Point3DCollection myPositionCollection = new Point3DCollection(); myPositionCollection.Add(new Point3D(-image.PixelWidth / 2.0, -image.PixelHeight / 2.0, 0.5)); myPositionCollection.Add(new Point3D(image.PixelWidth / 2.0, -image.PixelHeight / 2.0, 0.5)); myPositionCollection.Add(new Point3D(image.PixelWidth / 2.0, image.PixelHeight / 2.0, 0.5)); myPositionCollection.Add(new Point3D(image.PixelWidth / 2.0, image.PixelHeight / 2.0, 0.5)); myPositionCollection.Add(new Point3D(-image.PixelWidth / 2.0, image.PixelHeight / 2.0, 0.5)); myPositionCollection.Add(new Point3D(-image.PixelWidth / 2.0, -image.PixelHeight / 2.0, 0.5)); myMeshGeometry3D.Positions = myPositionCollection; // Create a collection of texture coordinates for the MeshGeometry3D. PointCollection myTextureCoordinatesCollection = new PointCollection(); Point p5 = new Point(0, 0); Point p34 = new Point(1, 0); Point p2 = new Point(1, 1); Point p16 = new Point(0, 1); myTextureCoordinatesCollection.Add(p16); myTextureCoordinatesCollection.Add(p2); myTextureCoordinatesCollection.Add(p34); myTextureCoordinatesCollection.Add(p34); myTextureCoordinatesCollection.Add(p5); myTextureCoordinatesCollection.Add(p16); myMeshGeometry3D.TextureCoordinates = myTextureCoordinatesCollection; // Create a collection of triangle indices for the MeshGeometry3D. Int32Collection myTriangleIndicesCollection = new Int32Collection(); myTriangleIndicesCollection.Add(0); myTriangleIndicesCollection.Add(1); myTriangleIndicesCollection.Add(2); myTriangleIndicesCollection.Add(3); myTriangleIndicesCollection.Add(4); myTriangleIndicesCollection.Add(5); myMeshGeometry3D.TriangleIndices = myTriangleIndicesCollection; // Apply the mesh to the geometry model. myGeometryModel.Geometry = myMeshGeometry3D; // The material specifies the material applied to the 3D object. ImageBrush br = new ImageBrush(image); // Define material and apply to the mesh geometries. DiffuseMaterial myMaterial = new DiffuseMaterial(br); myGeometryModel.Material = myMaterial; myGeometryModel.BackMaterial = myMaterial; MatrixTransform3D transform = new MatrixTransform3D(matr); myGeometryModel.Transform = transform; // Add the geometry model to the model group. myModel3DGroup.Children.Add(myGeometryModel); // Add the group of models to the ModelVisual3d. myModelVisual3D.Content = myModel3DGroup; myViewport3D.Children.Add(myModelVisual3D); //render Viewport3D into bitmap int width = image.PixelWidth ; int height = image.PixelHeight; myViewport3D.Width = width; myViewport3D.Height = height; myViewport3D.Measure(new Size(width, height)); myViewport3D.Arrange(new Rect(0, 0, width, height)); RenderTargetBitmap rtb = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32); rtb.Render(myViewport3D); //Save bitmap to file using (var fileStream = new FileStream(output, FileMode.Create)) { BitmapEncoder encoder = new JpegBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(rtb)); encoder.Save(fileStream); } } /*Поворачивает изображение вокруг указанной оси на указанный угол*/ public static void RotateImage(string input, string output, Vector3D axis, int angle) { var myRotateTransform3D = new RotateTransform3D(); var myAxisAngleRotation3d = new AxisAngleRotation3D(); myAxisAngleRotation3d.Axis = axis; myAxisAngleRotation3d.Angle = angle; myRotateTransform3D.Rotation = myAxisAngleRotation3d; ImageTransformer.ApplyTransform(input, output, myRotateTransform3D.Value); } } }
Пример использования (поворот на 30 градусов вокруг оси Y):
ImageTransformer.RotateImage( "c:\\images\\image.jpg", "c:\\images\\image2.jpg", new System.Windows.Media.Media3D.Vector3D(0, 1, 0), 30);
Применение произвольного преобразования, заданного матрицей:
ImageTransformer.ApplyTransform( "c:\\images\\image.jpg", "c:\\images\\image2.jpg", new System.Windows.Media.Media3D.Matrix3D(.615, 0, 0, .00015, 0, .615, 0, -.000005, 0, 0, 1, 0, -151, -120, 0, 1));
При использовании в проекте WinForms нужно добавить ссылки на необходимые сборки, но делать отдельный поток для обработки сообщений WPF (Application.Run) не обязательно.
Способ 2 - С использованием MSHTML
При наличии IE 10+ можно программно растеризовать приведенный HTML+CSS с помощью такого кода:
using System; using System.Collections.Generic; using System.Drawing; using System.Text; using System.Runtime.InteropServices; using System.ComponentModel; //Reference: COM -> Microsoft HTML Object Library namespace WindowsFormsTest { public class HtmlVisualizer { public static void DrawHtml(string content, string output) { RECTL rcClient = new RECTL(); bool b = SystemParametersInfo(SPI_GETWORKAREA, 0, ref rcClient, 0); if (b == false) { rcClient.bottom = 480; rcClient.right = 640; } int width = (int)(rcClient.right - rcClient.left); int height = (int)(rcClient.bottom - rcClient.top); IntPtr screendc = GetDC(IntPtr.Zero); mshtml.HTMLDocument doc = null; mshtml.IHTMLDocument2 d2 = null; IOleObject pObj = null; IViewObject pView = null; try { doc = new mshtml.HTMLDocument(); //создание документа d2 = (mshtml.IHTMLDocument2)doc; int hr; //установка размера документа pObj = (IOleObject)d2; SIZEL sz = new SIZEL(); sz.x = (uint)MulDiv(width, HIMETRIC_INCH, GetDeviceCaps(screendc, LOGPIXELSX)); sz.y = (uint)MulDiv(height, HIMETRIC_INCH, GetDeviceCaps(screendc, LOGPIXELSY)); ; hr = pObj.SetExtent((int)System.Runtime.InteropServices.ComTypes.DVASPECT.DVASPECT_CONTENT, ref sz); if (hr != 0) throw Marshal.GetExceptionForHR(hr); d2.write(content); d2.close(); while (d2.readyState != "complete") System.Windows.Forms.Application.DoEvents(); //преобразование в Bitmap pView = (IViewObject)d2; Bitmap bmp = new Bitmap(width, height); Graphics g = Graphics.FromImage(bmp); using (g) { IntPtr hdc = g.GetHdc(); hr = pView.Draw((int)System.Runtime.InteropServices.ComTypes.DVASPECT.DVASPECT_CONTENT, -1, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, hdc, ref rcClient, IntPtr.Zero, IntPtr.Zero, 0); if (hr != 0) throw Marshal.GetExceptionForHR(hr); g.ReleaseHdc(hdc); } //сохранение в PNG bmp.Save(output, System.Drawing.Imaging.ImageFormat.Png); } finally { //освобождение ресурсов if (d2 != null) Marshal.ReleaseComObject(d2); if (pObj != null) Marshal.ReleaseComObject(pObj); if (pView != null) Marshal.ReleaseComObject(pView); if (doc != null) Marshal.ReleaseComObject(doc); } } [DllImport("gdi32.dll")] static extern int GetDeviceCaps(IntPtr hdc, int nIndex); [DllImport("user32.dll")] static extern IntPtr GetDC(IntPtr hWnd); [DllImport("user32.dll")] static extern bool SystemParametersInfo(int nAction, int nParam, ref RECTL rc, int nUpdate); public static int MulDiv(int number, int numerator, int denominator) { return (int)(((long)number * numerator) / denominator); } const int LOGPIXELSX = 88; const int LOGPIXELSY = 90; const int HIMETRIC_INCH = 2540; const int SPI_GETWORKAREA = 48; } [ComImport()] [GuidAttribute("0000010d-0000-0000-C000-000000000046")] [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] public interface IViewObject { int Draw([MarshalAs(UnmanagedType.U4)] int dwDrawAspect, int lindex, IntPtr pvAspect, IntPtr ptd, IntPtr hdcTargetDev, IntPtr hdcDraw, ref RECTL lprcBounds, IntPtr lprcWBounds, IntPtr pfnContinue, int dwContinue); int a(); int b(); int c(); int d(); int e(); } [ComImport()] [Guid("00000112-0000-0000-C000-000000000046")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IOleObject { void f(); void g(); void SetHostNames(object szContainerApp, object szContainerObj); void Close(uint dwSaveOption); void SetMoniker(uint dwWhichMoniker, object pmk); void GetMoniker(uint dwAssign, uint dwWhichMoniker, object ppmk); void x(); void y(); void DoVerb(uint iVerb, uint lpmsg, object pActiveSite, uint lindex, uint hwndParent, uint lprcPosRect); void EnumVerbs(ref object ppEnumOleVerb); void Update(); void IsUpToDate(); void GetUserClassID(uint pClsid); void GetUserType(uint dwFormOfType, uint pszUserType); int SetExtent(uint dwDrawAspect, ref SIZEL psizel); void GetExtent(uint dwDrawAspect, uint psizel); void Advise(object pAdvSink, uint pdwConnection); void Unadvise(uint dwConnection); void EnumAdvise(ref object ppenumAdvise); void GetMiscStatus(uint dwAspect, uint pdwStatus); void SetColorScheme(object pLogpal); }; public struct RECTL { public uint left; public uint top; public uint right; public uint bottom; } public struct SIZEL { public uint x; public uint y; } }
На вход подавать HTML такого вида:
<html><head> <meta http-equiv="X-UA-Compatible" content="IE=10" /> <style> html{width:100%;overflow:hidden;}body{margin:0;position:relative;transform-origin:top left;}@supports(transform:scale(1)){body{-ms-zoom:1!important;zoom:1!important;}} .camera{pointer-events:none;}.screen:hover{z-index:2;outline:10px solid rgba(255,0,0,.5);} body { width: 1920px; height: 1080px; } img { position: absolute; } .camera { width: 1920px; height: 1080px; } .screen { width: 1600px; height: 900px; transform: matrix3d(.615,0,0,.00015,0,.615,0,-.000005,0,0,1,0,-151,-120,0,1); } </style> </head> <body> <img class="screen" src="file://localhost/c:/images/screen.png"> <img class="camera" src="file://localhost/c:/images/kamera.png"> </body></html>
Источники:
Projecting an object into a scene based on world coordinates only
wpf, c#, renderTargetBitmap of viewport3D without assigning it to a window
Content is retrieved from StackExchange API.
Auto-generated by ruso-archive tools.