質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.51%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

3回答

9000閲覧

[C#]取得画像表示上でマウス座標がずれる。(PictureBoxSizeMode.Zoomの倍率を取得し、マウス座標に合わせたい。)

dendenmushi

総合スコア98

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

0クリップ

投稿2019/06/09 14:43

編集2019/06/10 00:01

###エラー内容、解決したい内容が異なります。[以前の質問]

前提・実現したいこと

C#Windowsformデスクトップアプリ内に、ローカル画像をドラッグ&ドロップ(以下D&D)で運んで表示したあとに、その表示された画像の上でD&Dで四角を任意選択して画像を切り取り保存をしたいです。

環境:windows10 64bit
開発ツール:Visual Studio 2017 community
言語:C#(windows Forms visual C#)
イメージ説明

発生している問題・エラーメッセージ

画像をローカルからD&Dでアプリ側で表示し、その上でD&Dをした際に点線の四角形は出現して枠組みまではできますが、その枠がマウスポインターとずれる。

###コード

C#

1using System; 2using System.Collections.Generic; 3using System.ComponentModel; 4using System.Data; 5using System.Drawing; 6using System.Drawing.Imaging; 7using System.IO; 8using System.Linq; 9using System.Text; 10using System.Threading.Tasks; 11using System.Windows.Forms; 12 13namespace WindowsFormsApp_20190604 14{ 15 public partial class OCR_Application : Form 16 { 17 Point MD = new Point(); 18 Point MU = new Point(); 19 Bitmap backgroundBitmap;//20190605 20 Bitmap offscreenBitmap; 21 Bitmap cut_change_canvas; 22 bool view = false; 23 float scale; 24 25 public OCR_Application() 26 { 27 InitializeComponent(); 28 //プロパティの設定変更 29 pictureBox1.AllowDrop = true; 30 pictureBox1.Parent = panel1; 31 32 } 33 34 //画像をローカルからD&Dしアプリ内に置くメソッド1 35 private void pictureBox1_DragDrop(object sender, DragEventArgs e) 36 { 37 string[] files = (string[])e.Data.GetData(DataFormats.FileDrop, false); 38 for (int i = 0; i < files.Length; i++) 39 { 40 string fileName = files[i]; 41 //textBox1.Text += fileName + "\r\n"; 42 43 string filename = ((string[])e.Data.GetData(DataFormats.FileDrop))[0]; 44 //Bitmap bitmap = null; 45 backgroundBitmap = new Bitmap(filename); 46 47 //offscreenBitmap = new Bitmap(pictureBox1.Width, pictureBox1.Height); 48 49 offscreenBitmap = new Bitmap(backgroundBitmap); 50 51 //表示方法 52 pictureBox1.SizeMode = PictureBoxSizeMode.Zoom; 53 54         //α 55 56 pictureBox1.Image = offscreenBitmap; 57 58 59 } 60 } 61 //画像をローカルからD&Dしアプリ内に置くメソッド2 62 private void pictureBox1_DragEnter(object sender, DragEventArgs e) 63 { 64 if (e.Data.GetDataPresent(DataFormats.FileDrop)) 65 e.Effect = DragDropEffects.Copy; 66 } 67 68 69 //座標取得メソッド 70 private void GetRegion(Point p1, Point p2, ref Point start, ref Point end) 71 { 72 start.X = Math.Min(p1.X, p2.X); 73 start.Y = Math.Min(p1.Y, p2.Y); 74 75 end.X = Math.Max(p1.X, p2.X); 76 end.Y = Math.Max(p1.Y, p2.Y); 77 } 78 79 private int GetLength(int start, int end) 80 { 81 return Math.Abs(start - end); 82 } 83 84 //四角形に描くメソッド 85 private void DrawRegion(Point start, Point end) 86 { 87 Pen blackPen = new Pen(Color.Black); 88 89 Graphics g = Graphics.FromImage(offscreenBitmap); 90 91 // 描画する線を点線に設定 92 blackPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash; 93 94 // 画面を消去 95 g.Clear(SystemColors.Control); 96 97 //β 98 99 g.DrawRectangle(blackPen, start.X, start.Y, GetLength(start.X, end.X), GetLength(start.Y, end.Y)); 100 101 102 g.Dispose(); 103 104 } 105 106 107 //アプリ内画像の範囲指定の際のD&Dをし始めた座標取得 108 private void pictureBox1_MouseDown(object sender, MouseEventArgs e) 109 { 110 // 描画フラグON 111 view = true; 112        113       //γ 114 115 // Mouseを押した座標を記録 116 MD.X = eX;//e.X →eX 117 MD.Y = eY;//e.Y →eY 118 119 } 120 121 //アプリ内画像の範囲指定の際のD&Dをしマウスポインターが動いている最中の座標取得 122 private void pictureBox1_MouseMove(object sender, MouseEventArgs e) 123 { 124 Point p = new Point(); 125 Point start = new Point(); 126 Point end = new Point(); 127 128 // 描画フラグcheck 129 if (view == false) 130 { 131 return; 132 } 133 134 // カーソルが示している場所の座標を取得 135 p.X = e.X; 136 p.Y = e.Y; 137 138 // 座標から(X,Y)座標を計算 139 GetRegion(MD, p, ref start, ref end); 140 141 // 領域を描画 142 DrawRegion(start, end); 143 144 pictureBox1.BackgroundImageLayout = ImageLayout.Zoom; 145 146 //初めにとっておいた背景bitmapを表示 147 pictureBox1.BackgroundImage = backgroundBitmap; 148 149 //bitmapの透過 150 offscreenBitmap.MakeTransparent(); 151 152 //Imageを表示 153 pictureBox1.Image = offscreenBitmap;//最後の点線四角形が描画される 154 } 155 156 //アプリ内画像の範囲指定の際のD&Dをし終えた最後の座標取得 157 private void pictureBox1_MouseUp(object sender, MouseEventArgs e) 158 { 159 160 Point start = new Point(); 161 Point end = new Point(); 162 163 164 // Mouseを離し た座標を記録 165 MU.X = e.X; 166 MU.Y = e.Y; 167 168 // 座標から(X,Y)座標を計算 169 GetRegion(MD, MU, ref start, ref end); 170 171 // 領域を描画 172 DrawRegion(start, end); 173 174 // 描画フラグOFF 175 view = false; 176 177 } 178 } 179} 180

試したこと

上のコードのαの位置に以下コードを追加しまずpictureBox1のZoomによって元画像が何倍で縮小されているのか表示しようと思いました。

C#

1 scale = Math.Min(pictureBox1.Width / offscreenBitmap.HorizontalResolution, pictureBox1.Height / offscreenBitmap.VerticalResolution); 2

また、上のコードのβの場所に以下コードを記載し、マウスポインターの位置を調整しようと試みました。

C#

1 2 //縮小された長さと元の長さの差分をstar.XやYに足す 3 start.X = start.X - (start.X * (int)scale - pictureBox1.Width / 2); 4 start.Y = start.Y - (start.Y * (int)scale - pictureBox1.Height / 2); 5 end.X = end.X - (end.X * (int)scale - pictureBox1.Width / 2); 6 end.Y = end.Y - (end.Y * (int)scale - pictureBox1.Height / 2); 7

次にγの場所に以下コードを追記しました。

C#

1 2 3 //縮小された長さと元の長さの差分をstar.XやYに足す 4 int eX = e.X - (e.X * (int)scale - pictureBox1.Width / 2); 5 int eY = e.Y - (e.Y * (int)scale - pictureBox1.Height / 2); 6

でもこのように調整しても座標はずれてしまっていました。
考えられる原因は以下です。
1:そもそも倍率がうまく抽出できていない
2:ただの倍率ではなくpictureのzoomは中心座標などを使い縮小表示している?

###現在の状況
◆アプリにD&Dする画像サイズ(水色塗り)
イメージ説明

◆D&D後の左上の×印から右下の×印に向かってD&Dをしたところ、左角にある点線が表示されます。
(自分で試したαβγを行ったときは点線が現れませんでした。)
イメージ説明

参考になるサイト、書籍、ヒント、アドバイスよろしくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ikedas

2019/06/09 15:07

マウスポインタの位置を画像に×印などで書き込んで下さいますか。あとコードは```で囲んだほうが見やすいです。
dendenmushi

2019/06/09 23:43

ありがとうございます。コードくくれていませんでした。画像も現状のをアップし修正しました。よろしくお願い致します。
guest

回答3

0

試していないので違っていたらすみませんが、

pictureBox1.SizeModeで指定するのはあくまでもビットマップの表示方法であって、pictureBox1上の座標が変換されるわけではないです。またZoomにするとビットマップの縦横比率は保持してピクチャボックスいっぱいになるように表示するので、ビットマップの左上隅がピクチャボックスの左上隅に来るとは限りませんよね。

まずは、Graphics g = pictureBox1.CreateGraphics();として、ピクチャボックス自身に四角形を描画する (縮小率の計算は不要です) ようにし、それがうまくいってから画像のどこを切り取ったかを計算する方法を考えてはどうでしょうか。


なおついでに、MouseLeaveが発生したときもviewをリセットして描画をクリアすべきかとも思います。

投稿2019/06/10 03:12

編集2019/06/10 03:15
ikedas

総合スコア4198

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dendenmushi

2019/06/10 09:23

回答ありがとうございます。試してみます。いろいろ皆様から回答頂き感謝です。
guest

0

ベストアンサー

まず、本質問とは直接関係がありませんが、ラバーバンドの描画方法にかなり無駄な部分があるかと思います。
MouseMoveイベントにていろいろやっていますが、ラバーバンドの座標決定のみを行い、描画はPaintイベントにて行うほうがよろしいかと思います。

C#

1Point start; 2Point end; 3 4 5//画像をローカルからD&Dしアプリ内に置くメソッド1 6private void pictureBox1_DragDrop(object sender, DragEventArgs e) 7{ 8 string[] files = (string[])e.Data.GetData(DataFormats.FileDrop, false); 9 for (int i = 0; i < files.Length; i++) 10 { 11 string fileName = files[i]; 12 13 string filename = ((string[])e.Data.GetData(DataFormats.FileDrop))[0]; 14 backgroundBitmap = new Bitmap(filename); 15 16 pictureBox1.Invalidate(); 17 } 18} 19 20//四角形に描くメソッド 21private void DrawRegion(Graphics g, Point start, Point end) 22{ 23 using (Pen blackPen = new Pen(Color.Black)) 24 { 25 // 描画する線を点線に設定 26 blackPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash; 27 g.DrawRectangle(blackPen, start.X, start.Y, GetLength(start.X, end.X), GetLength(start.Y, end.Y)); 28 } 29 30} 31 32//アプリ内画像の範囲指定の際のD&Dをし始めた座標取得 33private void pictureBox1_MouseDown(object sender, MouseEventArgs e) 34{ 35 // 描画フラグON 36 view = true; 37 38 // Mouseを押した座標を記録 39 MD = e.Location; 40 start = e.Location; 41 end = e.Location; 42 43} 44 45//アプリ内画像の範囲指定の際のD&Dをしマウスポインターが動いている最中の座標取得 46private void pictureBox1_MouseMove(object sender, MouseEventArgs e) 47{ 48 // 描画フラグcheck 49 if (view) 50 { 51 // 座標から(X,Y)座標を計算 52 GetRegion(MD, e.Location, ref start, ref end); 53 54 // 領域を描画 55 pictureBox1.Invalidate(); 56 } 57} 58 59//アプリ内画像の範囲指定の際のD&Dをし終えた最後の座標取得 60private void pictureBox1_MouseUp(object sender, MouseEventArgs e) 61{ 62 // 座標から(X,Y)座標を計算 63 GetRegion(MD, e.Location, ref start, ref end); 64 65 // 領域を描画 66 pictureBox1.Invalidate(); 67 68 // 描画フラグOFF 69 view = false; 70 71} 72 73private void pictureBox1_Paint(object sender, PaintEventArgs e) 74{ 75 // 画像を表示する。 76 // このままではアスペクト比が崩れるのでその辺を考えてください 77 if(backgroundBitmap != null) e.Graphics.DrawImage(backgroundBitmap, e.ClipRectangle); 78 79 DrawRegion(e.Graphics,start, end); 80 81} 82

元のソースからあまり変更せずに書きましたので、細かい部分までは見ていません。
DrawImageのアスペクト比保持に関しては、検索すればいろいろ出てくるかと思います。
この方法の場合、Imageの描画を自前で行っていますのでSizeModeプロパティは関係ありません。

それで、PictureBoxの座標からImageの座標への変換関数を作ってやれば、画像のトリミングを
行えるかと思います。

投稿2019/06/10 02:39

YAmaGNZ

総合スコア10220

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dendenmushi

2019/06/10 09:24

Imageの描画を自前で…勉強不足ですぐに理解できませんでした。試してまた報告させて頂きます。本当に感謝です。ありがとうございます。
guest

0

αの位置なら、pictureBox1.SizeMode は AutoSize なので、拡大縮小率は100パーセントです。
また、拡大縮小率なら、解像度は関係ありません。100ドットのものを90ドットの所に表示したとしたら、拡大縮小率はいくらですか?というような各種の値を知って、「どうあるべき」を事前に組み立て、実際の値を調べて検証してください。
βに書こうとしているコードも、おかしいです。「率」のはずなのに、「足す」ってなんですか?

スクロールバーを出すようにしたPanelの上にAutoSizeにしたPictureBoxを置くのが、何も変換する必要がないので楽です。

投稿2019/06/09 23:53

Q71

総合スコア995

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dendenmushi

2019/06/10 00:06 編集

大変申し訳ないです。AutoSizeが間違えでZoomで行ったところ現状の画像のような状態になっております。コードの個所修正しました。 おっしゃるとおりでAutosizeで表示したほうがスクロールバーで表示されて大変楽にできたのですが、指定範囲を切り取るという作業をしていけるようにしたいので、Zoomで全体を見れるようにしたほうがいいと思っていまして、zoomの縮小率を出すやり方を調べているのですが、今わからない状態におります。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.51%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問