[インデックス 11281] ファイルの概要
このコミットは、Go言語のimage
パッケージにおけるYCbCr画像表現の内部構造を根本的に変更するものです。具体的には、YCbCr画像のピクセルバッファが、画像の矩形領域の最小座標(Rect.Min
)から開始するように修正されました。これにより、YCbCr画像がRGBAやGrayなどの他の画像タイプと同様に、正の象限に限定されない任意の座標に配置できるようになります。また、YCbCr画像の描画コードのパフォーマンス最適化も含まれています。
コミット
commit ab2ea94c609cb2e6b6dd61ceea93a88a7a66b090
Author: Nigel Tao <nigeltao@golang.org>
Date: Fri Jan 20 10:44:22 2012 +1100
image: change the YCbCr image's pixel buffers to start at Rect.Min
instead of the origin.
This makes YCbCr match the other image types (e.g. RGBA, Gray) in
that an image's bounds is not restricted to the positive quadrant.
Also optimize the YCbCr draw code by hoisting some computation
outside of the loop.
benchmark old ns/op new ns/op delta
draw.BenchmarkYCbCr 2544418 2373558 -6.72%
Like https://golang.org/cl/4681044/ I don't think a gofix is
feasible. People will have to make manual changes. On the other hand,
directly manipulating YCbCr images is relatively rare, compared to
RGBA images, and if other code just uses the jpeg and draw packages
instead of messing directly with a YCbCr's []byte representations,
then things should just continue to work.
R=r
CC=golang-dev
https://golang.org/cl/5558048
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ab2ea94c609cb2e6b6dd61ceea93a88a7a66b090
元コミット内容
image
パッケージにおいて、YCbCr画像のピクセルバッファが原点からではなく、Rect.Min
から開始するように変更されました。これにより、YCbCr画像が他の画像タイプ(例: RGBA, Gray)と同様に、境界が正の象限に限定されないようになります。また、YCbCr描画コードの最適化として、ループ外への計算の巻き上げが行われました。この変更により、draw.BenchmarkYCbCr
ベンチマークで約6.72%のパフォーマンス向上が見られました。この変更はgofix
ツールでの自動修正が困難であるため、手動でのコード修正が必要となる可能性がありますが、YCbCr画像を直接操作するケースは比較的稀であるため、jpeg
やdraw
パッケージを介して利用している場合は影響が少ないとされています。
変更の背景
この変更の主な背景は以下の2点です。
-
画像表現の一貫性の向上: 従来のGoの
image
パッケージでは、RGBA
やGray
といった他の画像タイプは、そのピクセルデータが画像の矩形領域(Rect
)の最小座標(Rect.Min
)を基準としてオフセットされていました。しかし、YCbCr
画像は、そのピクセルバッファが常に原点(0,0)を基準としていました。この不整合は、画像処理を行う際にYCbCr画像を他の画像タイプと組み合わせる場合や、画像のサブイメージを扱う場合に混乱や不便を生じさせていました。このコミットは、YCbCr画像の内部表現を他の画像タイプと統一し、Rect.Min
を基準とするようにすることで、APIの一貫性と使いやすさを向上させることを目的としています。これにより、YCbCr画像も負の座標や任意のオフセットを持つことができるようになります。 -
パフォーマンスの最適化:
YCbCr
画像をRGBA
画像に描画する際のパフォーマンス改善も目的の一つです。特に、ピクセルごとの色変換(YCbCrからRGBへの変換)を行うループ内で繰り返し計算されていたオフセット計算をループの外に移動(hoisting)することで、計算コストを削減し、描画処理の高速化を図っています。ベンチマーク結果が示すように、この最適化により顕著なパフォーマンス向上が達成されています。
これらの変更は、Go言語の画像処理ライブラリの堅牢性と効率性を高めるための重要なステップと言えます。
前提知識の解説
このコミットを理解するためには、以下の概念について基本的な知識が必要です。
1. YCbCr色空間
YCbCrは、デジタルビデオや画像圧縮で広く使用される色空間です。人間の視覚が輝度(明るさ)の変化に敏感で、色差(色の違い)の変化には比較的鈍感であるという特性を利用しています。
- Y (Luma): 輝度成分。画像の明るさを表します。
- Cb (Chroma Blue): 青色差成分。青と黄色の間の色差を表します。
- Cr (Chroma Red): 赤色差成分。赤とシアンの間の色差を表します。
YCbCrはRGB(赤、緑、青)色空間から変換され、通常、Y成分はフル解像度で保持される一方で、CbとCr成分は人間の視覚特性に合わせてサブサンプリング(間引き)されることが多いです。
2. クロマサブサンプリング (Chroma Subsampling)
YCbCr色空間の大きな利点の一つは、色差成分(Cb, Cr)を間引くことでデータ量を削減できることです。これは、人間の目が輝度の変化に比べて色の変化に鈍感であるという事実に基づいています。一般的なサブサンプリング方式には以下のものがあります。
- 4:4:4: サブサンプリングなし。Y, Cb, Cr成分がすべて同じ解像度を持ちます。
- 4:2:2: 水平方向に色差成分を半分に間引きます。つまり、2つのYピクセルに対して1つのCbと1つのCrピクセルが対応します。
- 4:2:0: 水平方向と垂直方向の両方で色差成分を半分に間引きます。つまり、4つのYピクセル(2x2のブロック)に対して1つのCbと1つのCrピクセルが対応します。
Goのimage.YCbCr
構造体は、このサブサンプリング比率をSubsampleRatio
フィールドで管理しています。
3. Go言語のimage
パッケージにおける画像表現
Go言語の標準ライブラリであるimage
パッケージは、様々な画像フォーマットを扱うための基本的な型と関数を提供します。
image.Image
インターフェース: すべての画像タイプが実装するインターフェースで、Bounds() Rectangle
、ColorModel() color.Model
、At(x, y int) color.Color
などのメソッドを定義します。image.Rectangle
構造体: 画像の矩形領域を表します。Min
(左上隅の座標)とMax
(右下隅の座標)のimage.Point
を含みます。Min.X
,Min.Y
,Max.X
,Max.Y
で構成され、Dx()
(幅)とDy()
(高さ)メソッドを持ちます。image.YCbCr
構造体: YCbCr画像を表現するための具体的な型です。Y
,Cb
,Cr
: それぞれ輝度、青色差、赤色差のピクセルデータを格納する[]uint8
スライス。YStride
,CStride
: それぞれY、Cb/Crスライスにおける行ごとのバイト数(ピクセル数)を表します。これにより、メモリ上の連続したデータから特定の行のピクセルにアクセスできます。SubsampleRatio
: 使用されているクロマサブサンプリング比率。Rect
: 画像の論理的な境界を表すimage.Rectangle
。
4. ピクセルバッファのオフセット
Goのimage
パッケージでは、画像のピクセルデータは通常、一次元バイトスライス([]uint8
)としてメモリに格納されます。特定の座標(x, y)
のピクセルデータにアクセスするためには、この一次元スライス内でのオフセットを計算する必要があります。
従来のimage.YCbCr
では、Y
, Cb
, Cr
スライスは常に論理的な画像の原点(0,0)から始まるものとして扱われていました。しかし、image.RGBA
などの他の画像タイプでは、Rect.Min
が画像の論理的な左上隅を表し、ピクセルデータへのアクセスはRect.Min
からの相対座標で計算されます。このコミットは、YCbCr
もRect.Min
を基準とするように変更し、画像表現の一貫性を高めています。
技術的詳細
このコミットの技術的な変更点は多岐にわたりますが、主に以下の3つの側面があります。
1. YCbCr画像のピクセルバッファの基準点変更
最も重要な変更は、image.YCbCr
構造体のY
, Cb
, Cr
スライスが、画像の論理的な境界であるRect.Min
を基準としてピクセルデータを格納するように変更された点です。
変更前:
YCbCr画像のピクセルデータは、常に(0,0)を原点としてメモリに配置されていると仮定されていました。したがって、YOffset
やCOffset
のようなメソッドは、単にy*YStride + x
のような計算でオフセットを求めていました。これは、Rect.Min
が常に(0,0)であるか、またはピクセルデータへのアクセス時にRect.Min
が考慮されない場合にのみ正しく機能します。
変更後:
YCbCr
構造体のY
, Cb
, Cr
スライスは、Rect.Min
で定義される画像の論理的な左上隅に対応するピクセルから開始するように変更されました。これにより、YOffset
およびCOffset
メソッドの計算式が以下のように変更されました。
-
YOffset(x, y int) int
: 変更前:return y*p.YStride + x
変更後:return (y-p.Rect.Min.Y)*p.YStride + (x - p.Rect.Min.X)
この変更により、Y
スライス内のインデックスは、Rect.Min
を基準とした相対座標(x - p.Rect.Min.X, y - p.Rect.Min.Y)
に基づいて計算されるようになりました。 -
COffset(x, y int) int
: 変更前:return y*p.CStride + (x / 2)
(4:2:2の場合) 変更後:return (y-p.Rect.Min.Y)*p.CStride + (x/2 - p.Rect.Min.X/2)
(4:2:2の場合) 同様に、Cb
およびCr
スライス内のインデックスもRect.Min
を基準とした相対座標に基づいて計算されます。サブサンプリング比率に応じて、x
やy
が2で割られる点も考慮されています。
この変更により、YCbCr
画像もRect.Min
が(0,0)以外の値を持つことが可能になり、画像が負の座標空間に存在したり、サブイメージとして切り出されたりした場合でも、ピクセルデータへのアクセスが正しく行われるようになります。
2. SubImage
メソッドの実装変更
image.YCbCr
のSubImage
メソッドは、元の画像の一部を新しいYCbCr
画像として返す機能を提供します。
変更前:
SubImage
は、元のYCbCr
構造体をシャローコピーし、Rect
フィールドのみを更新していました。これは、ピクセルデータスライス(Y
, Cb
, Cr
)が元の画像と共有されるため、新しいRect
が元の画像のピクセルバッファの範囲外を指す可能性があり、At
メソッドなどでパニックを引き起こす可能性がありました。
変更後:
SubImage
メソッドは、新しいYCbCr
構造体を生成し、そのY
, Cb
, Cr
スライスを元の画像の対応するスライスのサブスライスとして設定します。この際、新しい矩形領域r
のMin.X
とMin.Y
に対応するオフセットを計算し、そのオフセットから始まるサブスライスを作成します。
// 変更後のSubImageの抜粋
func (p *YCbCr) SubImage(r Rectangle) Image {
r = r.Intersect(p.Rect)
if r.Empty() {
return &YCbCr{SubsampleRatio: p.SubsampleRatio}
}
yi := p.YOffset(r.Min.X, r.Min.Y)
ci := p.COffset(r.Min.X, r.Min.Y)
return &YCbCr{
Y: p.Y[yi:],
Cb: p.Cb[ci:],
Cr: p.Cr[ci:],
SubsampleRatio: p.SubsampleRatio,
YStride: p.YStride,
CStride: p.CStride,
Rect: r,
}
}
この変更により、SubImage
によって返される画像は、そのRect
が示す範囲内のピクセルデータのみを参照するようになり、より安全で直感的な動作が実現されます。
3. NewYCbCr
コンストラクタの追加
image.YCbCr
型を適切に初期化するための新しいコンストラクタ関数NewYCbCr
が追加されました。
追加前:
YCbCr
画像を生成するには、手動でY
, Cb
, Cr
スライスを確保し、YStride
, CStride
, SubsampleRatio
, Rect
を設定する必要がありました。特に、サブサンプリング比率に応じてCb
とCr
のバッファサイズやストライドを正しく計算するのは複雑でした。
追加後:
NewYCbCr(r Rectangle, subsampleRatio YCbCrSubsampleRatio) *YCbCr
関数が追加されました。この関数は、指定された矩形領域r
とサブサンプリング比率に基づいて、必要なサイズのY
, Cb
, Cr
バッファを自動的に確保し、YCbCr
構造体を適切に初期化します。これにより、YCbCr
画像の生成がより簡単かつ安全になりました。
// NewYCbCr関数の抜粋
func NewYCbCr(r Rectangle, subsampleRatio YCbCrSubsampleRatio) *YCbCr {
w, h, cw, ch := r.Dx(), r.Dy(), 0, 0
switch subsampleRatio {
case YCbCrSubsampleRatio422:
cw = (r.Max.X+1)/2 - r.Min.X/2
ch = h
case YCbCrSubsampleRatio420:
cw = (r.Max.X+1)/2 - r.Min.X/2
ch = (r.Max.Y+1)/2 - r.Min.Y/2
default: // 4:4:4
cw = w
ch = h
}
b := make([]byte, w*h+2*cw*ch) // Y, Cb, Crのバッファをまとめて確保
return &YCbCr{
Y: b[:w*h],
Cb: b[w*h+0*cw*ch : w*h+1*cw*ch],
Cr: b[w*h+1*cw*ch : w*h+2*cw*ch],
SubsampleRatio: subsampleRatio,
YStride: w,
CStride: cw,
Rect: r,
}
}
この関数は、jpeg
パッケージのリーダーなど、YCbCr画像を生成する場所で利用されるようになります。
4. draw.YCbCr
描画コードの最適化
src/pkg/image/draw/draw.go
内のdrawYCbCr
関数において、YCbCr画像をRGBA画像に描画する際のループ内の計算が最適化されました。
変更前:
ループ内でyi
(Yオフセット) やci
(Cb/Crオフセット) がピクセルごとに再計算されていました。
変更後:
yi
とciBase
(Cb/Crのベースオフセット)の計算がループの外に巻き上げられました。ループ内では、これらのベースオフセットにsx
(ソースX座標)やsx/2
を加算するだけで、現在のピクセルのオフセットを効率的に計算できるようになりました。
// 4:2:2 サブサンプリングの場合の変更例
// 変更前:
// for x, sx := x0, sp.X; x != x1; x, sx = x+4, sx+1 {
// i := sx / 2
// yy = src.Y[sy*src.YStride+sx]
// cb = src.Cb[sy*src.CStride+i]
// cr = src.Cr[sy*src.CStride+i]
// rr, gg, bb := color.YCbCrToRGB(yy, cb, cr)
// }
// 変更後:
// yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X)
// ciBase := (sy-src.Rect.Min.Y)*src.CStride - src.Rect.Min.X/2
// for x, sx := x0, sp.X; x != x1; x, sx, yi = x+4, sx+1, yi+1 {
// ci := ciBase + sx/2
// rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci])
// }
この最適化により、ループごとの計算量が減少し、描画パフォーマンスが向上しました。ベンチマーク結果(約6.72%の改善)がこの効果を裏付けています。
5. image.jpeg
パッケージの更新
src/pkg/image/jpeg/reader.go
内のJPEGデコーダが、新しいNewYCbCr
コンストラクタを使用するように変更されました。これにより、JPEG画像をデコードしてYCbCr
画像を生成するプロセスが、新しいYCbCr表現と整合性が取れるようになりました。
6. テストケースの追加
src/pkg/image/ycbcr_test.go
という新しいテストファイルが追加され、YCbCr
構造体の変更が正しく機能することを確認するための広範なテストケースが記述されました。特に、様々な矩形領域、サブサンプリング比率、およびオフセットを持つYCbCr
画像の生成、ピクセルアクセス(YOffset
, COffset
)、およびサブイメージの動作が検証されています。これにより、変更の正確性と堅牢性が保証されます。
これらの技術的変更は、Goの画像処理ライブラリの内部的な整合性を高め、パフォーマンスを改善し、将来的な拡張性を向上させるための重要なステップです。
コアとなるコードの変更箇所
このコミットで変更された主要なファイルと、その中のコアとなる変更箇所は以下の通りです。
-
src/pkg/image/draw/draw.go
:drawYCbCr
関数内のループ最適化。yy
,cb
,cr
変数の削除と、yi
,ciBase
の計算をループ外に巻き上げ、ループ内のオフセット計算を簡素化。
-
src/pkg/image/image.go
:NewRGBA
,NewRGBA64
,NewNRGBA
,NewNRGBA64
,NewAlpha
,NewAlpha16
,NewGray
,NewGray16
関数のコメントが「width and height」から「bounds」に変更され、Rectangle
引数が単なる幅と高さだけでなく、オフセットも含むことを明確化。
-
src/pkg/image/jpeg/reader.go
:decoder.makeImg
関数内で、image.YCbCr
の生成方法が手動でのバイトスライス割り当てから、新しく追加されたimage.NewYCbCr
関数を使用するように変更。
-
src/pkg/image/ycbcr.go
:YCbCrSubsampleRatio
にString()
メソッドが追加され、サブサンプリング比率の文字列表現を提供。YCbCr
構造体からY
,Cb
,Cr
フィールドの個別の宣言が削除され、Y, Cb, Cr []uint8
としてまとめて宣言。これは機能的な変更ではなく、コードの簡潔化。YOffset
メソッドのオフセット計算ロジックが、Rect.Min
を考慮するように変更。return (y-p.Rect.Min.Y)*p.YStride + (x - p.Rect.Min.X)
COffset
メソッドのオフセット計算ロジックが、Rect.Min
を考慮するように変更。サブサンプリング比率に応じてx
やy
が2で割られる点も考慮。return (y-p.Rect.Min.Y)*p.CStride + (x/2 - p.Rect.Min.X/2)
(4:2:2の場合)return (y/2-p.Rect.Min.Y/2)*p.CStride + (x/2 - p.Rect.Min.X/2)
(4:2:0の場合)return (y-p.Rect.Min.Y)*p.CStride + (x - p.Rect.Min.X)
(4:4:4の場合)
SubImage
メソッドの実装が大幅に変更され、新しい矩形領域に対応するサブスライスを返すように修正。NewYCbCr
関数が新しく追加され、指定された矩形とサブサンプリング比率に基づいてYCbCr
画像を初期化する機能を提供。
-
src/pkg/image/ycbcr_test.go
:- 新しいテストファイルとして追加。
TestYCbCr
関数とtestYCbCr
ヘルパー関数が含まれ、様々なRectangle
、YCbCrSubsampleRatio
、Point
の組み合わせでNewYCbCr
、YOffset
、COffset
、SubImage
、At
メソッドの動作を検証。特に、Rect.Min
が非ゼロの場合の動作が重点的にテストされています。
- 新しいテストファイルとして追加。
これらの変更は、YCbCr
画像の内部表現と操作方法に大きな影響を与え、Goの画像処理ライブラリ全体の一貫性と効率性を向上させています。
コアとなるコードの解説
このコミットの核心は、image.YCbCr
型のピクセルデータへのアクセス方法と、その生成・サブイメージ化のロジックの変更にあります。
src/pkg/image/ycbcr.go
における変更
YOffset
および COffset
メソッドの変更
これらのメソッドは、特定の座標 (x, y)
に対応するY、Cb、またはCr成分が格納されているバイトスライス内のインデックスを計算します。
変更前:
func (p *YCbCr) YOffset(x, y int) int {
return y*p.YStride + x
}
func (p *YCbCr) COffset(x, y int) int {
switch p.SubsampleRatio {
case YCbCrSubsampleRatio422:
return y*p.CStride + (x / 2)
case YCbCrSubsampleRatio420:
return (y/2)*p.CStride + (x / 2)
}
return y*p.CStride + x // Default to 4:4:4 subsampling.
}
この古い実装では、ピクセルデータが常に論理的な原点 (0,0)
から始まるものと仮定していました。つまり、Rect.Min
の値はオフセット計算に考慮されていませんでした。
変更後:
func (p *YCbCr) YOffset(x, y int) int {
return (y-p.Rect.Min.Y)*p.YStride + (x - p.Rect.Min.X)
}
func (p *YCbCr) COffset(x, y int) int {
switch p.SubsampleRatio {
case YCbCrSubsampleRatio422:
return (y-p.Rect.Min.Y)*p.CStride + (x/2 - p.Rect.Min.X/2)
case YCbCrSubsampleRatio420:
return (y/2-p.Rect.Min.Y/2)*p.CStride + (x/2 - p.Rect.Min.X/2)
}
return (y-p.Rect.Min.Y)*p.CStride + (x - p.Rect.Min.X) // Default to 4:4:4 subsampling.
}
新しい実装では、x
と y
の座標から p.Rect.Min.X
と p.Rect.Min.Y
をそれぞれ減算しています。これにより、計算されるオフセットは、YCbCr
画像の Rect.Min
を基準とした相対的な位置を示すようになります。この変更により、YCbCr
画像が負の座標空間に存在したり、サブイメージとして切り出されたりした場合でも、ピクセルデータへのアクセスが正しく行われるようになります。これは、他の image
型(例: RGBA
)との一貫性を保つ上で非常に重要です。
SubImage
メソッドの変更
SubImage
メソッドは、既存の画像から指定された矩形領域に対応する新しい画像(サブイメージ)を生成します。
変更前:
func (p *YCbCr) SubImage(r Rectangle) Image {
q := new(YCbCr)
*q = *p
q.Rect = q.Rect.Intersect(r)
return q
}
この実装は、元の YCbCr
構造体をシャローコピーし、Rect
フィールドのみを更新していました。これは、Y
, Cb
, Cr
スライスが元の画像と共有されるため、新しい Rect
が元の画像のピクセルバッファの範囲外を指す可能性があり、At
メソッドなどでパニックを引き起こす可能性がありました。
変更後:
func (p *YCbCr) SubImage(r Rectangle) Image {
r = r.Intersect(p.Rect)
// If r1 and r2 are Rectangles, r1.Intersect(r2) is not guaranteed to be inside
// either r1 or r2 if the intersection is empty. Without explicitly checking for
// this, the Pix[i:] expression below can panic.
if r.Empty() {
return &YCbCr{
SubsampleRatio: p.SubsampleRatio,
}
}
yi := p.YOffset(r.Min.X, r.Min.Y)
ci := p.COffset(r.Min.X, r.Min.Y)
return &YCbCr{
Y: p.Y[yi:],
Cb: p.Cb[ci:],
Cr: p.Cr[ci:],
SubsampleRatio: p.SubsampleRatio,
YStride: p.YStride,
CStride: p.CStride,
Rect: r,
}
}
新しい実装では、まず r
と p.Rect
の交差部分を計算し、結果が空の場合は空の YCbCr
を返します。重要なのは、p.Y[yi:]
, p.Cb[ci:]
, p.Cr[ci:]
のように、元のスライスからサブスライスを作成している点です。yi
と ci
は、新しい矩形領域 r
の Min.X
と Min.Y
に対応する元のスライス内の開始インデックスです。これにより、返されるサブイメージは、その Rect
が示す範囲内のピクセルデータのみを参照するようになり、より安全で直感的な動作が実現されます。
NewYCbCr
関数の追加
この関数は、指定された矩形領域とサブサンプリング比率に基づいて、新しい YCbCr
画像を生成し、そのピクセルバッファを適切に初期化します。
func NewYCbCr(r Rectangle, subsampleRatio YCbCrSubsampleRatio) *YCbCr {
w, h, cw, ch := r.Dx(), r.Dy(), 0, 0
switch subsampleRatio {
case YCbCrSubsampleRatio422:
cw = (r.Max.X+1)/2 - r.Min.X/2
ch = h
case YCbCrSubsampleRatio420:
cw = (r.Max.X+1)/2 - r.Min.X/2
ch = (r.Max.Y+1)/2 - r.Min.Y/2
default: // Default to 4:4:4 subsampling.
cw = w
ch = h
}
// Y, Cb, Crのバッファをまとめて確保
b := make([]byte, w*h+2*cw*ch)
return &YCbCr{
Y: b[:w*h],
Cb: b[w*h+0*cw*ch : w*h+1*cw*ch],
Cr: b[w*h+1*cw*ch : w*h+2*cw*ch],
SubsampleRatio: subsampleRatio,
YStride: w,
CStride: cw,
Rect: r,
}
}
この関数は、r.Dx()
と r.Dy()
を使用して画像の幅と高さを計算し、サブサンプリング比率に基づいて Cb
と Cr
のコンポーネントの幅 (cw
) と高さ (ch
) を決定します。その後、必要なすべてのピクセルデータを格納するための単一のバイトスライス b
を割り当て、そのスライスを Y
, Cb
, Cr
の各フィールドに適切に分割して割り当てます。これにより、YCbCr
画像の生成がより簡単かつ安全になり、特に jpeg
デコーダのような場所で利用されます。
src/pkg/image/draw/draw.go
における変更
drawYCbCr
関数の最適化
この関数は、YCbCr
画像を RGBA
画像に描画する際に使用されます。
変更前:
// 4:2:2 サブサンプリングの場合の抜粋
for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
dpix := dst.Pix[y*dst.Stride:]
for x, sx := x0, sp.X; x != x1; x, sx = x+4, sx+1 {
i := sx / 2
yy = src.Y[sy*src.YStride+sx]
cb = src.Cb[sy*src.CStride+i]
cr = src.Cr[sy*src.CStride+i]
rr, gg, bb := color.YCbCrToRGB(yy, cb, cr)
// ... RGBAピクセル設定 ...
}
}
このコードでは、内側のループ(x
と sx
)の各イテレーションで yy
, cb
, cr
のインデックス計算が繰り返し行われていました。
変更後:
// 4:2:2 サブサンプリングの場合の抜粋
for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
dpix := dst.Pix[y*dst.Stride:]
// ループ外に計算を巻き上げ
yi := (sy-src.Rect.Min.Y)*src.YStride + (sp.X - src.Rect.Min.X)
ciBase := (sy-src.Rect.Min.Y)*src.CStride - src.Rect.Min.X/2
for x, sx := x0, sp.X; x != x1; x, sx, yi = x+4, sx+1, yi+1 {
ci := ciBase + sx/2
rr, gg, bb := color.YCbCrToRGB(src.Y[yi], src.Cb[ci], src.Cr[ci])
// ... RGBAピクセル設定 ...
}
}
変更後では、yi
と ciBase
の計算が外側のループ(y
と sy
)の各イテレーションで一度だけ行われるように巻き上げられています。内側のループでは、yi
は yi+1
でインクリメントされ、ci
は ciBase + sx/2
で計算されます。これにより、ループ内の計算量が減少し、描画処理のパフォーマンスが向上します。これは、コミットメッセージに記載されているベンチマーク結果(約6.72%の改善)に貢献しています。
これらの変更は、Goの画像処理ライブラリにおける YCbCr
画像の扱いをより堅牢で効率的、かつ他の画像タイプと一貫性のあるものにするための重要なステップです。
関連リンク
- Go CL 5558048: https://golang.org/cl/5558048
- Go CL 4681044: https://golang.org/cl/4681044 (このコミットで参照されている、
gofix
が困難な類似の変更)
参考にした情報源リンク
- コミットメッセージと差分
- Go言語の
image
パッケージのドキュメント (Go標準ライブラリ) - YCbCr色空間とクロマサブサンプリングに関する一般的な知識