Claudeに簡単な画像処理をきいてみた
画像処理の話をするとBayer,RGB,YUVといった話がよく出る.
これらについて一般的なJPEGになるまでの処理をClaudeに聞いてコードを生成した.
#include <stdint.h>
#define WIDTH 1920
#define HEIGHT 1080
static uint8_t clamp_u8( int val )
{
if( val<0 ) {
return 0;
} else if( val>255 ) {
return 255;
} else {
return (uint8_t)val;
}
}
static uint8_t get_pixel( const uint8_t bayer[HEIGHT][WIDTH], int row, int col )
{
if( row<0 ) {
row = 0;
}
if( row>=HEIGHT ) {
row = HEIGHT - 1;
}
if( col<0 ) {
col = 0;
}
if( col>=WIDTH ) {
col = WIDTH - 1;
}
return bayer[row][col];
}
/* RGGBパターン想定 */
void bayer_to_rgb(
const uint8_t bayer[HEIGHT][WIDTH],
uint8_t r_out[HEIGHT][WIDTH],
uint8_t g_out[HEIGHT][WIDTH],
uint8_t b_out[HEIGHT][WIDTH] )
{
int row;
int col;
for( row=0; row<HEIGHT; row=row+1 ) {
for( col=0; col<WIDTH; col=col+1 ) {
int is_even_row;
int is_even_col;
int r;
int g;
int b;
is_even_row = ( row%2==0 );
is_even_col = ( col%2==0 );
if( is_even_row&&is_even_col ) {
/* R画素の位置 */
r = get_pixel( bayer, row, col );
g = ( get_pixel( bayer, row-1, col ) + get_pixel( bayer, row+1, col )
+ get_pixel( bayer, row, col-1 ) + get_pixel( bayer, row, col+1 ) ) / 4;
b = ( get_pixel( bayer, row-1, col-1 ) + get_pixel( bayer, row-1, col+1 )
+ get_pixel( bayer, row+1, col-1 ) + get_pixel( bayer, row+1, col+1 ) ) / 4;
} else if( is_even_row&&!is_even_col ) {
/* G画素の位置(R行) */
r = ( get_pixel( bayer, row, col-1 ) + get_pixel( bayer, row, col+1 ) ) / 2;
g = get_pixel( bayer, row, col );
b = ( get_pixel( bayer, row-1, col ) + get_pixel( bayer, row+1, col ) ) / 2;
} else if( !is_even_row&&is_even_col ) {
/* G画素の位置(B行) */
r = ( get_pixel( bayer, row-1, col ) + get_pixel( bayer, row+1, col ) ) / 2;
g = get_pixel( bayer, row, col );
b = ( get_pixel( bayer, row, col-1 ) + get_pixel( bayer, row, col+1 ) ) / 2;
} else {
/* B画素の位置 */
r = ( get_pixel( bayer, row-1, col-1 ) + get_pixel( bayer, row-1, col+1 )
+ get_pixel( bayer, row+1, col-1 ) + get_pixel( bayer, row+1, col+1 ) ) / 4;
g = ( get_pixel( bayer, row-1, col ) + get_pixel( bayer, row+1, col )
+ get_pixel( bayer, row, col-1 ) + get_pixel( bayer, row, col+1 ) ) / 4;
b = get_pixel( bayer, row, col );
}
r_out[row][col] = clamp_u8( r );
g_out[row][col] = clamp_u8( g );
b_out[row][col] = clamp_u8( b );
}
}
}
module bayer2x2_to_rgb #(
parameter DATA_WIDTH = 8
) (
input clk
,input rst_n
,input valid_in
,input [DATA_WIDTH-1:0] bayer_00 // R
,input [DATA_WIDTH-1:0] bayer_01 // G
,input [DATA_WIDTH-1:0] bayer_10 // G
,input [DATA_WIDTH-1:0] bayer_11 // B
,output reg valid_out
,output reg [DATA_WIDTH-1:0] r_out
,output reg [DATA_WIDTH-1:0] g_out
,output reg [DATA_WIDTH-1:0] b_out
);
// 桁上がり保持のため1bit拡張して加算
wire [DATA_WIDTH:0] g_sum;
assign g_sum = bayer_01 + bayer_10;
always @( posedge clk or negedge rst_n ) begin
if( !rst_n ) begin
valid_out <= 1'b0;
r_out <= {DATA_WIDTH{1'b0}};
g_out <= {DATA_WIDTH{1'b0}};
b_out <= {DATA_WIDTH{1'b0}};
end else begin
if( valid_in ) begin
valid_out <= 1'b1;
r_out <= bayer_00;
g_out <= g_sum[DATA_WIDTH:1];
b_out <= bayer_11;
end else begin
valid_out <= 1'b0;
end
end
end
endmodule
#include <stdint.h>
#define WIDTH 1920
#define HEIGHT 1080
/* WIDTHは偶数前提 */
/* BT.601フルレンジ係数の256倍スケール整数近似 */
/* Y = ( 77R + 150G + 29B ) >> 8 */
/* U = ( -43R - 85G + 128B ) >> 8 + 128 */
/* V = ( 128R - 107G - 21B ) >> 8 + 128 */
static uint8_t clamp_u8( int val )
{
if( val<0 ) {
return 0;
} else if( val>255 ) {
return 255;
} else {
return (uint8_t)val;
}
}
void rgb_to_yuv422(
const uint8_t r_in[HEIGHT][WIDTH],
const uint8_t g_in[HEIGHT][WIDTH],
const uint8_t b_in[HEIGHT][WIDTH],
uint8_t y_out[HEIGHT][WIDTH],
uint8_t u_out[HEIGHT][WIDTH/2],
uint8_t v_out[HEIGHT][WIDTH/2] )
{
int row;
int col;
/* Y: 全画素で計算(+128は丸め) */
for( row=0; row<HEIGHT; row=row+1 ) {
for( col=0; col<WIDTH; col=col+1 ) {
int y;
y = ( 77 * r_in[row][col] + 150 * g_in[row][col] + 29 * b_in[row][col] + 128 ) >> 8;
y_out[row][col] = clamp_u8( y );
}
}
/* U/V: 水平2画素のRGB平均から全ラインで計算(4:2:2) */
for( row=0; row<HEIGHT; row=row+1 ) {
for( col=0; col<WIDTH; col=col+2 ) {
int r_avg;
int g_avg;
int b_avg;
int u;
int v;
r_avg = ( r_in[row][col] + r_in[row][col+1] + 1 ) / 2;
g_avg = ( g_in[row][col] + g_in[row][col+1] + 1 ) / 2;
b_avg = ( b_in[row][col] + b_in[row][col+1] + 1 ) / 2;
/* 32896 = 32768(オフセット128<<8) + 128(丸め) */
u = ( 32896 - 43 * r_avg - 85 * g_avg + 128 * b_avg ) >> 8;
v = ( 32896 + 128 * r_avg - 107 * g_avg - 21 * b_avg ) >> 8;
u_out[row][col/2] = clamp_u8( u );
v_out[row][col/2] = clamp_u8( v );
}
}
}
module rgb_to_yuv422 #(
parameter DATA_WIDTH = 8 // 係数と中間ビット幅は8bit前提
) (
input clk
,input rst_n
,input valid_in
,input [DATA_WIDTH-1:0] r_in
,input [DATA_WIDTH-1:0] g_in
,input [DATA_WIDTH-1:0] b_in
,output reg valid_y
,output reg [DATA_WIDTH-1:0] y_out
,output reg valid_uv
,output reg [DATA_WIDTH-1:0] u_out
,output reg [DATA_WIDTH-1:0] v_out
);
reg phase; // 0:水平ペアの1画素目, 1:2画素目
reg [DATA_WIDTH-1:0] r_hold;
reg [DATA_WIDTH-1:0] g_hold;
reg [DATA_WIDTH-1:0] b_hold;
wire [15:0] y_pre;
wire [DATA_WIDTH:0] r_sum;
wire [DATA_WIDTH:0] g_sum;
wire [DATA_WIDTH:0] b_sum;
wire [DATA_WIDTH-1:0] r_avg;
wire [DATA_WIDTH-1:0] g_avg;
wire [DATA_WIDTH-1:0] b_avg;
wire [16:0] u_pre;
wire [16:0] v_pre;
// Y = ( 77R + 150G + 29B + 128(丸め) ) >> 8
assign y_pre = 16'd77 * r_in + 16'd150 * g_in + 16'd29 * b_in + 16'd128;
// 水平2画素の平均(+1は丸め、9bitで桁上がり保持)
assign r_sum = r_hold + r_in + 1'b1;
assign g_sum = g_hold + g_in + 1'b1;
assign b_sum = b_hold + b_in + 1'b1;
assign r_avg = r_sum[DATA_WIDTH:1];
assign g_avg = g_sum[DATA_WIDTH:1];
assign b_avg = b_sum[DATA_WIDTH:1];
// 32896 = 32768(オフセット128<<8) + 128(丸め)
// 定数を先に置くことで演算途中も非負となり符号なし演算で完結
assign u_pre = 17'd32896 - 17'd43 * r_avg - 17'd85 * g_avg + 17'd128 * b_avg;
assign v_pre = 17'd32896 + 17'd128 * r_avg - 17'd107 * g_avg - 17'd21 * b_avg;
always @( posedge clk or negedge rst_n ) begin
if( !rst_n ) begin
phase <= 1'b0;
r_hold <= {DATA_WIDTH{1'b0}};
g_hold <= {DATA_WIDTH{1'b0}};
b_hold <= {DATA_WIDTH{1'b0}};
valid_y <= 1'b0;
y_out <= {DATA_WIDTH{1'b0}};
valid_uv <= 1'b0;
u_out <= {DATA_WIDTH{1'b0}};
v_out <= {DATA_WIDTH{1'b0}};
end else begin
// Y: 有効サイクルごとに1画素出力
if( valid_in ) begin
valid_y <= 1'b1;
y_out <= y_pre[15:8];
end else begin
valid_y <= 1'b0;
end
// U/V: 水平2画素に1回出力(4:2:2)
if( valid_in ) begin
if( phase==1'b0 ) begin
r_hold <= r_in;
g_hold <= g_in;
b_hold <= b_in;
phase <= 1'b1;
valid_uv <= 1'b0;
end else begin
// u_pre/v_preは最大65536(17bit)となり得るため飽和処理
if( u_pre[16] ) begin
u_out <= {DATA_WIDTH{1'b1}};
end else begin
u_out <= u_pre[15:8];
end
if( v_pre[16] ) begin
v_out <= {DATA_WIDTH{1'b1}};
end else begin
v_out <= v_pre[15:8];
end
phase <= 1'b0;
valid_uv <= 1'b1;
end
end else begin
valid_uv <= 1'b0;
end
end
end
endmodule