第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;
        }
    }
}