none
Bitmapを任意の角度の四角形切り取り保存したいのですが RRS feed

  • 質問

  • お世話になります。

    ビットマップを読み込んで任意の大きさの四角形に切り取り保存するのは、下記のようにすればいいようですが、角度を変えて保存するにはどうしたらいいでしょうか?

        // 画像を切り抜く
        System.Drawing.Rectangle rect = new System.Drawing.Rectangle((int)left, (int)top, (int)width, (int)height);
        System.Drawing.Bitmap bmpNew = bmpBase.Clone(rect, bmpBase.PixelFormat);
    
        // 画像をGIF形式で保存
        bmpNew.Save(ファイル名, System.Drawing.Imaging.ImageFormat.Gif);
    
    
        // 画像リソースを解放
        bmpBase.Dispose();
        bmpNew.Dispose();

    上記の場合、rect で左上の角を起点に縦横の長さで切り取る四角形を決めています。表示画面に対して左右の傾きなく水平に表示されたものならそれでいいのですが、斜めに撮影されたものを切り取り真っ直ぐ、水平に補正して切り取りたい場合どのようにしますか?

    Polygon のような形(四角形)の座標で切り取る方法などあるのでしょうか?

    2015年8月25日 6:46

回答

  • こんな?

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <DockPanel>
    
                <StackPanel DockPanel.Dock="Top" Orientation="Horizontal" >
                    <Button Content="Load" Click="LoadButton_Click"/>
                    <Button Content="Save" Click="SaveButton_Click"/>
                </StackPanel>
    
                <Grid DockPanel.Dock="Bottom" VerticalAlignment="Stretch" >
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition />
                    </Grid.ColumnDefinitions>
                    <TextBlock Grid.Row="0" Grid.Column="0" Text="表示倍率" />
                    <Slider x:Name="slScale"  Grid.Row="0" Grid.Column="1" VerticalAlignment="Stretch"
                            Minimum="0.1" Maximum="4" Value="1" Orientation="Horizontal" 
                            ValueChanged="slScale_ValueChanged"/>
    
                    <TextBlock Grid.Row="1" Grid.Column="0" Text="角度" />
                    <Slider x:Name="slAngle" Grid.Row="1" Grid.Column="1" VerticalAlignment="Stretch"
                            Minimum="-180" Maximum="180" Value="0"  Orientation="Horizontal" />
                </Grid>
    
                <ScrollViewer VerticalScrollBarVisibility="Visible"
                              HorizontalScrollBarVisibility="Visible">
                    <Grid Background="Black">
                        <Grid.LayoutTransform>
                            <ScaleTransform ScaleX="{Binding Path=Value,ElementName=slScale}" 
                                            ScaleY="{Binding Path=Value,ElementName=slScale}" 
                                            x:Name="scale"/>
                        </Grid.LayoutTransform>
                        
                        <!-- 画像表示領域 -->
                        <Grid x:Name="imageGrid" VerticalAlignment="Top" HorizontalAlignment="Left">
                            <Image x:Name="img" Stretch="None" >
                                <Image.LayoutTransform>
                                    <RotateTransform Angle="{Binding Path=Value,ElementName=slAngle}" />
                                </Image.LayoutTransform>
                            </Image>
                        </Grid>
                        
                        <!-- 切り取り領域表示用 -->
                        <Canvas VerticalAlignment="Stretch" HorizontalAlignment="Stretch" 
                                MouseDown="Canvas_MouseDown"
                                MouseUp="Canvas_MouseUp"
                                MouseMove="Canvas_MouseMove"
                                Background="Transparent"
                                x:Name="clipCanvas">
                            <Rectangle x:Name="clipRect" Stroke="Black" StrokeThickness="2"  StrokeDashArray="2,2,2,2"/>
                            <Rectangle Stroke="White" StrokeDashArray="0,2,2,0" 
                                       StrokeThickness="{Binding Path=StrokeThickness,ElementName=clipRect}" 
                                       Canvas.Left="{Binding Path=(Canvas.Left),ElementName=clipRect}"
                                       Canvas.Top="{Binding Path=(Canvas.Top),ElementName=clipRect}"
                                       Width ="{Binding Path=Width,ElementName=clipRect}"
                                       Height="{Binding Path=Height,ElementName=clipRect}"/>
    
                        </Canvas>
    
                    </Grid>
    
                </ScrollViewer>
            </DockPanel>
        </Grid>
    </Window>
    namespace WpfApplication1
    {
        using System;
        using System.Windows;
        using System.Windows.Controls;
        using System.Windows.Input;
        using System.Windows.Media;
        using System.Windows.Media.Imaging;
    
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            #region 切り抜き領域指定
    
            private bool isDown;
            private Point p1;
            private Point p2;
    
            private Rect GetClipRect()
            {
                double x1 = Math.Max(0, Math.Min(p1.X, p2.X));
                double y1 = Math.Max(0, Math.Min(p1.Y, p2.Y));
                double x2 = Math.Max(x1, Math.Max(p1.X, p2.X));
                double y2 = Math.Max(y1, Math.Max(p1.Y, p2.Y));
                double w = Math.Abs(x2 - x1);
                double h = Math.Abs(y2 - y1);
                return new Rect(x1, y1, w, h);
            }
            private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
            {
                Canvas canvas = (Canvas)sender;
                p1 = e.GetPosition(canvas);
                isDown = true;
                Mouse.Capture((Canvas)sender);
            }
    
            private void Canvas_MouseMove(object sender, MouseEventArgs e)
            {
                if (isDown)
                {
                    Canvas canvas = (Canvas)sender;
                    p2 = e.GetPosition(canvas);
    
                    Rect rect = GetClipRect();
                    Canvas.SetLeft(clipRect, rect.Left);
                    Canvas.SetTop(clipRect, rect.Top);
                    clipRect.Width = rect.Width;
                    clipRect.Height = rect.Height;
                }
            }
            private void Canvas_MouseUp(object sender, MouseButtonEventArgs e)
            {
                Mouse.Capture(null);
                isDown = false;
            }
    
            private void slScale_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
            {
                if (this.clipRect != null)
                {
                    //切り取り領域の点線の太さを縮尺の逆数で
                    clipRect.StrokeThickness = 2 / e.NewValue;
                }
            }
            #endregion
    
    
            private string filePath;
    
            private void LoadButton_Click(object sender, RoutedEventArgs e)
            {
                Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
                if (dlg.ShowDialog() ?? false)
                {
                    try
                    {
                        var uri = new Uri(dlg.FileName);
                        BitmapImage bi = new BitmapImage(uri);
                        this.img.Source = bi;
                        filePath = dlg.FileName;
                    }
                    catch
                    {
                        this.img.Source = null;
                    }
                }
            }
            private void SaveButton_Click(object sender, RoutedEventArgs e)
            {
                try
                {
                    if (System.IO.File.Exists(filePath))
                    {
                        //クリップ領域を表示画像領域(imageGrid)の座標に変換
                        Rect r = GetClipRect();
                        Point p1 = clipCanvas.TranslatePoint(r.TopLeft, imageGrid);
                        Point p2 = clipCanvas.TranslatePoint(r.BottomRight, imageGrid);
                        double wpfWidth = imageGrid.ActualWidth;
                        double wpfHeight = imageGrid.ActualHeight;
    
                        #region 方法1 Bitmapから自分で切り出し(GDI+)
                        {
                            //画像を読み直し
                            var bmpOriginal = BitmapTool.GetImage(filePath);
    
                            //回転後画像を作成
                            double angle = slAngle.Value;
                            var bmpRotate = BitmapTool.CreateRotateBitmap(bmpOriginal, (float)angle);
                            bmpOriginal.Dispose();
    
                            //表示画像座標領域を回転後画像の領域に変換
                            double x1 = Math.Min((double)bmpRotate.Width * p1.X / wpfWidth, bmpRotate.Width);
                            double y1 = Math.Min((double)bmpRotate.Height * p1.Y / wpfHeight, bmpRotate.Height);
                            double x2 = Math.Min((double)bmpRotate.Width * p2.X / wpfWidth, bmpRotate.Width);
                            double y2 = Math.Min((double)bmpRotate.Height * p2.Y / wpfHeight, bmpRotate.Height);
                            double w = x2 - x1;
                            double h = y2 - y1;
                            System.Drawing.Rectangle rect
                               = new System.Drawing.Rectangle((int)x1, (int)y1, (int)w, (int)h);
    
                            //回転後画像を切り抜き
                            var bmpCliped = BitmapTool.CreateClipBitmap(bmpRotate, rect);
    
                            //保存
                            bmpCliped.Save(@"test.png", System.Drawing.Imaging.ImageFormat.Png);
                        }
                        #endregion
    
                        #region 方法2 表示用に回転させた画像からRenderTargetBitmapとCroppedBitmapを使って切り出し
                        {
                            //回転画像全体をBitmap化
                            var bmp = new System.Windows.Media.Imaging.RenderTargetBitmap((int)wpfWidth, (int)wpfHeight, 96d, 96d, PixelFormats.Pbgra32);
                            bmp.Render(imageGrid);
    
                            //回転画像から領域を指定して切り出し
                            System.Windows.Int32Rect r32 = new Int32Rect((int)r.Left, (int)r.Top, (int)r.Width, (int)r.Height);
                            var cropped = new System.Windows.Media.Imaging.CroppedBitmap(bmp, r32);
    
                            //保存
                            PngBitmapEncoder encoder = new PngBitmapEncoder();
                            encoder.Frames.Add(BitmapFrame.Create(cropped));
                            using (System.IO.FileStream fs = new System.IO.FileStream("test2.png", System.IO.FileMode.Create, System.IO.FileAccess.Write))
                            {
                                encoder.Save(fs);
                            }
                        }
                        #endregion
    
                    }
                    MessageBox.Show("保存しました");
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }
        }
    }
    namespace WpfApplication1
    {
        using System;
        using System.Drawing;
        using System.Linq;
    
        /// <summary>GDIでBitmap処理するクラス</summary>
        class BitmapTool
        {
            public static Bitmap GetImage(string filePath)
            {
                using (System.IO.FileStream fs = System.IO.File.OpenRead(filePath))
                {
                    return (Bitmap)Bitmap.FromStream(fs);
                }
            }
    
            /// <summary> 画像を切り出す </summary>
            /// <param name="bmpBase">元画像</param>
            /// <param name="rect">切り出し範囲</param>
            /// <returns>切り出した画像</returns>
            public static Bitmap CreateClipBitmap(Bitmap bmpBase, Rectangle rect)
            {
                Bitmap bmp = new Bitmap(rect.Width, rect.Height, bmpBase.PixelFormat);
                using (Graphics g = Graphics.FromImage(bmp))
                {
                    g.DrawImage(bmpBase, new Rectangle(0, 0, bmp.Width, bmp.Height), rect, GraphicsUnit.Pixel);
    
                    //g.DrawImage(bmpBase, rect, new Rectangle(0, 0, bmp.Width, bmp.Height), GraphicsUnit.Pixel);
                }
                return bmp;
            }
            /// <summary>画像を回転させる</summary>
            /// <param name="bmpBase">元画像</param>
            /// <param name="angleDeg">回転角度(°)</param>
            /// <returns>回転後の座標</returns>
            public static Bitmap CreateRotateBitmap(Bitmap bmpBase, float angleDeg)
            {
                return CreateRotateBitmap(bmpBase, angleDeg, Color.Black);
            }
            /// <summary>画像を回転させる</summary>
            /// <param name="bmpBase">元画像</param>
            /// <param name="angleDeg">回転角度(°)</param>
            /// <returns>回転後の座標</returns>
            public static Bitmap CreateRotateBitmap(Bitmap bmpBase, float angleDeg, Color backGround)
            {
                angleDeg = (angleDeg % 360 + 360) % 360;// 0~360に制限
    
                Bitmap bmpNew;
    
                if (angleDeg == 0)
                {
                    System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, bmpBase.Width, bmpBase.Height);
    
                    return CreateClipBitmap(bmpBase, rect);
                }
                else
                {
                    //元画像の四隅の座標
                    PointF[] ps = new[]
                        {
                            new PointF(0,0),
                            new PointF(bmpBase.Width,0),
                            new PointF(bmpBase.Width,bmpBase.Height),
                            new PointF(0,bmpBase.Height)
                        };
    
                    var matrix = new System.Drawing.Drawing2D.Matrix();
                    matrix.Rotate(angleDeg);//回転のアフィン変換を作る
    
                    matrix.TransformPoints(ps);//元画像を左上基準で回転させた後の各点の座標を計算
    
                    //回転後の画像が収まる範囲を調べる
                    float xmin = ps.Min(p => p.X);
                    float xmax = ps.Max(p => p.X);
                    float ymin = ps.Min(p => p.Y);
                    float ymax = ps.Max(p => p.Y);
    
                    //はみ出さないように平行移動
                    matrix.Translate(-xmin, -ymin, System.Drawing.Drawing2D.MatrixOrder.Append);
    
                    //新しい画像の幅と高さ
                    int newWidth = (int)Math.Ceiling(xmax - xmin);
                    int newHeight = (int)Math.Ceiling(ymax - ymin);
    
                    //新しいBitmapを用意
                    bmpNew = new Bitmap(bmpBase, newWidth, newHeight);
    
                    using (System.Drawing.Graphics g = Graphics.FromImage(bmpNew))
                    {
                        g.Clear(backGround);
                        g.Transform = matrix;//以降の描画がアフィン変換されるように指定
                        g.DrawImage(bmpBase, Point.Empty);//アフィン変換適用して複写
                    }
                }
                return bmpNew;
            }
        }
    }

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 編集済み gekkaMVP 2015年8月26日 15:08
    • 回答としてマーク ferret001 2015年8月27日 5:32
    2015年8月26日 13:04

すべての返信

  • Graphicsを使って描画するのが基本でしょう。

    1. 出力先のBitmapを作成する。
    2. Graphics.FromImage(出力先Bitmap)でGraphicsオブジェクトを取得する。
    3. GraphicsのTranslateTransformおよびRotateTransformで描画が適切な角度に回転して行われるようにする。
    4. GraphicsのDrawImageで元画像を描画する。
    5. 出力先のBitmapをSaveする。

    大体こんな手順です。

    <追記>あー、元画像が斜めになっている場合、こういう単純な方法だとちょっと不足ですね。テンポラリのBitmapを用意して、これから作ったGraphicsを使って上記4までを実施し、その後、Cloneなどで適切な部分を切り出すのがいいでしょうか。</追記>
    • 編集済み Hongliang 2015年8月25日 7:12
    2015年8月25日 7:06
  • Hongliang様、いつもお世話になります。

    前述の通り画像の切り取りの部分はできておりますが、「任意に回転して切り取り」がどうしてもできません。

    画像の回転は、Window_ManipulationDeltaで処理していますが、回転した同じ画像を作れません。

    今行っている切り出しは、保存時に新たに同画像を読み

    // 画像を切り抜く
       
    System.Drawing.Rectangle rect = new System.Drawing.Rectangle((int)left, (int)top, (int)width, (int)height);
       
    System.Drawing.Bitmap bmpNew = bmpBase.Clone(rect, bmpBase.PixelFormat);
     
    // 画像をJpeg形式で保存
        bmpNew
    .Save(ファイル名, System.Drawing.Imaging.ImageFormat.Jpeg);
    常に再度読むため回転した結果を無視して作成しています。

    2015年8月26日 7:29
  • こんな?

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <DockPanel>
    
                <StackPanel DockPanel.Dock="Top" Orientation="Horizontal" >
                    <Button Content="Load" Click="LoadButton_Click"/>
                    <Button Content="Save" Click="SaveButton_Click"/>
                </StackPanel>
    
                <Grid DockPanel.Dock="Bottom" VerticalAlignment="Stretch" >
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition />
                    </Grid.ColumnDefinitions>
                    <TextBlock Grid.Row="0" Grid.Column="0" Text="表示倍率" />
                    <Slider x:Name="slScale"  Grid.Row="0" Grid.Column="1" VerticalAlignment="Stretch"
                            Minimum="0.1" Maximum="4" Value="1" Orientation="Horizontal" 
                            ValueChanged="slScale_ValueChanged"/>
    
                    <TextBlock Grid.Row="1" Grid.Column="0" Text="角度" />
                    <Slider x:Name="slAngle" Grid.Row="1" Grid.Column="1" VerticalAlignment="Stretch"
                            Minimum="-180" Maximum="180" Value="0"  Orientation="Horizontal" />
                </Grid>
    
                <ScrollViewer VerticalScrollBarVisibility="Visible"
                              HorizontalScrollBarVisibility="Visible">
                    <Grid Background="Black">
                        <Grid.LayoutTransform>
                            <ScaleTransform ScaleX="{Binding Path=Value,ElementName=slScale}" 
                                            ScaleY="{Binding Path=Value,ElementName=slScale}" 
                                            x:Name="scale"/>
                        </Grid.LayoutTransform>
                        
                        <!-- 画像表示領域 -->
                        <Grid x:Name="imageGrid" VerticalAlignment="Top" HorizontalAlignment="Left">
                            <Image x:Name="img" Stretch="None" >
                                <Image.LayoutTransform>
                                    <RotateTransform Angle="{Binding Path=Value,ElementName=slAngle}" />
                                </Image.LayoutTransform>
                            </Image>
                        </Grid>
                        
                        <!-- 切り取り領域表示用 -->
                        <Canvas VerticalAlignment="Stretch" HorizontalAlignment="Stretch" 
                                MouseDown="Canvas_MouseDown"
                                MouseUp="Canvas_MouseUp"
                                MouseMove="Canvas_MouseMove"
                                Background="Transparent"
                                x:Name="clipCanvas">
                            <Rectangle x:Name="clipRect" Stroke="Black" StrokeThickness="2"  StrokeDashArray="2,2,2,2"/>
                            <Rectangle Stroke="White" StrokeDashArray="0,2,2,0" 
                                       StrokeThickness="{Binding Path=StrokeThickness,ElementName=clipRect}" 
                                       Canvas.Left="{Binding Path=(Canvas.Left),ElementName=clipRect}"
                                       Canvas.Top="{Binding Path=(Canvas.Top),ElementName=clipRect}"
                                       Width ="{Binding Path=Width,ElementName=clipRect}"
                                       Height="{Binding Path=Height,ElementName=clipRect}"/>
    
                        </Canvas>
    
                    </Grid>
    
                </ScrollViewer>
            </DockPanel>
        </Grid>
    </Window>
    namespace WpfApplication1
    {
        using System;
        using System.Windows;
        using System.Windows.Controls;
        using System.Windows.Input;
        using System.Windows.Media;
        using System.Windows.Media.Imaging;
    
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            #region 切り抜き領域指定
    
            private bool isDown;
            private Point p1;
            private Point p2;
    
            private Rect GetClipRect()
            {
                double x1 = Math.Max(0, Math.Min(p1.X, p2.X));
                double y1 = Math.Max(0, Math.Min(p1.Y, p2.Y));
                double x2 = Math.Max(x1, Math.Max(p1.X, p2.X));
                double y2 = Math.Max(y1, Math.Max(p1.Y, p2.Y));
                double w = Math.Abs(x2 - x1);
                double h = Math.Abs(y2 - y1);
                return new Rect(x1, y1, w, h);
            }
            private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
            {
                Canvas canvas = (Canvas)sender;
                p1 = e.GetPosition(canvas);
                isDown = true;
                Mouse.Capture((Canvas)sender);
            }
    
            private void Canvas_MouseMove(object sender, MouseEventArgs e)
            {
                if (isDown)
                {
                    Canvas canvas = (Canvas)sender;
                    p2 = e.GetPosition(canvas);
    
                    Rect rect = GetClipRect();
                    Canvas.SetLeft(clipRect, rect.Left);
                    Canvas.SetTop(clipRect, rect.Top);
                    clipRect.Width = rect.Width;
                    clipRect.Height = rect.Height;
                }
            }
            private void Canvas_MouseUp(object sender, MouseButtonEventArgs e)
            {
                Mouse.Capture(null);
                isDown = false;
            }
    
            private void slScale_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
            {
                if (this.clipRect != null)
                {
                    //切り取り領域の点線の太さを縮尺の逆数で
                    clipRect.StrokeThickness = 2 / e.NewValue;
                }
            }
            #endregion
    
    
            private string filePath;
    
            private void LoadButton_Click(object sender, RoutedEventArgs e)
            {
                Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
                if (dlg.ShowDialog() ?? false)
                {
                    try
                    {
                        var uri = new Uri(dlg.FileName);
                        BitmapImage bi = new BitmapImage(uri);
                        this.img.Source = bi;
                        filePath = dlg.FileName;
                    }
                    catch
                    {
                        this.img.Source = null;
                    }
                }
            }
            private void SaveButton_Click(object sender, RoutedEventArgs e)
            {
                try
                {
                    if (System.IO.File.Exists(filePath))
                    {
                        //クリップ領域を表示画像領域(imageGrid)の座標に変換
                        Rect r = GetClipRect();
                        Point p1 = clipCanvas.TranslatePoint(r.TopLeft, imageGrid);
                        Point p2 = clipCanvas.TranslatePoint(r.BottomRight, imageGrid);
                        double wpfWidth = imageGrid.ActualWidth;
                        double wpfHeight = imageGrid.ActualHeight;
    
                        #region 方法1 Bitmapから自分で切り出し(GDI+)
                        {
                            //画像を読み直し
                            var bmpOriginal = BitmapTool.GetImage(filePath);
    
                            //回転後画像を作成
                            double angle = slAngle.Value;
                            var bmpRotate = BitmapTool.CreateRotateBitmap(bmpOriginal, (float)angle);
                            bmpOriginal.Dispose();
    
                            //表示画像座標領域を回転後画像の領域に変換
                            double x1 = Math.Min((double)bmpRotate.Width * p1.X / wpfWidth, bmpRotate.Width);
                            double y1 = Math.Min((double)bmpRotate.Height * p1.Y / wpfHeight, bmpRotate.Height);
                            double x2 = Math.Min((double)bmpRotate.Width * p2.X / wpfWidth, bmpRotate.Width);
                            double y2 = Math.Min((double)bmpRotate.Height * p2.Y / wpfHeight, bmpRotate.Height);
                            double w = x2 - x1;
                            double h = y2 - y1;
                            System.Drawing.Rectangle rect
                               = new System.Drawing.Rectangle((int)x1, (int)y1, (int)w, (int)h);
    
                            //回転後画像を切り抜き
                            var bmpCliped = BitmapTool.CreateClipBitmap(bmpRotate, rect);
    
                            //保存
                            bmpCliped.Save(@"test.png", System.Drawing.Imaging.ImageFormat.Png);
                        }
                        #endregion
    
                        #region 方法2 表示用に回転させた画像からRenderTargetBitmapとCroppedBitmapを使って切り出し
                        {
                            //回転画像全体をBitmap化
                            var bmp = new System.Windows.Media.Imaging.RenderTargetBitmap((int)wpfWidth, (int)wpfHeight, 96d, 96d, PixelFormats.Pbgra32);
                            bmp.Render(imageGrid);
    
                            //回転画像から領域を指定して切り出し
                            System.Windows.Int32Rect r32 = new Int32Rect((int)r.Left, (int)r.Top, (int)r.Width, (int)r.Height);
                            var cropped = new System.Windows.Media.Imaging.CroppedBitmap(bmp, r32);
    
                            //保存
                            PngBitmapEncoder encoder = new PngBitmapEncoder();
                            encoder.Frames.Add(BitmapFrame.Create(cropped));
                            using (System.IO.FileStream fs = new System.IO.FileStream("test2.png", System.IO.FileMode.Create, System.IO.FileAccess.Write))
                            {
                                encoder.Save(fs);
                            }
                        }
                        #endregion
    
                    }
                    MessageBox.Show("保存しました");
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }
        }
    }
    namespace WpfApplication1
    {
        using System;
        using System.Drawing;
        using System.Linq;
    
        /// <summary>GDIでBitmap処理するクラス</summary>
        class BitmapTool
        {
            public static Bitmap GetImage(string filePath)
            {
                using (System.IO.FileStream fs = System.IO.File.OpenRead(filePath))
                {
                    return (Bitmap)Bitmap.FromStream(fs);
                }
            }
    
            /// <summary> 画像を切り出す </summary>
            /// <param name="bmpBase">元画像</param>
            /// <param name="rect">切り出し範囲</param>
            /// <returns>切り出した画像</returns>
            public static Bitmap CreateClipBitmap(Bitmap bmpBase, Rectangle rect)
            {
                Bitmap bmp = new Bitmap(rect.Width, rect.Height, bmpBase.PixelFormat);
                using (Graphics g = Graphics.FromImage(bmp))
                {
                    g.DrawImage(bmpBase, new Rectangle(0, 0, bmp.Width, bmp.Height), rect, GraphicsUnit.Pixel);
    
                    //g.DrawImage(bmpBase, rect, new Rectangle(0, 0, bmp.Width, bmp.Height), GraphicsUnit.Pixel);
                }
                return bmp;
            }
            /// <summary>画像を回転させる</summary>
            /// <param name="bmpBase">元画像</param>
            /// <param name="angleDeg">回転角度(°)</param>
            /// <returns>回転後の座標</returns>
            public static Bitmap CreateRotateBitmap(Bitmap bmpBase, float angleDeg)
            {
                return CreateRotateBitmap(bmpBase, angleDeg, Color.Black);
            }
            /// <summary>画像を回転させる</summary>
            /// <param name="bmpBase">元画像</param>
            /// <param name="angleDeg">回転角度(°)</param>
            /// <returns>回転後の座標</returns>
            public static Bitmap CreateRotateBitmap(Bitmap bmpBase, float angleDeg, Color backGround)
            {
                angleDeg = (angleDeg % 360 + 360) % 360;// 0~360に制限
    
                Bitmap bmpNew;
    
                if (angleDeg == 0)
                {
                    System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, bmpBase.Width, bmpBase.Height);
    
                    return CreateClipBitmap(bmpBase, rect);
                }
                else
                {
                    //元画像の四隅の座標
                    PointF[] ps = new[]
                        {
                            new PointF(0,0),
                            new PointF(bmpBase.Width,0),
                            new PointF(bmpBase.Width,bmpBase.Height),
                            new PointF(0,bmpBase.Height)
                        };
    
                    var matrix = new System.Drawing.Drawing2D.Matrix();
                    matrix.Rotate(angleDeg);//回転のアフィン変換を作る
    
                    matrix.TransformPoints(ps);//元画像を左上基準で回転させた後の各点の座標を計算
    
                    //回転後の画像が収まる範囲を調べる
                    float xmin = ps.Min(p => p.X);
                    float xmax = ps.Max(p => p.X);
                    float ymin = ps.Min(p => p.Y);
                    float ymax = ps.Max(p => p.Y);
    
                    //はみ出さないように平行移動
                    matrix.Translate(-xmin, -ymin, System.Drawing.Drawing2D.MatrixOrder.Append);
    
                    //新しい画像の幅と高さ
                    int newWidth = (int)Math.Ceiling(xmax - xmin);
                    int newHeight = (int)Math.Ceiling(ymax - ymin);
    
                    //新しいBitmapを用意
                    bmpNew = new Bitmap(bmpBase, newWidth, newHeight);
    
                    using (System.Drawing.Graphics g = Graphics.FromImage(bmpNew))
                    {
                        g.Clear(backGround);
                        g.Transform = matrix;//以降の描画がアフィン変換されるように指定
                        g.DrawImage(bmpBase, Point.Empty);//アフィン変換適用して複写
                    }
                }
                return bmpNew;
            }
        }
    }

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 編集済み gekkaMVP 2015年8月26日 15:08
    • 回答としてマーク ferret001 2015年8月27日 5:32
    2015年8月26日 13:04
  • gekka様、お世話になります。

    すごいサンプルの提示ありがとうございます。まさにこれです。

     

    //回転画像全体をBitmap化
    var bmp = new System.Windows.Media.Imaging.RenderTargetBitmap((int)wpfWidth, (int)wpfHeight, 96d, 96d, PixelFormats.Pbgra32);
    bmp.Render(imageGrid);

    //回転画像から領域を指定して切り出し
    System.Windows.Int32Rect r32 = new Int32Rect((int)r.Left, (int)r.Top, (int)r.Width, (int)r.Height);
    var cropped = new System.Windows.Media.Imaging.CroppedBitmap(bmp, r32);

    で使われいる各関数は、何度となくヒットしてますが使い方が分からず困り果てていました。このコードを理解するのに時間が必要ですが、いったん解決とさせていただきます。

    ありがとうございました。

    2015年8月27日 5:32