Int型の変数をUInt8型にクランプして代入する
画像処理をしている場合、途中で Int 型の変数を経由して結果を UInt8 型のデータに戻すというシチュエーションが多くあります。
具体的にはInt型の変数の値が 256 以上になっていたら 255 にして、値が 0 未満(マイナス)になっていたら 0 にする、のようなコードです。
if文をつかってそういったコードをまともに書くのもよいですが、Swift ではコードを簡略化できるアプローチがあるので下記に紹介します。2種類あります。
UInt8 型キャストのときに clamping: を用いる
キャストするときに clampling: をつかえば、範囲外の値をキャストしようとしても、範囲内にクランプしてキャストしてくれます。UInt8 ならば 0 〜 255 にしてからキャストしてくれますので、実行時エラーの発生を抑えることができます。
// 値を型にあわせてクランプする Debug0.
Button( action: {
let NUM_DATA = 4
var data_a : [UInt8] = []
var data_b : [UInt8] = []
var data_diff : [UInt8] = []
data_a = Array( repeating: 0, count: NUM_DATA )
data_b = Array( repeating: 0, count: NUM_DATA )
data_diff = Array( repeating: 0, count: NUM_DATA )
data_a[0] = UInt8( 255 )
data_a[1] = UInt8( 0 )
data_a[2] = UInt8( 0 )
data_a[3] = UInt8( 255 )
data_b[0] = UInt8( 0 )
data_b[1] = UInt8( 255 )
data_b[2] = UInt8( 0 )
data_b[3] = UInt8( 255 )
let OFFSET: Int = 128
var diff: Int = 0
for n in 0 ..< NUM_DATA {
// ふたつのデータ系列の差分をとって、それにオフセットを加える.
diff = Int( data_a[n] ) - Int( data_b[n] ) + OFFSET
// 0 〜 255 の範囲に収めて UInt8 型に代入する.
let clamped_value: UInt8 = UInt8(clamping: diff)
// UInt8 型のデータを UInt8 型の配列に代入する.
data_diff[n] = clamped_value
// コンソールに出力する.
print( String( format: "%d,%d,%d,%d", data_a[n], data_b[n], diff, data_diff[n] ))
}
}){
Text( "debug (clamping)" )
.foregroundColor( Color.white )
}
.padding()
.background( Color.blue )
maxとminを組み合わせて用いる
これは、ああなるほどね、ってなるコロンブスの卵的やり方ですが、可読性がやや低いので初心者の人が混じっているチームで仕事している場合は避けた方がいいかもしれません。
min は二つの引数のうち小さいほうを選ぶメソッドです。max は二つの引数のうち大きいほうを選ぶメソッドです。これの引数を工夫して組み合わせると、得られる値が 0 〜 255 にクランプされます。あとはこれを UInt8 でキャストして代入してやればいいです。
// 値を型にあわせてクランプする Debug1.
Button( action: {
let NUM_DATA = 4
var data_a : [UInt8] = []
var data_b : [UInt8] = []
var data_diff : [UInt8] = []
data_a = Array( repeating: 0, count: NUM_DATA )
data_b = Array( repeating: 0, count: NUM_DATA )
data_diff = Array( repeating: 0, count: NUM_DATA )
data_a[0] = UInt8( 255 )
data_a[1] = UInt8( 0 )
data_a[2] = UInt8( 0 )
data_a[3] = UInt8( 255 )
data_b[0] = UInt8( 0 )
data_b[1] = UInt8( 255 )
data_b[2] = UInt8( 0 )
data_b[3] = UInt8( 255 )
let OFFSET: Int = 128
var diff: Int = 0
for n in 0 ..< NUM_DATA {
// ふたつのデータ系列の差分をとって、それにオフセットを加える.
diff = Int( data_a[n] ) - Int( data_b[n] ) + OFFSET
// 0 〜 255 の範囲に収めて UInt8 型に代入する.
let clamped_value: UInt8 = UInt8(max(0, min(255, diff)))
// UInt8 型のデータを UInt8 型の配列に代入する.
data_diff[n] = clamped_value
// コンソールに出力する.
print( String( format: "%d,%d,%d,%d", data_a[n], data_b[n], diff, data_diff[n] ))
}
}){
Text( "debug1 (max,min)" )
.foregroundColor( Color.white )
}
.padding()
.background( Color.blue )
上記の二つのコードは、下記のようなコンソール出力が得られます。どちらも同じです。
255,0,383,255
0,255,-127,0
0,0,128,128
255,255,128,128