bitmapオブジェクトのピクセル情報を配列にコピーし配列内の要素を加工することで画像を編集する方法がC#でもできるようなので、そのサンプルになります。24bitのjpeg画像などを読み込ませ32bitで用意した画像配列にRGB順にコピーしています。A(アルファチャンネル)には255(不透明)をセットしてあります。配列をゴリゴリ加工するのは高級言語のC#には似つかわしくない処理ですが、以前試したGetPixelやSetPixelでピクセル単位で加工するよりだいぶ実用的な速度で動作します。既存のAPIがありそうなネタですし、そもそも、最終成果物である24bitのjpeg画像を素材として再加工するはクオリティ的に褒められたことではないとは思いますが、不幸にも多量の画像ファイルを変換する機会に恵まれまして作成に至りました。
ソース
ソースファイル:adda.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
namespace adda
{
class Program
{
// アルファチャンネルの追加
static int adda(string src_file, string dst_file)
{
using (var bmp = new Bitmap(src_file))
{
if (bmp.PixelFormat != PixelFormat.Format24bppRgb)
{
return 1; // 24bitRGB以外は未対応
}
using (var bmp2 = new Bitmap(bmp.Width, bmp.Height, PixelFormat.Format32bppArgb))
{
var bmpd = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.ReadWrite,
bmp.PixelFormat
);
var bmpd2 = bmp2.LockBits(
new Rectangle(0, 0, bmp2.Width, bmp2.Height),
ImageLockMode.ReadWrite,
bmp2.PixelFormat
);
byte[] pixels = new byte[bmpd.Stride * bmp.Height];
byte[] pixels2 = new byte[bmpd2.Stride * bmp2.Height];
System.Runtime.InteropServices.Marshal.Copy(bmpd.Scan0, pixels, 0, pixels.Length);
// 先頭からループ
for (int y = 0; y < bmp.Height; y++)
{
for (int x = 0; x < bmpd.Width; x++)
{
int pos = y * bmpd.Stride + x * 3;
int pos2 = y * bmpd2.Stride + x * 4;
pixels2[pos2] = pixels[pos];
pixels2[pos2+1] = pixels[pos+1];
pixels2[pos2+2] = pixels[pos+2];
pixels2[pos2+3] = 255;
}
}
System.Runtime.InteropServices.Marshal.Copy(pixels2,0,bmpd2.Scan0,pixels2.Length);
bmp.UnlockBits(bmpd);
bmp2.UnlockBits(bmpd2);
bmp2.Save(dst_file, ImageFormat.Png);
}
}
return 0;
}
// アプリケーションのエントリポイント
static int Main(string[] args)
{
Console.WriteLine("adda");
var src_file = "";
var dst_file = "";
if (args.Length == 0)
{
src_file = @"入力ファイルのパス";
//return 1; // 異常終了
} else {
src_file = args[0];
}
if (args.Length < 2)
{
dst_file = Path.GetFullPath(@"出力ファイルのパス");
//return 1; // 異常終了
} else {
dst_file = args[1];
}
Console.WriteLine("src:{0} dst:{1}", src_file, dst_file);
return adda(src_file, dst_file); // 正常終了
}
}
}
プロジェクトファイル:adda.csproj
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<AssemblyName>adda</AssemblyName>
<OutputPath>Bin\</OutputPath>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
</PropertyGroup>
<ItemGroup>
<Compile Include="*.cs" />
</ItemGroup>
<Target Name="Build" Inputs="@(Compile)" Outputs="$(OutputPath)$(AssemblyName).exe">
<MakeDir Directories="$(OutputPath)" Condition="!Exists('$(OutputPath)')" />
<Csc Sources="@(Compile)" TargetType="exe" OutputAssembly="$(OutputPath)$(AssemblyName).exe" />
</Target>
<Target Name="Clean">
<Delete Files="$(OutputPath)$(AssemblyName).exe" />
</Target>
<Target Name="Rebuild" DependsOnTargets="Clean;Build" />
<Target Name="Run" DependsOnTargets="Rebuild">
<Exec Command="$(OutputPath)$(AssemblyName).exe" />
</Target>
</Project>
アルファチャンネルについて
コンピュータで取り扱う画像は点(ドット)の集合で、ドットは色の情報をもつわけですが、R(赤)G(緑)B(青)の三原色を組み合わせることで色を表現する方法があります。各色を256諧調で表現する場合が多くその場合、各色8bit(1byte)の3色で24bit相当の情報量になります。
この辺当たりは比較的なじみがあったのですが、RGBに加えてアルファチャンネルと呼ばれる透明度を表すチャンネルが存在します。こちらも知識として知ってはいましたが、あまりなじみがなく何のために存在するのか理解できませんでした。
画像を作成や加工などで像を重ね合わせる場合、透明部分の濃淡?により下側にある画像をどの程度透過させるかを設定することができます。幸いにも個人的に画像の作成加工などはすることがなく、完成した画像を見るだけの人である私は、透明度を表すアルファチャンネルとかかわる機会がありませんでした。てっきり24ビットだとCPUやメモリの配置的に収まりが悪いので、収まりをよくするためにあえて8bitの詰物をしているだと思っていて、ファイル容量的に32bitより24bitのほうが小さくて済むのに何故このようなファイル形式があるのか不思議に思っていたぐらいです。GIMPなどの画像加工ソフトでレイヤーをつかってみて初めて役割を知った次第でございます。
コメント