TChartを使用してグラデーション表示をしようと考えています。
例えば10×10の枠がそれぞれあるとします。
(1,1)が一番左下のとして(1,2)は左下から一つ上にのものと考えます。
10が赤で0が青とします。(5が黄色ぐらいになるはず?)
例えば(1,1)が9として(10,10)が2という値をもっているとします。
この2つの値を使用してグラデーションしていきたいので、(1,1)から(10,10)までの7の差をうまく分割して全ての枠に(小数もOK)データを埋め込んでいきたいと考えています。
Delphiの質問というより、どうやったら均等に値をうめこめるかという話になってしまうので筋違いかもしれませんが、どのように計算していくのが良いのかアドバイスもらえませんでしょうか。
よろしくお願いします。
すいません、実データ2点の場合は簡単にできてしまうので例が悪かったです。
例)
実データを4つとして(1,1)を9、(3,4)を8、(7,4)を6、(10,10)を10とした場合などの計算とします。
赤と青の間をグラデーションしていったら中間は紫になるんじゃないかとか、
左下から真上や真横に行く時どうなればいいのかとか、いくつか不明点はありますけど…
一般的な線形補間の方法として
・入力がX1の時の出力をY1
・入力がX2の時の出力をY2
とすると、任意の入力Xに対する出力Yは次のように求められます。
Y = Y1 + (Y2 - Y1) * (X - X1) / (X2 - X1)
始めと終わりの色をそれぞれRGBなりHSVなりに分解し、
それぞれの点で各成分を上の式で補完してやれば求める色が得られるかと。
失礼、よく質問を読み返してみたら、どうやら問題は2つに分割されるようですね。
(1) 0〜10 間の値が求まったとして、それぞれに色を割り当てる
これは上で説明した線形補間の原理でできます。
例えば0の時の色が青(0,0,255)、10の時の色が赤(255,0,0)としたら、
値Xに対する色は RGB(X/10*255, 0, (10-X)/10*255) となります。
(2) 二次元の格子状に配置されたデータの値を補間する
たとえば次のようなm*nの格子で、左下と右上のデータ値が与えられたときに
残りの点の値を求めたいということだと解釈します。
(途中の点がいくつか与えられた場合は、それぞれの点を対角線とする
長方形に分割して個別に補間します)
?? ?? ?? 10
?? ** ** ??
04 ?? ?? ??
実は、これだけの条件では答えは出せません。
左下と右上を結ぶ線上(図の「**」の部分)は補間可能ですが、
残りの「??」の部分はどのような解釈も可能だからです。
一意に解くには追加条件(例えば左上と右下の値も与えられていること)が必要です。
というわけで、左下から上や横に進んだ場合、どのようになることをお望みでしょうか?
torさん
ありがとうございます。
それぞれの値の位置、データはランダムを想定しています。
10 ** 05 ** 08
05 ** ** ** 06
** ** ** 05 **
** ** 02 ** **
** ** ** ** 01
例えばこのような状態の場合**の部分をカラーがあまりぎこちなくならないような値を埋めていきたいです。
例えば(1,5)の**は10と5の間にあるので7.5という値がでます。
では(1,4)はどうしましょう?というのが問題点です。
現在私のやり方は左上から探したとして、値がないところ**を見つけたら、そこと隣り合う(最大4点、値がないところは点としない)を使用して平均をだし、そこの値に補間しています。
例えば(5,3)であれば5、6を2で割って5.5という値をだします。
そうやってすべてうまるまで繰り返していってますが、あまりきれいにかけません。
ほかにいい方法ありますでしょうか?
綺麗に出ないのは補間方法が単純平均だからですよ。
Excelで多項式近似を行うと次のようになりました。
10 6.5 5 5 8
5 5.6 6 6.1 6
2.3 4 5 5 4.2
1.8 1.9 2.0 2.1 2.3
3.5 -0.9 -2.9 -2.3 1.0
ただし、データが少ないのでどうしても綺麗にはでませんよ。
そういう条件だと、単純な補間では基準の取り方によってそれぞれ違う答えが出せますし
(1, 1)のような外挿する部分をどうするか、というのも明確な答えは出せなさそうです。
ここは一つ、エネルギーの最小化の問題として考えてみましょう。
まず、二つのセルの間の誤差評価関数を定義します。これは単純に差の二乗でいいでしょう。
error = Power(cell[x1, y1] - cell[x2, y2], 2);
最初に、「**」の場所をすべて最大値(たとえば10)で埋めておきます。
そして、「**」の場所すべてに対して、以下の計算を繰り返し行います。
1. 隣接するセルとの間で誤差をとり、その平均を求める
2. セルの値を一定量(たとえば0.05)減らし、同様に隣接セルとの間で誤差をとる
3. 2の誤差が1より小さかったら、2の値を採用(セルの値を減らす)。そうでなければ元のまま
(実際には更新前の各セルの値をとっておき、全マスの計算が終わったら新しい値で更新します)
この計算を、それ以上変化が起こらなくなるまで繰り返すと
隣接するセル間で誤差が最小になる状態が求まると思いますが、どうでしょうか。
torさん
確かになんかよさそうです。
やってみます。
ちなみに
error = Power(cell[x1, y1] - cell[x2, y2], 2);とするのはなぜでしょう?
2乗したものを平均するのでしょうか?
またこのやり方は※※の最大値が隣接していた場合は最初の段階では
最大値-最大値で0になるので、平均には反映されません。
2回目の段階で9.95という値になるので
隣接しているものが※※の最大値であれば差が-0.05となります。
このもともとが※※の部分は計算に入れませんよね?
> 2乗したものを平均するのでしょうか?
そうです。差が広がるほど急激に増える関数が欲しかったので2乗にしています。
単なる絶対値より、その方が良好な結果が出るはずです。
評価関数の選び方で出方も変わってくるので、そこは色々試してみてください。
> このもともとが※※の部分は計算に入れませんよね?
ここのところの意味がよくわかりませんが……やるべき手順は
1. 現在のセルの値 cell[x,y] を取り出す。これをVとする
2. V の値を使って、隣接するセルとの間で評価値を求める。これをE1とする。
3. V-0.05 の値を使って、隣接するセルとの間で評価値を求める。これをE2とする。
4. e2<e1 なら、new[x,y] := V-0.05。そうでなければ new[x,y] := V。
もと「**」だったセルすべてについてnew[x,y]を計算したら、
その結果をすべてのcell[x,y]にコピー。ここまでが1セットです。
当然、もともと数字が入っていたセル(変えてはいけないところ)と
そうでないところは区別できるように覚えておく必要があります。
単色でよいのならば
グラフィック処理でいうとこのぼかし処理をすればいいんじゃないか?
要は平均化
その点(1箇所)とその周りの値(8箇所)を足して 9 で割った値にする
torさん
私の理解不足だったらすいません。
これを
10 ** 05 ** 08
05 ** ** ** 06
** ** ** 05 **
** ** 02 ** **
** ** ** ** 01
まず
10 10 05 10 08
05 10 10 10 06
10 10 10 05 10
10 10 02 10 10
10 10 10 10 01
こうしたら、
右上から探していくとして、右上から左に2番目の【3,4】(右下【0,0】)
の10がもともと「**」だったのでこいつを変えるために計算を行います。
計算
(10 - 08【4,4】)**2 = 4
(10 - 10【3,3】)**2 = 0
(10 - 05【2,4】)**2 = 25
E1 = 29 / 3 = 9.66・・・・;
(9.95 - 08【4,4】)**2 = 3.8025
(9.95 - 10【3,3】)**2 = 0.0025
(9.95 - 05【2,4】)**2 = 24.9
E2 = 28.705 / 3 = 9.56;
E2 < E1なので
10【3,4】-0.05 = 9.95;
これだと「**」の値が10か9.95にしかなりません。
認識が間違っていると思うのですが。
間違いはどこにありますか?
理解不足すいません。
> これだと「**」の値が10か9.95にしかなりません。
1回目の計算ではそうです。
全部のマスについて計算を行い、出た結果で全部のマスの値を更新する
…という過程をさらに何度も繰り返すのです。
(繰り返すうちにいくつかのセルの値は 9.90, 9.85 ... とどんどん減っていき、
周辺のセルもそれに影響を受けて変化していきます)
いずれどのセルの値も変化しなくなります。そうなったら計算終了です。
ちなみに1回目の計算が終わるとこうなります。
10.00 9.95 5.00 9.95 8.00
5.00 9.95 9.95 9.95 6.00
9.95 10.00 9.95 5.00 9.95
10.00 9.95 2.00 9.95 9.95
10.00 10.00 9.95 9.95 1.00
2回目が終わるとこう。
10.00 9.90 5.00 9.90 8.00
5.00 9.90 9.90 9.90 6.00
9.90 9.95 9.90 5.00 9.90
9.95 9.90 2.00 9.90 9.90
10.00 9.95 9.90 9.90 1.00
この例だと162回で収束し、最終結果は次のようになります。
10.00 6.75 5.00 6.20 8.00
5.00 5.20 4.95 5.55 6.00
4.25 4.15 4.00 5.00 4.65
3.55 3.15 2.00 3.00 2.90
3.20 2.90 2.35 2.10 1.00
ツイート | ![]() |