トップ回答者
画像の拡大・縮小・切り取り・貼り付けについて

質問
-
Windowsフォームの画像にて、マウスで範囲を指定し、
その指定した範囲の拡大・縮小・切り取り・貼り付けという4つの操作を行おうと思います。(開発言語:VB.NET 2003)
■今現在の設定:
①フォームにPictureBoxコントロールを追加
②プロパティの設定状態:
(1)imageプロパティにはSample.jpegを設定(imageプロパティ:System.Drawing.Bitmapと反映されている)
(2)Name:pbに設定
(3)Cursor
efaultに設定
<Cursorプロパティの設定で、実行した時に、範囲選択が可能かと勘違いして、いろいろと設定を変えてみたのですが、結局Cusorの単なる表示形式の設定でした。>
■ここで、「拡大・縮小・切り取り・貼り付け」をするには、
最初に、コントロール(画像)の範囲内で、クリックしたらその位置を取得できるようにしないといけない。
そこで、ヘルプでPictureBoxのメンバを調べたら、CanFocus(フォーカスを受け取ることができるかどうかを示す値を取得)とか、CanSelectとか、いろいろ工夫してみたのですが、どうもうまくいきませんでした。
と言いますか、実際VB.NET 2003で画像の指定された範囲を拡大・縮小・切り取り・貼り付けができるんでしょうか?
どなたか詳しい方・実際開発されたことがある方がおったら、教えていただけないんでしょうか?
よろしく、お願いします。
回答
-
外池です。
切り貼りだけであっても進展されたようで、何よりです。
で、調査優先順位1の「拡大してぼやけた画像をどうやってはっきり表示させるか?」ですが・・・、
ごくごく一般論として「無理」なのではないかと。元の画像データの解像度より細かいところで埋もれてしまい一度情報が失われてしまったら、解像度を上げた画像にしても復元することは出来ません。
一方で、市販の画像処理ソフトなどでは、拡大したり縮小したり自由自在にできて、かつ、切り貼りもできて、という感じでとても便利なわけですが、
- 元の高解像度のデータはメモリ上に保持しつつ、
-
画面上には縮小した低解像度な画像で表示し、
-
画面上で切り貼り作業が行えているように見えても、実際には、選択した領域が元の高解像度のデータのどの部分に該当するのか計算して、メモリ上に保持されている1.の元の高解像度のデータに対して修正を加え、改めて、2.の縮小した低解像度な画像にして表示する、
というような感じの流れです。つまり、常に、高解像度の元のデータを保持し続けているわけで、画面上で拡大したように見えても、元のデータを縮小する割合を少なくしているだけです。
すべての返信
-
外池と申します。
「と言いますか、実際~(以下省略)」のご質問ですが、そりゃ、プログラムさえ組めば「なんでもできます」よ?
ただ、PictureBoxコントロールに画像編集の機能があるか? という意味の質問であれば、そのような機能はありません。
「最初に、(途中省略) クリックしたらその位置を取得できるようにしないといけない。」のアプローチは大変正しいと思います。まずは、これを頑張ってやってみてください。MouseDownイベントを使えば、マウスのボタンを押し下げた場所の情報が得られます。MouseUpイベントを使えば、ボタンを離した場所の情報が得られます。
次は何をしますか? 画像編集ソフトの使い勝手を真似すれば、上述のMouseDownとMouseUpの間で、ボタンを押し下げたままドラッグしていけば、選択しようとしている範囲を四角形か何かで表示してくれますよね? そうするには、MouseMoveイベントが使えるんじゃないでしょうか? 次々に四角形の大きさを変化させながら表示を更新していけばよいかと。
以上は、画面上の場所(編集対象となる四角形の範囲)を指定するためのインターフェイスの問題。
あと、実際に画像を編集するとなれば、Bitmapクラスのオブジェクトを使えばよいでしょう。GraphicsというオブジェクトをBitmapから作成すれば、Bitmapに対して様々な描画を行えます。途中の詳細をすっとばしますが、結果的に、Bitmapの一部に別のBitmapを張り込むことは可能です。張り込む際にサイズを変更することも可能です。
-
★こんにちは★
私も前VB.NET2003で画像の処理したことがあります。
今はVB2005ですが。。。
K.Tyou さんからの引用 ■ここで、「拡大・縮小・切り取り・貼り付け」をするには、
最初に、コントロール(画像)の範囲内で、クリックしたらその位置を取得できるようにしないといけない。
そこで、ヘルプでPictureBoxのメンバを調べたら、CanFocus(フォーカスを受け取ることができるかどうかを示す値を取得)とか、CanSelectとか、いろいろ工夫してみたのですが、どうもうまくいきませんでした。
と言いますか、実際VB.NET 2003で画像の指定された範囲を拡大・縮小・切り取り・貼り付けができるんでしょうか?
どなたか詳しい方・実際開発されたことがある方がおったら、教えていただけないんでしょうか?
よろしく、お願いします。
詳しく教えていただきたいのですが、位置を取得とはpicturebox自体をって事でしょうか??
picturebox1ならpicturebox1。picturebox2ならpicturebox2とかって事でしょうか??
あと、範囲を拡大とはPictureBoxSizeMode.Zoomとかの事ですか?
それともこんな感じのですか??↓
http://hpcgi1.nifty.com/MADIA/VBBBS/wwwlng.cgi?print+200701/07010049.txt
私もこのような感じを作成したことがあります。
もう少し詳しく記入していただけますか??
-
K.Tyou さんからの引用 最初に、コントロール(画像)の範囲内で、クリックしたらその位置を取得できるようにしないといけない。
たぶん,画像を選択する矩形を表示し,その矩形の画像を取得したいのだと読み取りました。
Graphicsオブジェクトで矩形を描こうとしてもXorの指定ができないのでもとに戻すのが大変です。
しかたがないので私はいつもWin32APIのDrawFocusRectを使っています。
これなら描いた矩形をもう一度上書きすれば矩形が消えるので簡単です。
サンプルは選択した矩形部分を切り取って別のフォームに表示するものです。参考にしてください。
Code SnippetImports System.Runtime.InteropServices
Public Class Form1
Dim fr As FocusRect
Dim p As PointDim WithEvents Picturebox1 As New PictureBox
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.Picturebox1.Dock = DockStyle.Fill
Me.Picturebox1.Image = New Bitmap("C:\Documents and Settings\All Users\Documents\My Pictures\Sample Pictures\Water lilies.jpg")
Me.Controls.Add(Picturebox1)
fr = New FocusRect(Me.Picturebox1)
End SubPrivate Sub PictureBox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Picturebox1.MouseDown
p = e.Location
fr.Draw(FocusRect.GetRect(p, p))
End SubPrivate Sub PictureBox1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Picturebox1.MouseMove
If e.Button = Windows.Forms.MouseButtons.Left Then
fr.Clear()
fr.Draw(FocusRect.GetRect(p, e.Location))
End If
End SubPrivate Sub PictureBox1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Picturebox1.MouseUp
fr.Clear()
Dim r As Rectangle = fr.FocusRect
Dim s As Size = r.Size
If s.Equals(New Size(0, 0)) Then
Exit Sub
End If
Dim img As New Bitmap(s.Width, s.Height)
Using g As Graphics = Graphics.FromImage(img)
g.DrawImage(Me.Picturebox1.Image, New Rectangle(0, 0, img.Width, img.Height), r, GraphicsUnit.Pixel)
End Using
Dim f As New Form
f.BackgroundImage = img
f.BackgroundImageLayout = ImageLayout.None
f.ClientSize = img.Size
f.Show()
End SubEnd Class
Class FocusRect
Private _focusRect As Rectangle
Private _pic As PictureBox<DllImport("user32.dll")> _
Private Shared Function DrawFocusRect(ByVal hDC As IntPtr, ByRef lprc As RECT) As Boolean
End Function<StructLayout(LayoutKind.Explicit)> _
Private Structure RECT
<FieldOffset(0)> Dim _left As Integer
<FieldOffset(4)> Dim _top As Integer
<FieldOffset(8)> Dim _right As Integer
<FieldOffset(12)> Dim _bottom As Integer
Public Sub New(ByVal left As Integer, ByVal top As Integer, ByVal right As Integer, ByVal bottom As Integer)
_left = left
_top = top
_right = right
_bottom = bottom
End Sub
End StructureSub New(ByVal pic As PictureBox)
Me._pic = pic
End SubPublic Shared Function GetRect(ByVal p1 As Point, ByVal p2 As Point) As Rectangle
If p1.X > p2.X Then
Swap(p1.X, p2.X)
End If
If p1.Y > p2.Y Then
Swap(p1.Y, p2.Y)
End If
Return New Rectangle(p1, New Size(Point.Subtract(p2, p1)))
End FunctionPrivate Shared Sub Swap(ByRef a As Integer, ByRef b As Integer)
Dim tmp As Integer
tmp = a
a = b
b = tmp
End Sub'矩形を描く
Public Sub Draw(ByVal r As Rectangle)
Dim newRect As New RECT(r.Left, r.Top, r.Right, r.Bottom)
Dim hDC As IntPtr = Me._pic.CreateGraphics.GetHdc
DrawFocusRect(hDC, newRect)
Me._focusRect = r
End Sub'直前に描いた矩形を消す
Public Sub Clear()
Dim oldRect As New RECT(Me._focusRect.Left, Me._focusRect.Top, Me._focusRect.Right, Me._focusRect.Bottom)
Dim hDC As IntPtr = Me._pic.CreateGraphics.GetHdc
DrawFocusRect(hDC, oldRect)
End Sub'選択した矩形を返す
ReadOnly Property FocusRect() As Rectangle
Get
Return Me._focusRect
End Get
End PropertyEnd Class
-
外池さん のんさん Yas.sさん いろいろと アドバイスありがとうございました。
まだ、みなさんから頂いたアドバイスで試行錯誤中です。
最初から、質問した内容についてなんですが、実は社内での開発にあたっての、調査になります。
今現在、また調査の重点が変わって、先に「拡大した画像がぼんやりぼやけている。そのぼんやりした画像をはっきり表示させるための制御方法」を検討中です。
縮小した画像はもちろんはっきりと反映されるのですが、拡大した場合は、当然ながらぼやけて見えます。
このぼやけた画像をどうやって、はっきり表示させれるか 教えていただけないでしょうか?
今現在とりあえず、画像全体の拡大・画像全体の縮小・画像範囲指定して切り取りは成功しました。
しかし、画像範囲指定して切り取りは、ラバーバンドでの範囲指定ではなく、コーディング上指定しました。
切り取りは以下のようにコーディングしています。
Private Sub bt切り取り_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bt切り取り.Click
'Bitmapオブジェクトの作成
Dim img = New Bitmap("C:\画像関連調査\WindowsApplication1\img\IT.jpg")'PictureBox1のGraphicsオブジェクトの作成
Dim g As Graphics = pb.CreateGraphics()'切り取り範囲指定(補足:ここの切り取り範囲指定を実はラバーバンドでの指定ができればと思います。)
Dim rect As New Rectangle(10, 10, 50, 50)
'貼り付け先指定
g.DrawImage(img, 320, 200, rect, GraphicsUnit.Pixel)'Graphicsオブジェクトを破棄
g.Dispose()End Sub
■調査優先順位:
①拡大してぼやけた画像をどうやってはっきり表示させるか?
②画像の上で、ラバーバンドして、範囲取得し、その選択範囲だけを拡大・縮小・切り取りするには?
(ラバーバンドについても今調べ中です)
-
外池です。
切り貼りだけであっても進展されたようで、何よりです。
で、調査優先順位1の「拡大してぼやけた画像をどうやってはっきり表示させるか?」ですが・・・、
ごくごく一般論として「無理」なのではないかと。元の画像データの解像度より細かいところで埋もれてしまい一度情報が失われてしまったら、解像度を上げた画像にしても復元することは出来ません。
一方で、市販の画像処理ソフトなどでは、拡大したり縮小したり自由自在にできて、かつ、切り貼りもできて、という感じでとても便利なわけですが、
- 元の高解像度のデータはメモリ上に保持しつつ、
-
画面上には縮小した低解像度な画像で表示し、
-
画面上で切り貼り作業が行えているように見えても、実際には、選択した領域が元の高解像度のデータのどの部分に該当するのか計算して、メモリ上に保持されている1.の元の高解像度のデータに対して修正を加え、改めて、2.の縮小した低解像度な画像にして表示する、
というような感じの流れです。つまり、常に、高解像度の元のデータを保持し続けているわけで、画面上で拡大したように見えても、元のデータを縮小する割合を少なくしているだけです。
-
早速なご回答ありがとうございます。
外池 さんからの引用 ごくごく一般論として「無理」なのではないかと。元の画像データの解像度より細かいところで埋もれてしまい一度情報が失われてしまったら、解像度を上げた画像にしても復元することは出来ません。
一方で、市販の画像処理ソフトなどでは、拡大したり縮小したり自由自在にできて、かつ、切り貼りもできて、という感じでとても便利なわけですが、
- 元の高解像度のデータはメモリ上に保持しつつ、
-
画面上には縮小した低解像度な画像で表示し、
-
画面上で切り貼り作業が行えているように見えても、実際には、選択した領域が元の高解像度のデータのどの部分に該当するのか計算して、メモリ上に保持されている1.の元の高解像度のデータに対して修正を加え、改めて、2.の縮小した低解像度な画像にして表示する、
というような感じの流れです。つまり、常に、高解像度の元のデータを保持し続けているわけで、画面上で拡大したように見えても、元のデータを縮小する割合を少なくしているだけです。
なるほどですね!!今回の調査とは別にとても勉強になりました。
今回の処理内容なんですが、以下のものです。
1.美容室でデジカメでお客さんのヘアスタイルの写真をとり、それをパソコンに画像として保存する。
2.写真の上で、ラバーバンドして拡大したり縮小したりしたい。
ところで、外池さんの説明からすると、「元のデータ自体が高解像度」っていうことが必要であって、それをメモリ上に常に保持する必要があるとのことですよね!
そうならば、デジタルカメラで写真を撮った時点で、それは、高解像度のデータではないと、所詮拡大処理とかではっきりと表示させようとするのは、理論上無理とのことですよね?
デジカメに詳しくないからよくわからないのですが、高解像度の機能を持っているデジカメとか本当にあるんでしょうか?
本当に高解像度の機能を持っているデジカメがあって、また高解像度のデータ(写真)が撮れるならば、実際、メモリに保存させといて、画面上には縮小した低解像度な画像で表示させるということは実現可能性があるんでしょうか?
たて続き質問ばかりですいません!
よろしくお願いします。
-
外池です。
おーーー。面白そうなアプリですね。要素操作のプログラミングのテクニックの議論もですが、こういう、全体像が見える対話は非常に面白いし・・・、もっと言えば、この全体像が無いと、結果的に無理なことを知らないまま空論をやりとりしてしまうことになるので怖いところです。こういうご説明を頂けると非常にハッキリしますよね。
で、全然大丈夫です。
今のデジカメの解像度は、それほど高級な機種でなくても、驚くほど細かいところまで写るはずです。ヘアスタイルの写真ということであれば、頭部だけの写真になると思いますが・・・、極端な話、毛穴まで写るかも。例えば、横1600ドット×縦1200ドットの解像度で元画像が撮れるとして、これで、たったの2メガピクセル(画素数=ドットの総数)ですからね? 今のデジカメは数百メガピクセルですから、縦横ともさらに10倍ぐらいの画素数があるのでしょう。(私のデジカメは数メガピクセル程度) 普通のパソコンの画面から、思いっきりはみ出すほどに大量の情報量があります。
で、大きい画素数の、例えば、jpgファイルでも、これは、簡単に縮小して表示できます。これはGraphicsオブジェクトのDrawImageメソッドについてドキュメントを調べてもらえればわかると思います。
-
外池 さんからの引用 おーーー。面白そうなアプリですね。要素操作のプログラミングのテクニックの議論もですが、こういう、全体像が見える対話は非常に面白いし・・・、もっと言えば、この全体像が無いと、結果的に無理なことを知らないまま空論をやりとりしてしまうことになるので怖いところです。こういうご説明を頂けると非常にハッキリしますよね。
そうですよね!私も、ものすごく楽しみにしています。今回PGを担当することになって、結構わくわくしています。
外池 さんからの引用 今のデジカメの解像度は、それほど高級な機種でなくても、驚くほど細かいところまで写るはずです。ヘアスタイルの写真ということであれば、頭部だけの写真になると思いますが・・・、極端な話、毛穴まで写るかも。例えば、横1600ドット×縦1200ドットの解像度で元画像が撮れるとして、これで、たったの2メガピクセル(画素数=ドットの総数)ですからね? 今のデジカメは数百メガピクセルですから、縦横ともさらに10倍ぐらいの画素数があるのでしょう。(私のデジカメは数メガピクセル程度) 普通のパソコンの画面から、思いっきりはみ出すほどに大量の情報量があります。
さっきの外池さんの市販の画像処理ソフトの説明で、自分の考え方が間違っていることに気付きました
自分は、元を拡大する―この時点ではぼんやりしても、その次に、制御で高解像度のデータに加工すると勘違いしてました。
さんからの引用 ■説明を聞いてから、このように整理しています。
①デジカメを利用して、元の高解像度のデータ(写真)をパソコンに保存する
②アプリを通して、ユーザに見せる時は、ある程度の低解像度のデータにする
③写真の上で、ラバーバンドして、「拡大」ボタンをクリックさせる⇒これで、拡大されるわけですが・・・
☆実際の裏処理では、A。ラバーバンドした範囲を記憶する
B。Aが元の高解像度のデータ上での範囲を計算する
C。「②の低解像度 < Bの解像度 < ①の高解像度 」に一致する解像度に表示させる
こうなると、次のステップとしては、今回ラバーバンド範囲の取得ですが、写真の上でMouseDownを4回しても おそらく
処理としては可能かもしれないのですが、少し格好悪いかと思いまして・・・
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=15742&forum=7&2
こちらのサイトを参考して、ContolPaintクラスでラバーバンドを描けるそうなんですが、勉強不足でできませんでした。
といいますか、写真の上で、ラバーバンドの選択範囲の点線を表示することもできてません!
何かアドバイスがありましたら、ぜひご教授お願いします。
-
外池です。
ここでは、単純に画像の表示と、その上で「選択範囲」の四角を描く(マウスの動きに合わせてアニメーションさせる)方法に限定して書きますが・・・、
私の考えでは、PictureBoxコントロールだけで実現できるのではないかと思います。
PictureBoxには固定的(プロパティーとしてセットしてしまう)、あるいは、半固定的(プログラムによって入れ替え可能)に画像を表示する機能があります。PictureBoxのImageプロパティーです。このプロパティーを使って、操作をしたい対象の画像を表示させておけばよいかと思います。元の高解像度の画像を小さくして(Bitmapクラスを使って内部的な画像の操作ができます)、Imageプロパティーにセットするわけです。
あと、PictureBoxのPaintイベントを使って四角形を描くわけですが、これは、結構複雑な動作になります。
-
まずMouseDownイベントで選択操作開始、最初の座標を得る。
-
次にMouseMoveイベントでマウスの移動量を得て、上述の座標と組み合わせて描くべき四角形の位置と大きさを確定します。そして、Invalidateメソッドを呼び出せば画像を更新すべきこと(四角形を描きなおすべきこと)をシステムに通知します。この結果Paintイベントが自動的に呼び出されますので、Paintイベントには選択操作中に限って四角形を描くプログラムを書いておきます。
-
MouseUpイベントで選択操作終了。最終的な座標を得ます。
自作でガチャガチャと作ってみたら、デジカメで撮った画像を表示させつつ、その上に四角形をマウスの動きに合わせて描くことは、ちゃんとできましたね・・・。
あくまで、考え方、だけですので、細かいところは、工夫してください。結構落とし穴は多いです。マウスを左に動かしたり、上に動かしたりすると・・・、マイナスの巾や高さの値になって四角形が描けないとか・・・。
がんばってください。
-