ナビゲーション リンクのスキップホーム > そのまま使えるシリーズ > そのまま使えるメッシュ画像

そのまま使えるメッシュ画像

このページは、2017年3月7日現在の情報に基づいて掲載しております。
情報の信頼性は、法令の改正や温暖化の進行具合、摂取したコラーゲンの量、ムカデがどの足から歩き出したか等の様々な事象の影響を受け、日々刻々と常に変化しております。

画像ファイル:
ブロックサイズ: Pixels
横方向: %
縦方向: %
背景色:

まず最初に、画像を選択します。
次にパラメータを設定して、画像を生成します。
画像を右クリックのうえ「名前を付けて画像を保存」を選択して、画像を保存してください(Internet Exprolerの場合)。
生成された画像の著作権は、元の画像の著作権者に帰属します。他のツールと同様に、送信されたいかなる情報も、一時的にサーバのメモリ上に展開される他は、弊社としては保持いたしません。
生成された画像は、みなさまご自身の判断と責任において、ご自由にご活用ください。

元の画像:
生成された画像:

きっかけは興味本位

まず、下記のニュースリリースを拝見しました。
凸版印刷|凸版印刷、店内が透ける大型電子看板

そして、手元にある写真を格子でくり抜いたらどんな具合になるのだろうと興味が沸いてきてしまったので、ついつい出来心で作ってしまったWebツールです。

フォトレタッチソフトかなんかでやろうと思えばできることではありますが、なかなか地味な作業の繰り返しになりそうな気がするので、どうせなら一発で生成できるようなものを作りたくなったわけです。

やっていることは単純

.NET Framework標準の、System.Drawing.Bitmapクラスを使用しています。
というより、このクラスしか使用していません。

そのまま使えるランダム画像では(説明はしていないものの)Bitmapクラスに加えて、GraphicsクラスやSolidBrushクラスも使用して描画していますが、このWebツールはそもそも描画していない(ここ重要です)ので、Bitmapクラスだけ使用すれば実現できます。
より正確にいえば、指定した背景色を塗るためだけにGraphicsクラスを使用していますが、背景色を塗らない(透明にする)のであれば、Graphicsクラスを使用する必要はありません。

それでは早速、コードを紹介していきましょう。

ファイルを読み込む

まず、<asp:FileUpload>コントロールを設置して、ファイルのアップロードを受け取れるようにします。
このコントロール、クライアントの情報を一部とはいえ引っこ抜いてしまうUIであるからなのかわかりませんが、他のコントロールと比べて制御できる幅が少ないですね。
また、僕の知識不足かも知れませんが、送信ボタンを押すたびに選択したファイルがクリアされてしまうので、HasFileプロパティだけを見て処理を分ける実装にしてしまうとパラメータを変更する度にファイルを選び直す必要があるようです。

ユーザが送信したいかなる情報もサーバに保持しないことを是とする本サイトですから、都度ファイルを選び直す実装にしてリリースしようかと思ったのですが、うちの人から激しい突っ込みが入りましたので、Session変数(つまりサーバのメモリ上)に画像を保持する方法を採用しました。
当然ですが、セッションが切れればサーバのメモリ上から自動的に消滅します。

具体的には、HasFileプロパティがtrueのときはアップロードしたファイルを読み込んでSession変数に格納して使用し、HasFileプロパティがfalseかつSession変数がNULLでないときはSession変数に格納された画像を使用する実装としております。
コードで書くと、次のような具合です。

        if (this.SelectImageFile.HasFile || Session["画像データ"] != null)
        {
            try
            {
                if (this.SelectImageFile.HasFile)
                    using (var __stream = this.SelectImageFile.FileContent)
                        Session["画像データ"] = new System.Drawing.Bitmap(__stream);

                // 画像処理
            }
            catch
            {
                Session.Remove("画像データ");

                // 読み込めない形式です画像表示
            }
        }
        else
        {
            Session.Remove("画像データ");

            // 画像を選択してください画像表示
        }

オリジナル画像を表示する

オリジナル画像を表示したければ上記コードのSession["画像データ"]を単にSaveメソッドでResponse.OutputStreamにSystem.Drawing.Imaging.ImageFormat.Pngとかで流せばよい、と安直に考えてしまうところですが、元の画像形式と異なる形式で流そうとすると、真っ黒な画像が出たり例外が生じて表示されなかったりします。
それでも安直な発想から抜け切れない僕は、次のようなコードで対処してしまうのでした。

    protected void Page_Load(object sender, EventArgs e)
    {
        if (Session["画像データ"] != null)
        {
            var __image = (System.Drawing.Bitmap)Session["画像データ"];
            using(var __outImage = new System.Drawing.Bitmap(__image.Size.Width, __image.Size.Height))
            {
                for (var __x = 0; __x < __image.Size.Width; __x++)
                    for (var __y = 0; __y < __image.Size.Height; __y++)
                        __outImage.SetPixel(__x, __y, __image.GetPixel(__x, __y));

                __outImage.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Png);
            }
        }
        else
        {
            // 元のaspxでSession["original-image-bitmap"]がNULLでないことが保証されているので、特にエラー処理はいらないと思う。
        }
    }
安直って素晴らしい。。。

メッシュ画像を表示する

メッシュ画像を作るには、前項で書いた安直なコードを元に、パラメータで指定したとおりに点を間引いて表示すればよい、ということになります。

アップロードする画像ファイルの大きさは、実際に受け取ってみないとわかりません。
どんな大きさのファイルを受け取っても、パラメータで指定したとおりにバランスよく間引いてあげたいところです。
そこで、まず最初に、余白を決めます。幅(高さ)をブロックサイズで割った剰余とブロックサイズから横(縦)方向の値を差し引いた値とを足した数を、2で割った値を左(上)余白としています。
また、幅(高さ)から左(上)余白の値を差し引いた値でforループを止めることで、右(下)余白を設けています。

余白が決まったら、点を間引きながらコピーするだけです。
コードで書くとこんな感じです。

    protected void Page_Load(object sender, EventArgs e)
    {
        if (Session["画像データ"] != null)
        {
            var __orgImage = (System.Drawing.Bitmap)Session["画像データ"];

            var __blockPixels = (int)Session["ブロックサイズ"];
            var __horizontalMaxPixels = __blockPixels;
            var __verticalMaxPixels = __blockPixels;
            var __horizontalPixels = (int)Session["横方向"];
            var __verticalPixels = (int)Session["縦方向"];
            var __colorBackGround = (DrawingCustomClasses.ARGB)Session["color-back-ground"];

            using (var __meshImage = new System.Drawing.Bitmap(__orgImage.Size.Width, __orgImage.Size.Height))
            {
                // 背景色を塗るためだけにGraphicsクラスを使用する。
                using (var __graph = System.Drawing.Graphics.FromImage(__meshImage))
                    __graph.Clear(System.Drawing.Color.FromArgb(__colorBackGround.R, __colorBackGround.G, __colorBackGround.B));

                if (__horizontalMaxPixels > __orgImage.Size.Width)
                    __horizontalMaxPixels = __orgImage.Size.Width;

                if (__verticalMaxPixels > __orgImage.Size.Height)
                    __verticalMaxPixels = __orgImage.Size.Height;

                // 余白を決める
                var __horizontalOffset = Convert.ToInt32(Math.Floor((double)((__orgImage.Size.Width % __horizontalMaxPixels + (__blockPixels - __horizontalPixels)) / 2)));
                var __verticalOffset = Convert.ToInt32(Math.Floor((double)((__orgImage.Size.Height % __verticalMaxPixels + (__blockPixels - __verticalPixels)) / 2)));

                // 点を間引いてコピー
                for (var __x = __horizontalOffset; __x < __orgImage.Size.Width - __horizontalOffset; __x++)
                    if ((__x - __horizontalOffset) % __horizontalMaxPixels < __horizontalPixels)
                        for (var __y = __verticalOffset; __y < __orgImage.Size.Height - __verticalOffset; __y++)
                            if ((__y - __verticalOffset) % __verticalMaxPixels < __verticalPixels)
                                __meshImage.SetPixel(__x, __y, __orgImage.GetPixel(__x, __y));

                __meshImage.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Png);
            }
        }
        else
        {
            // 元のaspxでSession["original-image-bitmap"]がNULLでないことが保証されているので、特にエラー処理はいらないと思う。
        }
    }
う~ん、安直ですねぇ。
これをロジックと呼んでよいかどうかは、みなさまにお任せします。。。

ほぼ余談

本当は、画像を一旦読み込んだら、元の画像を表示しておく実装にしようと思ったのですが、iPhoneでうまく動かなかったので、現在の実装に落ち着きました。
どうせSession変数を使うのならもっとページ遷移があってもいいのですけれど、1ツール1ページという今現在のこだわりのもとに、ページ遷移は見送りました。

iPhoneといえば、そもそもAjaxControlToolkitのSliderExtenderやColorPickerExtenderが使いにくかったりしますので、ASP.NETに標準で付いてくる機能だけで作ることを選択した時点で、本サイト自体がiPhoneに優しくないことが決まってしまったようなものではあるのですが。。

本サイトは、下記のサイトポリシーに同意いただいたうえでご利用ください。

サイトポリシー | お問い合わせ | 運営会社(株式会社GAIDE)

Copyright © 2016 GAIDE CO., LTD. All rights reserved.