第8章 色を変える

カラー・バーを作る

カラーバー
上のような、白、黄、シアン、緑、マゼンタ、赤、緑、黒の順に並んだカラー・バーを作成する。
色の詳細は以下の通りである。

白
R,G,Bが同じ割合で混ざったもの。
黄
R,Gが同じ割合で混ざったもの。
シアン
G,Bが同じ割合で混ざったもの。
マゼンタ
R,Bが同じ割合で混ざったもの。
黒
すべて0のもの。

このようなカラーバーを作るためには、R,G,Bのプレーンをしたの用にデータを 書き込めばよい。
白が明るくする部分である。

Rプレーン Gプレーン Bプレーン
Rプレーン Gプレーン Bプレーン

色を調べる

カラー・バー
カラー・バーを元に、輝度や色相(Hue)や彩度(Saturation)を表示する。

輝度

輝度
左から順に輝度が変化しているのがわかる。
(本書に輝度情報を画像に表示するプログラムはないが、 R, G, BとY, R-Y, B-Yの変換を行う関数のリスト中の y が輝度情報を保持しているので、 y を0〜255に変換し、 画像にすればよい。ここではそのプログラムの実装は行わない。)

色相

赤色を基準にした色相
赤色(113.2°)を基準にした色相を表示した。
赤から離れるほど暗くなっているのがわかる。

彩度

彩度
鮮やかな色ほど明るく表示されているのがわかる。

輝度、色相、彩度を変える

カラー画像をR,G,Bの信号ではなく輝度信号と色差信号に分離して扱うと、 そのカラー画像の色の特徴がよくわかる。
そこで、輝度、色相、彩度に分けたカラー画像を画像処理した後、 R,G,B成分に変換すれば、輝度、色相、彩度を自在に扱うことができる。

元画像
上の画像に対して、色を変える処理を行い、その例を以下に示す。

輝度を変える

輝度を2倍に
輝度を2倍した(彩度等倍、色相の増分を0°)。
明るくなったのがわかる。

色度を変える

色度を2倍
色度を2倍にした(輝度1倍、色相の増分を0°)。
鮮やかになったのがわかる。

色相を変える

色相を90°変化
色相を90°ずらした(輝度、彩度等倍)。
明るさや鮮やかさは変わっていないが、色相が変化したのがわかる。

プログラミング例

カラー・バーを作る

#include "Params.h"

/*---- colorbar --- カラーバーを作る ------------------------------------------
    image_r:    出力画像配列 R
    image_g:    出力画像配列 G
    image_b:    出力画像配列 B
    level:        濃度値
-----------------------------------------------------------------------------*/
void colorbar(unsigned char image_r[Y_SIZE][X_SIZE], 
    unsigned char image_g[Y_SIZE][X_SIZE], 
    unsigned char image_b[Y_SIZE][X_SIZE], int level)
{
    int     i, j, width;

    width = X_SIZE / 8;
    for (i = 0; i < Y_SIZE; i++){
        for (j = 0; j < X_SIZE; j++){
            if (((j >= 0) && (j < 2*width)) ||  /* Rプレーン */
                    ((j >= 4*width) && (j < 6*width)))
                image_r[i][j] = level;
            else image_r[i][j] = 0;
            if ((j >= 0) && (j < 4*width ))     /* Gプレーン */
                image_g[i][j] = level;
            else image_g[i][j] = 0;
            if (((j >= 0) && (j < width )) ||   /* Bプレーン */
                    ((j >= 2*width) && (j < 3*width)) ||
                    ((j >= 4*width) && (j < 5*width)) ||
                    ((j >= 6*width) && (j < 7*width)))
                image_b[i][j] = level;
            else image_b[i][j] = 0;
        }
    }
}

R,G,Bから輝度,色差信号に変換する

#include "Params.h"

/*--- rgb_to_yc --- R,G,Bから輝度,色差信号に変換する -----------------------
    image_r:    入力画像配列    R
    image_g:    入力画像配列    G
    image_b:    入力画像配列    B
    y:            出力データ配列 Y
    c1:            出力データ配列R−Y
    c2:            出力データ配列B−Y
-----------------------------------------------------------------------------*/
void rgb_to_yc(unsigned char image_r[Y_SIZE][X_SIZE], 
    unsigned char image_g[Y_SIZE][X_SIZE], 
    unsigned char image_b[Y_SIZE][X_SIZE], 
    int y[Y_SIZE][X_SIZE], int c1[Y_SIZE][X_SIZE], int c2[Y_SIZE][X_SIZE])
{
    int        i, j;
    float    fr, fg, fb;

    for (i = 0; i < Y_SIZE; i++) {
        for (j = 0; j < X_SIZE; j++) {
            fr = (float)image_r[i][j];
            fg = (float)image_g[i][j];
            fb = (float)image_b[i][j];
            y[i][j] = (int)(0.3 * fr + 0.59 * fg + 0.11 * fb);
            c1[i][j] = (int)(0.7 * fr - 0.59 * fg - 0.11 * fb);
            c2[i][j] = (int)(-0.3 * fr - 0.59 * fg + 0.89 * fb);
        }  
    }
}

/*--- yc_to_rgb --- 輝度,色差信号からR,G,B信号に変換する -------------------
    y:            入力データ配列 Y
    c1:            入力データ配列R−Y
    c2:            入力データ配列B−Y
    image_r:    出力画像配列    R
    image_g:    出力画像配列    G
    image_b:    出力画像配列    B
------------------------------------------------------------------------------*/
void yc_to_rgb(int y[Y_SIZE][X_SIZE],
    int c1[Y_SIZE][X_SIZE],
    int c2[Y_SIZE][X_SIZE],
    unsigned char image_r[Y_SIZE][X_SIZE],
    unsigned char image_g[Y_SIZE][X_SIZE],
    unsigned char image_b[Y_SIZE][X_SIZE])
{
    int    i, j;
    int    ir, ig, ib;

    for (i = 0; i < Y_SIZE; i++) {
        for (j = 0; j < X_SIZE; j++) {
            ir = y[i][j] + c1[i][j];
            if (ir > 255) ir = 255;
            if (ir < 0) ir = 0;
            ig = (int)(y[i][j] - 0.3 / 0.59 *
                c1[i][j] - 0.11 / 0.59 * c2[i][j]);
            if (ig > 255) ig = 255;
            if (ig < 0) ig = 0;
            ib = y[i][j] + c2[i][j];
            if (ib > 255) ib = 255;
            if (ib < 0) ib = 0;
            image_r[i][j] = (unsigned char)ir;
            image_g[i][j] = (unsigned char)ig;
            image_b[i][j] = (unsigned char)ib;
        }
    }
}

R-Y, B-Yと彩度、色相の変換を行う

#include <math.h>
#include "Params.h"

#define    PI            3.141592
#define    THRESHOLD    0.0            /* 彩度の有無を判断する閾値        */
#define    NONE        0.0            /* 彩度がない場合に代入する値    */

/*---- c_to_sh --- 色差信号から彩度,色相を計算する ----------------------------
    c1:        入力データ配列R−Y
    c2:        入力データ配列B−Y
    sat:    彩度のデータ配列
    hue:    色相のデータ配列
-----------------------------------------------------------------------------*/
void c_to_sh(int c1[Y_SIZE][X_SIZE], int c2[Y_SIZE][X_SIZE], 
    int sat[Y_SIZE][X_SIZE], int hue[Y_SIZE][X_SIZE])
{
    int        i, j;
    float    fhue, length;

    for (i = 0; i < Y_SIZE; i++) {
        for (j = 0; j < X_SIZE; j++) {
            length =(float)c1[i][j] * (float)c1[i][j]
                    +(float)c2[i][j] * (float)c2[i][j];
            sat[i][j] = (int)(sqrt((double)length));

            if ((sat[i][j]) > THRESHOLD){
                fhue = (float)(atan2((double)c1[i][j], (double)c2[i][j]) *
                        180.0 / PI);
                if (fhue < 0 ) fhue = fhue + (float)360.0;
                hue[i][j] = (int)fhue;
                }
            else hue[i][j] = (int)NONE; /* 彩度が閾値以下の時 */
        }
    }
}

/*--- sh_to_c --- 彩度,色相から色差信号を計算する -----------------------------
    c1:        入力データ配列R−Y
    c2:        入力データ配列B−Y
    sat:    彩度のデータ配列
    hue:    色相のデータ配列
-----------------------------------------------------------------------------*/
void sh_to_c(int sat[Y_SIZE][X_SIZE], int hue[Y_SIZE][X_SIZE], 
    int c1[Y_SIZE][X_SIZE], int c2[Y_SIZE][X_SIZE])
{
    int        i, j;
    float    rad;

    for (i = 0; i < Y_SIZE; i++) {
        for (j = 0; j < X_SIZE; j++) {
            rad = (float)(PI * hue[i][j] / 180.0);
            c1[i][j] = (int)(sat[i][j] * sin((double)rad));
            c2[i][j] = (int)(sat[i][j] * cos((double)rad));
        }
    }
}

彩度、色相データを画像化する

#include <math.h>
#include "Params.h"

/*--- sat_image --- 彩度データを濃淡画像化する --------------------------------
    sat:        彩度のデータ配列
    image_out:    出力画像配列
-----------------------------------------------------------------------------*/
int sat_image(int sat[Y_SIZE][X_SIZE], unsigned char image_out[Y_SIZE][X_SIZE])
{
    int i, j;
    int min, max;
    int isat;

    min = 255;
    max = 0;
    for (i = 0; i < Y_SIZE; i++){
        for (j = 0; j < X_SIZE; j++ ){
            if (sat[i][j] > max) max = sat[i][j];
            if (sat[i][j] < min) min = sat[i][j];
        }
    }
    if (min == max) return -1;
    for (i = 0; i < Y_SIZE; i++){
        for (j = 0; j < X_SIZE; j++ ){
            isat = 255 * (sat[i][j] - min) / (max - min);
            image_out[i][j] = (unsigned char)(isat);
        }
    }
    return 0;
}

/*--- hue_image --- 色相データを画像化する ------------------------------------
    sat:        彩度のデータ配列
    hue:        色相のデータ配列
    stdhue:        基準となる色相値
    image_out:    出力画像配列
-----------------------------------------------------------------------------*/
void hue_image(int sat[Y_SIZE][X_SIZE], int hue[Y_SIZE][X_SIZE], 
    float stdhue, unsigned char image_out[Y_SIZE][X_SIZE])
{
    int        i, j;
    int        ihue;
    float    delt;

    for (i = 0; i < Y_SIZE; i++){
        for (j = 0; j < X_SIZE; j++){
            if (sat[i][j] > 0){
                delt = (float)(fabs((double)hue[i][j] - (double)stdhue));
                if (delt > 180.0) delt = (float)360.0 - delt;
                ihue = (int)(255.0 - delt * 255.0 / 180.0);
                image_out[i][j] = (unsigned char)ihue;
            }
            else image_out[i][j]=0;
        }
    }
}

輝度、彩度、色相を変える

#include "Params.h"

/*--- tran_ysh --- 輝度,彩度,色相を変える -------------------------------------
    in_y:        入力データ配列   Y
    in_sat:        入力データ配列 SAT
    in_hue:        入力データ配列 HUE
    out_y:        出力データ配列   Y
    out_sat:    出力データ配列 SAT
    out_hue:    出力データ配列 HUE
    ym:            輝度の乗数
    sm:            彩度の乗数
    hd:            色相の増分
-----------------------------------------------------------------------------*/
void tran_ysh(int in_y[Y_SIZE][X_SIZE], int in_sat[Y_SIZE][X_SIZE], 
    int in_hue[Y_SIZE][X_SIZE], int out_y[Y_SIZE][X_SIZE], 
    int out_sat[Y_SIZE][X_SIZE], int out_hue[Y_SIZE][X_SIZE], 
    float ym, float sm, float hd)
{
    int i, j;

    for (i = 0; i < Y_SIZE; i++){
        for (j = 0; j < X_SIZE; j++){
            out_y[i][j]   = (int)(in_y[i][j] * ym);
            out_sat[i][j] = (int)(in_sat[i][j] * sm);
            out_hue[i][j] = (int)(in_hue[i][j] + hd);
            if(out_hue[i][j]>360) out_hue[i][j] = out_hue[i][j] - 360;
            if(out_hue[i][j]<  0) out_hue[i][j] = out_hue[i][j] + 360;
        }
    }
}