2018年12月12日水曜日

VB WPF グラデーションとバンディング(縞模様)

目は正確に判断するもの ... と思いきや、
錯覚によって、とんでもない効果を齎す事があります。
WPF に限りませんが、 錯覚を起こし易い もの への対応!。
今回の グラデーションとバンディング もそうです。  正確では、宜しくありません!。


バンディングは Banding で縞模様が色の差異の境界で不自然に見える状態の事です。
グラデーションは広範囲の色を対象にすれば目立ち難いですが、
狭い色範囲で行うと目立って来ます。
ですから、WPF で、一般的なグラデーション効果を説明する例は、カラフルなものが多い筈です。
単色や狭い色範囲の例では、バンディングによって見た目が悪くなるからですね。

写真加工の世界の話ですが、
.jpg を .gif (アニメ) に加工する場合にも、この現象に悩まされる事になります。
この場合、絶対的な色数の不足が主原因です。

一般的な画像編集や写真加工の世界で、説明される対処法は、
ノイズを加える 色深度を深く取り処理する 補間する ディザリングする 等ですが、 (ディザリングする = ノイズを加える).
興味深い ご質問 を msdn で見付けました。
丁度、上で書いた 避けたくなる例 だからです。

  msdn の QA 上では バンディング(縞模様) を モアレ と表現されています。
  モアレ状のもの が私の認識です。 *1.

画像処理であれば、上記の対策を取りますが、
WPF で、どうやるのかな? ... と思っていた処、  MVP の kenjinote さま が回答をお寄せになりました。
やはり、 ノイズ付加! でした。

参考リンク:  https://social.msdn.microsoft.com/Forums/ja-JP/7918c892-a2be-4ce6-9adb-5a055b4707e3/19981368792612624230125101247312463124342035112387123901246412?forum=wpfja


ご回答の xaml と C# Code を引用させて戴きます。  序に、VB.net のものも。

xaml

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="MainWindow" Height="500" Width="500">
    <Grid Background="#212121">
        <Rectangle x:Name="rectangle1" Fill="#408CC1">
            <Rectangle.OpacityMask>
                <LinearGradientBrush StartPoint="0,0" EndPoint="1,1" >
                    <LinearGradientBrush.GradientStops>
                        <GradientStop Offset="0.0" Color="#00000000" />
                        <GradientStop Offset="1.0" Color="#80000000"/>
                    </LinearGradientBrush.GradientStops>
                </LinearGradientBrush>
            </Rectangle.OpacityMask>
        </Rectangle>
    </Grid>
</Window>

C#

using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Drawing;
namespace WpfApp1
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Bitmap bitmap = new Bitmap((int)Width, (int)Height);
            Random rnd = new Random();
            for (int x = 0; x < (int)Width; x++)
            {
                for (int y = 0; y < (int)Height; y++)
                {
                    int nAlpha = rnd.Next(230, 255); // ここの範囲を広げすぎるとざらつきが目立つようになる
                    bitmap.SetPixel(x, y, System.Drawing.Color.FromArgb(nAlpha, 0x40, 0x8C, 0xC1));
                }
            }
            IntPtr hbitmap = bitmap.GetHbitmap();
            rectangle1.Fill = new ImageBrush(System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(hbitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()));
            DeleteObject(hbitmap);
        }
        [System.Runtime.InteropServices.DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr hObject);
    }
}

VB.net  (Converted by http://www.carlosag.net/tools/codetranslator/ )

Imports System
Imports System.Windows
Imports System.Windows.Media
Imports System.Windows.Media.Imaging
Imports System.Drawing                      'Add Reference System.Drawing On My Project

Namespace WpfApp1
  
    Public Class MainWindow
        Inherits Window
      
        Public Sub New()
            MyBase.New
            InitializeComponent
            Dim bitmap As Bitmap = New Bitmap(CType(Width,Integer), CType(Height,Integer))
            Dim rnd As Random = New Random
            Dim x As Integer = 0
            Do While (x < CType(Width,Integer))
                Dim y As Integer = 0
                Do While (y < CType(Height,Integer))
                    Dim nAlpha As Integer = rnd.Next(230, 255)
                    ' ここの範囲を広げすぎるとざらつきが目立つようになる
                    bitmap.SetPixel(x, y, System.Drawing.Color.FromArgb(nAlpha, 64, 140, 193))
                    y = (y + 1)
                Loop
              
                x = (x + 1)
            Loop
          
            Dim hbitmap As IntPtr = bitmap.GetHbitmap
            rectangle1.Fill = New ImageBrush(System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(hbitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions))
            MainWindow.DeleteObject(hbitmap)
        End Sub
      
        Public Declare Function DeleteObject Lib "gdi32.dll" (ByVal hObject As IntPtr) As Boolean
    End Class
End Namespace


比較の為、この QA に投稿された画像も引用掲載して置きます。
  https://social.msdn.microsoft.com/Forums/getfile/1369268
  https://social.msdn.microsoft.com/Forums/getfile/1369344 .

左が 質問者 さま のもの、 右が kenjinote さまのもの、 です。  著作権は各位。

Banding


With Noise (This Codes)



各々、OS が異なる為か、印象が異なりますが、 グラデーションの変化にご注目下さい。
尚、同一環境下 且つ 原寸 で比較しないと、正確な差異は 判断出来ないかも知れません。
特に、画面コピーが拡大縮小されると、 再び、バンディングが発生するからです。
ですから、この図は参考程度で!。


  参考 *1.

  この図を見て分かる事は、拡大縮小でまた出現する可能性と、 描画エンジンの影響です。
  描画エンジンの影響 とは 画像を表示するレンダリング能力に左右される、と言う意味です。
  更に、見る側の 認識度 も差異があります。  錯覚の類 ですから。
  バンディングはなかなか解消が難しい 現象 なのです。 *1.



[2018/12/18] 私見を追記 *1.

0 件のコメント:

コメントを投稿