午後わてんのブログ

ベランダ菜園とWindows用アプリ作成(WPFとC#)

Opacity(アルファ値)の足し算と引き算もできるようにした、OpaOpaOpasity

OpaOpaOpasity1.0.2

OpaOpaOpasity1.0.2


ダウンロード先

ここのOpaOpaOpasity1.0.2.7z
github.com

追加、変更点

テスト用画像

Opasity0.5(アルファ値128)の画像

テスト用画像で使われている色とその数をCountColorで見てみると

使われている色の詳細とその数
ほとんどがアルファ値128(不透明度0.5)の半透明画像


足し算

指定不透明度を0.2(アルファ値52)にして、これを足してみる

不透明度の足し算
期待する結果は
不透明度だと0.5+0.2=0.7
アルファ値だと128+52=180
になればいい

結果

0.2足した結果
アルファ値は128から180になった
不透明度に直してみると
128/255=0.50196078
から
180/255=0.70588235
0.2増えている、いいね!

引き算

同様に0.2(アルファ値52)を引いてみた結果

0.2引いた結果
アルファ値:128-52=76
不透明度:76/255=0.29803922
期待通り

マルチスレッド処理で速度向上

ファイルごとの処理をマルチスレッドにしたことで、複数ファイル処理の速度が上がったはず

結果は
CPU:Ryzen 5 2400Gで
ファイル数:426
ファイル合計サイズ:142MBの処理時間が
40秒から8秒になった

2400Gは4コア8スレッドのAPU
これで約5倍速くなって、これは期待以上だった

コード変更箇所

最初は画像処理の部分をマルチスレッド(並列化)にしようとしたけど、うまくできなかったので、ファイルごとの処理をマルチスレッドにした

/// <summary>
/// 実行
/// 対象フォルダにopaフォルダ作成、そこにAlpha値を変換した画像を
/// png形式で保存
/// <param name="files">対象画像ファイルリスト</param>
/// <param name="ope">アルファ値の計算方法</param>
/// </summary>
private void MyExe(IEnumerable<string> files, MyOpe ope)
{
    try
    {
        if (!string.IsNullOrEmpty(MyDirectory))
        {
            string dir = System.IO.Path.Combine(MyDirectory, MY_FOlDER_NAME);
            _ = Directory.CreateDirectory(dir);
            foreach (var file in files)
            {
                SaveExe(file,
                    dir + "\\" + System.IO.Path.GetFileName(file),
                    (byte)MySlider.Value,
                    ope);
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}


/// <summary>
/// 画像ファイルを指定Alphaに変換して保存
/// </summary>
/// <param name="bmpPath"></param>
/// <param name="savePath"></param>
/// <param name="alpha"></param>
/// <returns></returns>
private bool SaveExe(string bmpPath, string savePath, byte alpha, MyOpe ope)
{
    if (GetBitmap(bmpPath) is BitmapSource bmp)
    {
        switch (ope)
        {
            case MyOpe.Replace:
                bmp = AlphaReplace(bmp, alpha);
                break;
            case MyOpe.Add:
                bmp = AlphaAdd(bmp, alpha);
                break;
            case MyOpe.Subtract:
                bmp = AlphaSubtract(bmp, alpha);
                break;
            default:
                break;
        }
        SaveBitmapToPng(savePath, bmp);
        return true;
    }
    return false;
}

↑前回、シングルスレッド処理

↓今回、マルチスレッド処理

/// <summary>
/// 対象フォルダにopaopaopasityフォルダ作成、そこにAlpha値を変換した画像をpng形式で保存
/// </summary>
/// <param name="files">対象画像ファイルリスト</param>
/// <param name="ope">アルファ値の計算方法</param>
private void MyExe(List<string> files, MyOpe ope)
{
    try
    {
        if (!string.IsNullOrEmpty(MyDirectory))
        {
            string dir = System.IO.Path.Combine(MyDirectory, MY_FOlDER_NAME);
            _ = Directory.CreateDirectory(dir);
            byte alpha = (byte)MySlider.Value;
            Parallel.For(0, files.Count, async ii =>
            {
                await SaveExe(files[ii], dir + "\\" + System.IO.Path.GetFileName(files[ii]),
                      alpha,
                      ope);
            });
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}


/// <summary>
/// 画像ファイルを指定Alphaと計算して保存
/// </summary>
/// <param name="bmpPath">画像ファイルパス</param>
/// <param name="savePath">保存パス</param>
/// <param name="alpha">アルファ値指定</param>
/// <param name="ope">アルファ値の計算方法指定</param>
/// <returns></returns>
private async Task<bool> SaveExe(string bmpPath, string savePath, byte alpha, MyOpe ope)
{
    if (GetBitmap(bmpPath) is BitmapSource bmp)
    {
        switch (ope)
        {
            case MyOpe.Replace:
                bmp = AlphaReplace(bmp, alpha);
                break;
            case MyOpe.Add:
                bmp = AlphaAdd(bmp, alpha);
                break;
            case MyOpe.Subtract:
                bmp = AlphaSubtract(bmp, alpha);
                break;
            default:
                break;
        }
        await Task.Run(() =>
        {
            SaveBitmapToPng(savePath, bmp);
        });
        return true;
    }
    return false;
}

MyExeのforeachでファイルリスト(files)のファイルを一個づつ処理していたところをParallel.Forでマルチスレッドに変更

foreach (var file in files){SaveExe()}

から

Parallel.For(0, files.Count, async ii =>{await SaveExe()}

これだけだとエラー表示だったので

SaveExeも変更

private bool SaveExe()
SaveBitmapToPng();

をそれぞれ

private async Task<bool> SaveExe()
await Task.Run(() =>{SaveBitmapToPng();});

に変更したら動いてくれた

このへんはよくわかっていない
asyncとawaitは対になっている感じで両方必要、これでエラー表示ならそれっぽいところにTask.Ran()、これで動いた

変換中のCPU使用率

CPU使用率比較
今回100%使い切っているのは期待通り、いいねえ
でも、今気づいたのがメモリの使用量が多い!3GBも多く使っている!
いくらなんでも多すぎる、なんで?

感想

前回使っていて不満だったところの一部が直せてよかった、と同時にメモリ使用量っていう不満が出てきたけど、これは直せそうにないなあ

関連記事

次のOpaOpaOpasityは1週間後
gogowaten.hatenablog.com

前回の記事は昨日
gogowaten.hatenablog.com

画像に使われている色の詳細とその個数を表示するアプリCountColorの記事
画像の使用色数とその色のピクセル数を表示するアプリその6 - 午後わてんのブログ
gogowaten.hatenablog.com

画像の使用色数を表示するアプリ作ってみた - 午後わてんのブログ
gogowaten.hatenablog.com