1

インデックス付きのカラー パレットを持つ Surfaceがあり、それに別のインデックス付きの色の Surface を追加BaseSurfしたいblit(それを と呼びましょうNewSurf)。ただし、BaseSurf のパレットは NewSurf のパレットとは異なり、pygame は不足している色を BaseSurf のパレットに自動的に追加しません。したがって、NewSurf の色を BaseSurf のパレットにマングリングせずに追加する方法が必要です (Surface のピクセルで実際に使用されるパレット インデックスを上書きします)。

このコードを実行すると:

screen = pygame.display.set_mode((222,222))
screen.fill((255,255,255))

BaseSurf = pygame.load.image("4samps.png").convert(8)  # indexed with four colours; larger dimensions
NewSurf  = pygame.load.image("another4samps.png").convert(8)  # indexed with four more colours

screen.blit(BaseSurf, (10,10))
screen.blit(NewSurf, (160,43))

BaseSurf.blit(NewSurf, (33,33))

screen.blit(BaseSurf, (50,122))

...これが私が見るものです...

私が考えていたものではありません。

--編集:組み合わせたグラフィックは、シーケンスがオフであっても、NewSurf の色を BaseSurf が使用しているものに近づけることに注意してください。右下のブロブは、実際には null 値 (0,0,0,255) を使用して、最も近い色に一致します!

明らかに、NewSurf (右側) のインデックスは BaseSurf に転送されています。これは正確に間違っているわけではありませんが、私が望んでいるものでもありません。NewSurf を BaseSurf にブリットして、修正した画像の正確なカラー データを保持するにはどうすればよいですか (ただし、インデックス付きのカラー画像は保持します)。

これは、同じ意味での別の (未回答の) 質問です。

4

1 に答える 1

2

私は実際にそれを尋ねる過程で答えを見つけました。

を使用してpygame.Surface.set_palette_at()、NewSurf からパレット データを抽出し (NewSurf.get_palette(..)とイテレータを使用してそのパレットの空白をクリアする)、それを BaseSurf のパレットの「最後」 (つまり、空白でない最後の RGBA インデックス値の後) に貼り付けることができました。BaseSurf.set_palette_at(first_null_RGBA_index, new_RGBA_value).

first_null_RGBA_valueさまざまな方法で見つけることができます。で使用中のパレットに続くnull(未使用)値の「トレイン」でnext()、最初のnull値(によって定義され、私の経験では通常(0,0,0,255)です)を見つけていました。を使用して、最初の空白値のインデックスを取得することもできます(実際にグラフィックに空白値のピクセルがある場合、これは非常に危険です!)。メソッドとその問題点 (私が見ている限り) については、すぐに説明します。blankRGBAget_palette()get_palette().index(blankRGBA)

たまたま、 NewSurf のインデックスを BaseSurf のパレットの新しい位置に合わせて再インデックスする必要はないようです。重複した RGB 値を再マップした場合、これがどのような結果をもたらすかわかりません! pygame は blit された画像の RGB 値を近似して、受け取った画像の最も近いものに一致させると思います。事前に NewSurf から BaseSurf にパレット全体を意図的に貼り付けた場合、正確に一致します。

これが私がそれをした方法です。

   ... # initialized and blitted samples as before...
blankRGBA = (0,0,0,255)  # the RGBA value for null values in the index.

destpal   = list(BaseSurf.get_palette())
pallength = len(destpal) - 1 # This is probably always going to be 255, but just to be sure... (minus one 'cuz indices start at zero.)

nextblank = pallength - next((n for n, RGBA in enumerate(destpal[::-1]) if RGBA != blankRGBA), - 1)   
    #  ^ The palette will have a train of null values matching blankRGBA for every unusued index value. If the palette is complete full, it'll raise an error later.
    # This finds the index of the first such value by following the train until it 'starts', then subtracting that index number from the total length (probably 256). I'll explain why destpal.index(blankRGBA) is chancey later...

copypal = list(NewSurf.get_palette())
while True:
    if copypal[-1] == blankRGBA:  # Get rid of NewSurf's null index train, too.
        copypal.pop()
    else:
        print "Popped all trailing blank values from the incoming palette. %d entries remain." % len(copypal)
        break

    if not copypal:
        raise IndexError, "Depleted incoming palette. It was entirely blank entries?! What??"

    # Now that we have the useful section of NewSurf's palette (copypal) and the indices we can replace with it (nextblank), let's apply the new values and reindex NewSurf ahead of blitting it...

for n, newRGBA in enumerate(copypal):  
    if (nextblank + n) > 255: # It's possible the thing will fill up. For now, I'll have it throw an error.
        raise IndexError, "Ran out of palette space at %s! (colour number %d)" % (newRGBA, n)
    BaseSurf.set_palette_at((nextblank + n), newRGBA)  # Add the palette value to BaseSurf. As it happens, blit will reindex the colours on its own.

baseimage.blit(newimage, (33,33))
screen.blit(baseimage, (50, 122))

pygame.display.flip()

成功!

おそらく、ループcopypal = list(RGBA for RGBA in NewSurf.get_palette() if RGBA != blankRGBA)の代わりに次のようなものを使用して、NewSurf のパレットから空白の RGBA 値を消去することもできたでしょう。また、より複雑な命令の代わりにwhile True; ... copypal.pop()、BaseSurf のパレットで最初の空白を見つけることもできました。私がこれらのどちらもしなかった理由は、パレットが画像内の少なくとも 1 つのピクセルの値を使用した可能性があり、これらのピクセルが空白であることを意図していた可能性があるためです。(0,0,0,255 ) が画像のどこかで使用されます。destpal.index(blankRGBA)next()blankRGBA

おそらく、その場合、RGBA インデックスはパレットの最後ではなく最初にあるでしょう。それらが最後のインデックス以外の場所にある場合、それらは安全です。そうしないと、問題が発生する可能性があります。


画像データを厳密に制御できる状況では、これらの圧縮バージョンも機能する場合があります。

ただし、これらは読みにくいため Python 的ではない可能性が非常に高く、特定の問題 (実際に使用されている null に見えるインデックス値を誤って置き換える) や未処理の例外 (ベースサーフ)。

自己責任!

  # init as before...
for newRGBA in tuple(RGBA for RGBA in NewSurf.get_palette() if RGBA != blankRGBA):
    try: swapidx = next(n for n, RGBA in enumerate(BaseSurf.get_palette()) if RGBA == blankRGBA)
    except StopIteration: raise IndexError, "Ran out of palette space!"
    BaseSurf.set_palette_at(swapidx, newRGBA)

BaseSurf.blit(NewSurf, (33,33))
screen.blit(BaseSurf, (50, 122))

next()これは、 copypal の各値に対してパレットを反復処理するため、少し遅くなりますが、行数が少ないので、これは良いことです。

この恐ろしい、非常に非Pythonicなシングルトンを使用することさえできます:

[BaseSurf.set_palette_at(swapidx, newRGBA) for swapidx, newRGBA in zip(tuple(n for n, RGBA in enumerate(BaseSurf.get_palette()) if RGBA == blankRGBA), tuple(RGBA for RGBA in NewSurf.get_palette() if RGBA != blankRGBA))]  # PLEASE GOD NO.

どちらの短いバージョンもお勧めしません。私は学術的な考慮のためにそれらを含めただけです.

于 2014-12-14T06:22:54.067 に答える