窓助タッチバージョンアップ

自作ソフト 窓助タッチ をバージョンアップしました。
http://www.geocities.jp/mocchi_2003/soft.html

最新バージョンは 1.12です。

このソフトを使うと、前面にあるウィンドウの透明度を調節したり、そのウィンドウの一枚下のウィンドウを、前面にあるウィンドウを
移動させずに操作できるようになります。

今までは透明度を調節する方法として、マウスでつまみを移動するようにしていましたが、キー操作だけで透明度を調節できるようにするとより便利になりますので、今回のバージョンアップでその機能を追加してみました。

キー操作で透明度を調節する手順は、
(1) 窓助タッチを起動
(2) 透明度を調節したいウィンドウを前面に表示
(3) Shiftキーを押す、Shiftキーを離す、Shiftキーを押す、Shiftキーを離す、Ctrlキーを押す
(4) Ctrlキーを押したまま、↑↓キーで透明度の調整
(5) Ctrlキーを押したまま、spaceキーで透過状態の変更
(6) Ctrlキーを押したまま、F4キーで窓助タッチ終了

(3)の操作が何となく格闘ゲームっぽいですね^^;

(3)の操作が成功すると、窓助タッチの上側のバーの色が青から赤に変わります。
バーの色が赤に変わっているときだけ、↑↓キー、spaceキー、F4キーによる操作が有効になります。
また、バーの色が赤に変わっているときはウィンドウのフォーカスが窓助タッチに移動してますので、透明度を調節しているウィンドウにはキー入力が入りません。
Ctrlキーを離すとまたバーの色が青に戻ります。

ぜひ、お試しください!

はてなダイアリーではいろいろなことが表現できます

はてなダイアリーは普通のブログサービスと異なり、1日分の記事は全てまとめて編集することができます。1日に複数の記事を書きたいときは、記事の先頭に「*」+記事のタイトルを書き込んでください。このサンプルでも利用しているので、「編集」リンクを押して編集画面に行き、実際にどのように記述しているのかをチェックしてみてください。
写真をたくさん貼りたいときには、フォトライフを利用することができます。写真のアップロードや貼り付けは、編集画面から簡単に行うことができます。
はてなダイアリーでは、単に文章を記述するだけでなく、書籍やCDを紹介したり、文字の大きさを指定したり、さらには表やリストを作ることもできます。
例えば編集画面に用意された「はまぞう」をつかうと書籍を簡単に紹介できます。

「へんな会社」のつくり方 (NT2X)

「へんな会社」のつくり方 (NT2X)

さらに、表を書いたり、動画や地図を貼り付けることもできます。さらにくわしい説明ははてなダイアリーのヘルプをご覧ください。

はてなダイアリーへようこそ!

これはあなた専用のブログです。早速記事を書いてみてください。記事の編集は、日付欄の横にある「編集」リンクより行うことができます。日々の出来事、本やCD、テレビ番組や出来事の感想、普段考えていることなど、内容は自由です。自分ならではのブログを書いて、楽しんでください!
なお、この文章はサンプルです。実際に自分の記事を書くときには削除してください。
削除を行うには、編集ページで書かれていることを全て消すか、編集画面の一番下にある「日記の削除」をクリックしてください。
また、明日から新しい記事を書く際には、画面右上の「日記を書く」リンクから編集画面にアクセスしてください。

NPlot で値の表示範囲を設定する方法

割と基本的なことだとは思いますが、意識せずにプログラミングすると
嵌る恐れがある(というか、嵌りました)ので、やり方をメモします。

表示範囲を指定してグラフを描画したいときは、XAxis 及び YAxis の
WorldMax, WorldMin にそれぞれの値を設定すればよいです。
なので、例えば横軸の範囲を0以上60以下、縦軸の範囲を30以上100以下
とした折れ線グラフlinePlot1 を plotSurface2D1 に表示したい時は

// plotSurface2D1 は NPlot.Windows.PlotSurface2D クラスのオブジェクト。
// (GUIデザイナで貼り付けられるオブジェクト)
InitializeComponent();

NPlot.LinePlot linePlot1 = new NPlot.LinePlot();

// 横軸
plotSurface2D1.XAxis1.WorldMin = 0; // (1)
plotSurface2D1.XAxis1.WorldMax = 60;

// 縦軸
plotSurface2D1.YAxis1.WorldMin = 30;
plotSurface2D1.YAxis1.WorldMax = 100;

plotSurface2D1.Add(linePlot1); // (2)
plotSurface2D1.Refresh();

とすれば… 動きませんorz。 (1)の行で NullReferenceException
が発生してしまいます。XAxis1、YAxis1が、InitializeComponent()
メソッド終了時にはまだ作成されていないことが原因です。
(2)のメソッドの中でそれぞれの軸のオブジェクトがが作成されるよう
なので、一番簡単な解決方法は、

NPlot.LinePlot linePlot1 = new NPlot.LinePlot();

plotSurface2D1.Add(linePlot1); // (2)

// 横軸
plotSurface2D1.XAxis1.WorldMin = 0; // (1)
plotSurface2D1.XAxis1.WorldMax = 60;

// 縦軸
plotSurface2D1.YAxis1.WorldMin = 30;
plotSurface2D1.YAxis1.WorldMax = 100;

plotSurface2D1.Refresh();

このように、(1)より先に(2)の処理をやってしまえばよいかと
思います。
軸の設定を細かく行いたい、または、軸の設定を使い回したいと
いった場合は、

NPlot.LinePlot linePlot1 = new NPlot.LinePlot();

NPlot.Axis xAxis = linePlot1.SuggestXAxis();
NPlot.Axis yAxis = linePlot1.SuggestYAxis();

// このあたりで xAxis や yAxis を色々設定する。

plotSurface2D1.XAxis1 = xAxis;
plotSurface2D1.YAxis1 = yAxis;

plotSurface2D1.Add(linePlot1);
plotSurface2D1.Refresh();

このようにしてもよさそうです。




もっちーの小部屋 自作のソフトを公開してます。
http://www.geocities.jp/mocchi_2003/index.html

System.Drawing.Bitmap画像を使った高速な画像処理プログラミング その2

 

.Net Frameworkベンチマークの公開にはややこしいルールがあるようなので、
ベンチマーク結果を公開する代わりにベンチマークを取れるプログラムを公開します。
こうにすれば画像処理プログラムの雛形としても使えますし…

 

<bench1.cs : SetPixel, GetPixel を使う方法>
using System;
using System.Drawing;
using System.Runtime.InteropServices;

class Bench{
  [DllImport("winmm.dll" )]
  public static extern int timeGetTime();

  public static void Main(string[ ] args){
    Bitmap bmp = new Bitmap("test.png" );
    int time1 = timeGetTime();
    const int TH = 128;
    for (int j = 0; j < bmp.Height; ++j){
      for (int i = 0; i < bmp.Width; ++i){
        Color c = bmp.GetPixel(i,j) ;
        byte r = c.R < TH ? (byte)0 : (byte)255, g = c.G < TH ? (byte)0 : (byte)255, b = c.B < TH ? (byte)0 : (byte)255;
        bmp.SetPixel(i,j, Color.FromArgb(r,g,b)) ;
      }
    }
    int time2 = timeGetTime();
    Console.WriteLine("{0}[msec]", time2 - time1);
    bmp.Save("test_o.png" );
  }
}

  

<bench2.cs : LockBits, UnlockBits, BitmapData を使う方法>
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

 

class Bench{
  [DllImport("winmm.dll" )]
  public static extern int timeGetTime();

  static void CopyBmp(Bitmap src, Bitmap dst){
    Graphics g = Graphics.FromImage(dst) ;
    g.DrawImageUnscaled(src,0,0) ;
    g.Dispose() ;
  }

  public static void Main(string[ ] args){
    Bitmap bmp_ = new Bitmap("test.png" );
    int time1 = timeGetTime();
    const int TH = 128;
    Bitmap bmp = new Bitmap(bmp_.Width, bmp_.Height, PixelFormat.Format24bppRgb);
    CopyBmp(bmp_, bmp);
    bmp_.Dispose();
    BitmapData bd = bmp.LockBits(new Rectangle(0,0,bmp.Width,bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);
    int stride = bmp.Width * 3;
    byte[ ] data = new byte[stride * bmp.Height];
    Marshal.Copy(bd.Scan0, data, 0, data.Length);
    for (int j = 0, ji = 0; j < bmp.Height; ++j, ji += stride){
      for (int i = 0; i < bmp.Width * 3; i += 3){
        int p = i + ji;
        data[p] = data[p] < TH ? (byte)0 : (byte)255; // R
        data[p+1] = data[p+1] < TH ? (byte)0 : (byte)255; // G
        data[p+2] = data[p+2] < TH ? (byte)0 : (byte)255; // B
      }
    }
    Marshal.Copy(data, 0, bd.Scan0, data.Length);
    bmp.UnlockBits(bd);
    int time2 = timeGetTime();
    Console.WriteLine("{0}[msec]", time2 - time1);
    bmp.Save("test_o.png" );
  }
}

 

 

<bench3.cs : MemoryStream にBMPファイルを書き出す方法>
using System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

 

class Bench{
  [DllImport("winmm.dll" )]
  public static extern int timeGetTime();

  static void CopyBmp(Bitmap src, Bitmap dst){
    Graphics g = Graphics.FromImage(dst) ;
    g.DrawImageUnscaled(src,0,0) ;
    g.Dispose() ;
  }

  static int readint32(MemoryStream ms){
    byte b = new byte[4];
    ms.Read(b, 0, 4);
    return (int)b[0] + (int)b[1] * 0x100 + (int)b[2] * 0x10000 + (int)b[3] * 0x1000000;
  }

  public static void Main(string args){
    Bitmap bmp_ = new Bitmap("test.png" );
    int time1 = timeGetTime();
    const int TH = 128;
    Bitmap bmp = new Bitmap(bmp_.Width, bmp_.Height, PixelFormat.Format24bppRgb);
    CopyBmp(bmp_, bmp);
    bmp_.Dispose();
    MemoryStream ms = new MemoryStream();
    bmp.Save(ms, ImageFormat.Bmp);
    ms.Seek(10, SeekOrigin.Begin);
    int startRaw = readint32(ms);
    ms.Seek(startRaw, SeekOrigin.Begin);
    int stride = ((bmp.Width * 3 + 3) / 4) * 4;
    byte[] data = new byte[stride * bmp.Height];
    ms.Read(data, 0, data.Length);
    for (int j = 0, ji = (bmp.Height - 1) * stride; j < bmp.Height; ++j, ji -= stride){
      for (int i = 0; i < bmp.Width * 3; i += 3){
        int p = i + ji;
        data[p] = data[p] < TH ? (byte)0 : (byte)255; // B
        data[p+1] = data[p+1] < TH ? (byte)0 : (byte)255; // G
        data[p+2] = data[p+2] < TH ? (byte)0 : (byte)255; // R
      }
    }
    ms.Seek (startRaw, SeekOrigin.Begin) ;
    ms.Write (data, 0, data.Length);
    bmp.Dispose();
    bmp_ = new Bitmap(ms);
    bmp = new Bitmap(bmp_);
    ms.Dispose();
    int time2 = timeGetTime();
    Console.WriteLine("{0}[msec]", time2 - time1);
    bmp.Save("test_o.png" );
  }
}

  

3例ともSystem.Runtime.InteropServices名前空間を参照していますが、3番目については、timeGetTimeのためだけに使っていますので、その部分を外せばSystem.Runtime.InteropServicesの参照を外すことができます。


 

System.Drawing.Bitmap画像を使った高速な画像処理プログラミングについて

はじめに

.Net Framework の System.Drawing.Bitmapを使うと、jpegpngなどの画像データを
簡単に扱うことができます。しかし、このクラスに用意されている、各画素にアクセスするメソッド SetPixel はあまり高速ではありません。
閾値処理、エッジ検出など各種画像処理をこのメソッドを使って行おうとすると、
画像データが大きい場合、結構処理に時間がかかります。

<処理が遅い例>

// 閾値128で2値化する処理
Bitmap bmp = new Bitmap("hoge.png" ) ;
const int TH = 128; // 閾値
for (int j = 0; j < bmp.Height; ++j){
  for (int i = 0; i < bmp.Width; ++i){
    Color c = bmp.GetPixel(i,j) ;
    byte r = c.R < TH ? 0 : 255, g = c.G < TH ? 0 : 255, b = c.B < TH ? 0 : 255;
    bmp.SetPixel(i,j, Color.FromArgb(r,g,b)) ;
  }
}
bmp.Save("hogera.png" );

 

これを解決する手段を2つほど見つけましたので書いておきます。
 

方法1: LockBits

 bmp.LockBitsで手に入るBitmapDataオブジェクトを介して、画像データ配列を直接
取得してしまう方法です。この方法は、CodeProjectというサイトに載っていました。
http://www.codeproject.com/cs/media/csharpfilters.asp
ただし、この方法では画像データ配列をアンマネージドなアドレスから取得する必要があるので、
System.Runtime.InteropServices.Marshalクラスのメソッドのお世話になるか、
Win32APIを呼び出す、またはunsafeモードにする必要があります。

 

方法2: BMP形式に変換して直接編集

画像ファイルをフォーマットごとMemoryStreamに書き込んで、それを自力で
書き換える方法です。このアイデアは iTextSharpのソースから得ました。
圧縮されているjpegpngフォーマットを直接自力で書き換えるのはかなりしんどい
ので、非圧縮形式であるBMP形式に変換します。

 

<方法2による改善例>

Bitmap bmp_ = new Bitmap("hoge.png" );
bmp = new Bitmap(bmp_.Width, bmp_.Height, PixelFormat.Format24bppRgb) ;
Graphics g = Graphics.FromImage(bmp) ;
g.DrawImageUnscaled(bmp_,0,0) ;
g.Dispose() ;
bmp_.Dispose() ;
MemoryStream ms = new MemoryStream() ;

 

// ここでmsに 24bppなMicrosoft Windows BMPフォーマットが
// 書き込まれる。
bmp.Save(ms, ImageFormat.Bmp) ;

 

// BMP ファイル先頭から10バイト-13バイトの4バイトに画像データ
// 本体の先頭アドレスが書かれている
ms.Seek(10, SeekOrigin.Begin) ;

 

int startRaw = readint32(ms) ;
ms.Seek(startRaw, SeekOrigin.Begin) ;

 

// BMPフォーマットは横方向のデータを4バイト境界に揃える仕様
int stride = ((bmp.Width * 3 + 3) / 4) * 4;
byte data = new byte[stride * bmp.Height]; // data に全画像データを格納する
ms.Read(data, 0, data.Length) ;

 

// ここでdataに対して処理を行う BGRBGRBGR... の順で各画素のデータが格納されている点に注意

 

// ここから先は data を Bitmapオブジェクトに書き出す処理。これまでと逆の処理をやるだけ
ms.Seek (startRaw, SeekOrigin.Begin) ;
ms.Write (data, 0, data.Length) ;
Bitmap bmpOut = new Bitmap(ms) ; // 処理後の画像がbmpOutに入る
ms.Dispose() ;

 

readint32は 4バイトのバイト列をリトルエンディアンの整数として読み込むメソッドです。

int readint32(MemoryStream ms){
  byte b = new byte[4];
  ms.Read(b, 0, 4);
  return (int)b[0] + (int)b[1] * 0x100 + (int)b[2] * 0x10000 + (int)b[3] * 0x1000000;
}

 
BMPフォーマットは非圧縮形式とはいえそこそこクセのあるフォーマットなので、
手順はそれなりに煩雑になってしまってますが、この方法ならば全部マネージドで
済む上に高速に処理できます。

 

(注意:当方の環境では、bmpOut.Saveメソッドを呼んでpngファイルを書き出そうとしたときに例外を発生しました。PixelFormat.Format24bppRgb決め打ちにしたのが原因でしょうか?さらに↓のような処理を加えたあと、bmpOut2.Saveとしたら大丈夫でした)
bmpOut2 = new Bitmap(bmpOut.Width, bmpOut.Height);
Graphics g2 = Graphics.FromImage(bmpOut2);
g2.DrawImageUnscaled(bmpOut, 0, 0);
// この時点でbmpOut2にはbmpOutと同じ画像データが入っているはず。
g2.Dispose();

 

ベンチマークなどは気が向いたら後ほど…ということで。

 

 


関連ページ

もっちー書庫 自作ソフトウェアやOSSライブラリの調べごと等を公開しています。
http://ja.osdn.net/users/mocchi_2012/pf/mocchi_stack_room/wiki/FrontPage

NPlot でマーカー付き折れ線グラフを使いたい!

まず、先日公開した、PDF出力機能付きNPlotで、僕が追加した箇所に一部ミスがありましたので、修正しました。
- http://www.geocities.jp/mocchi_2003/soft/NPlot_with_self_patch_051.zip

NPlotには、折れ線グラフ(LinePlot)も、マーカーだけのグラフ(PointPlot)もあるようです。しかし、
マーカー付き折れ線グラフはないみたいです。
折れ線とマーカーを両方表示して、2つのグラフに同じ値を書き込めば、グラフ部分はそれっぽくなるのですが、凡例を表示するとき、凡例を1つだけ表示したいのに2つできてしまうという問題があります。
(もしかしたらやり方があるのかもしれませんが…)

そこで、LinePlotとPointPlotの両方の機能を持っている、LinePointPlotというクラスを作ってみました。基本的な使い方はLinePlotと同じです。Markerプロパティを持ったLinePlot、といった感じになってます。↓のように使います。LinePointPlotも上に書いてある機能追加したNPlotの中にいれてあります。

NPlot.LinePointPlot linePlot1 = new NPlot.LinePointPlot();
linePlot1.AbscissaData = new int { 1, 5, 2, 3, 4, 1}; // 横軸
linePlot1.OrdinateData = new int
{ 3, 3, 1, 4, 1, 3}; // 縦軸
linePlot1.Marker = new NPlot.Marker(NPlot.Marker.MarkerType.Diamond, 10); // マーカーを指定できます。

this.plotSurface2D1.Add(linePlot1);
this.plotSurface2D1.Refresh();




もっちーの小部屋 自作のソフトを公開してます。
http://www.geocities.jp/mocchi_2003/index.html