QtのQColor::hsvHue()徹底解説:色相の取得からトラブルシューティングまで

2025-05-27

QColor::hsvHue() は、Qtの QColor クラスが提供するメンバー関数で、現在の QColor オブジェクトが表現している色のHSV(Hue-Saturation-Value)モデルにおける「色相(Hue)」の成分を整数値で返します。

HSVモデルとは?

色を表現する方法はいくつかありますが、RGB(Red-Green-Blue)モデルと並んでよく使われるのがHSVモデルです。HSVモデルは、人間の色の認識方法により近いとされています。

  • V (Value - 明度): 色の明るさを表します。0は黒、255は最も明るい色(その色相と彩度における最大輝度)を示します。

  • S (Saturation - 彩度): 色の鮮やかさを表します。0に近いほど色が薄く(灰色に近く)、255に近いほど色が鮮やかになります。

  • H (Hue - 色相): 色の種類を表します。赤、緑、青といった、いわゆる「色合い」を指します。通常、色相環上の角度で表現され、0度から359度の範囲の整数値を取ります。

    • 赤は0度、緑は120度、青は240度です。
    • 灰色(無彩色)の場合、色相は意味を持たないため、Qtでは通常-1を返します。

QColor::hsvHue() の戻り値

QColor::hsvHue() は、現在の QColor オブジェクトの色相成分を int 型で返します。

  • 無彩色(灰色、黒、白など)の場合: -1を返します。これは、無彩色には特定の色相がないことを示します。
  • 有彩色の場合: 0から359の範囲の整数値を返します。これは色相環上の角度に相当します。

使用例

ある QColor オブジェクト myColor があり、その色相成分を取得したい場合、次のように記述します。

#include <QColor>
#include <QDebug> // qInfo() を使用するために必要

int main() {
    QColor myColor(Qt::red); // 赤色を作成
    int hue = myColor.hsvHue();
    qInfo() << "赤色の色相 (Hue):" << hue; // 出力: 0

    QColor greenColor(Qt::green); // 緑色を作成
    hue = greenColor.hsvHue();
    qInfo() << "緑色の色相 (Hue):" << hue; // 出力: 120

    QColor grayColor(Qt::gray); // 灰色を作成
    hue = grayColor.hsvHue();
    qInfo() << "灰色の色相 (Hue):" << hue; // 出力: -1

    QColor customColor;
    customColor.setHsv(300, 200, 150); // 色相300、彩度200、明度150のカスタムカラー
    hue = customColor.hsvHue();
    qInfo() << "カスタムカラーの色相 (Hue):" << hue; // 出力: 300

    return 0;
}


QColor::hsvHue() は比較的単純な関数ですが、色そのものの性質や QColor オブジェクトの初期化方法によって、意図しない結果を招くことがあります。

戻り値が常に -1 になる

問題の現象
期待する色相(0-359)ではなく、常に -1 が返される。

原因
QColor::hsvHue()-1 を返すのは、その色が**無彩色(achromatic color)**である場合です。無彩色とは、灰色、黒、白など、色相を持たない色のことです。

考えられるシナリオ

  • RGB値が全て同じ、または彩度 (Saturation) が非常に低い色。 例えば、QColor(128, 128, 128) は灰色なので、色相は -1 になります。また、彩度が0に近い色(ほとんど白や黒に近い色)も、実質的に無彩色として扱われ、色相が意味を持たなくなるため -1 を返すことがあります。
  • QColor オブジェクトが黒、白、または灰色で初期化されている。
    QColor blackColor(Qt::black); // H = -1
    QColor whiteColor(Qt::white); // H = -1
    QColor grayColor(Qt::gray);   // H = -1
    qDebug() << blackColor.hsvHue(); // -1
    qDebug() << whiteColor.hsvHue(); // -1
    qDebug() << grayColor.hsvHue();  // -1
    

トラブルシューティング

  1. QColor オブジェクトがどのような色を表しているか確認する。 isValid()red(), green(), blue() などの関数を使って、色のRGB値を確認してください。
  2. 彩度を確認する。 hsvSaturation() の戻り値が0に近い場合、その色は無彩色に非常に近いため、hsvHue() が -1 を返すのは正しい挙動です。
  3. 無彩色を意図しない場合は、色の初期化方法を見直す。 RGB値が全て同じになっていないか、あるいは意図せず彩度が低い色になっていないか確認してください。

意図しない色相値が返される

問題の現象
期待する色相とは異なる値が返される。例えば、赤色のつもりなのに10が返される、など。

原因
QColor オブジェクトが初期化されたRGB値が、期待するHSV値に正確に変換されていない可能性があります。色の変換にはわずかな丸め誤差が生じることがあります。また、人間の色の認識とコンピュータの色空間の変換には差異があるため、直感と異なる値になることもあります。

考えられるシナリオ

  • HSVの仕様への理解不足。 HSVの色相環は、赤が0度、緑が120度、青が240度です。例えば、紫系の色を期待している場合、赤(0度)と青(240度)の中間あたり(例:300度前後)の値になることを理解している必要があります。
  • 初期化したRGB値が、期待する純粋な色ではない。 例えば、QColor(255, 0, 0) は純粋な赤(H=0)ですが、QColor(255, 10, 5) のような、わずかに他の成分が混ざった赤では、色相が0からわずかにずれることがあります。
  • RGBからHSVへの変換誤差。 QColor は内部でRGB値を保持しており、hsvHue() が呼ばれたときにRGBからHSVへ変換を行います。この変換プロセスで浮動小数点演算が関与するため、ごくわずかな誤差が生じ、それが色相値に影響を与える可能性があります。

トラブルシューティング

  1. 元のRGB値を確認する。 QColor::red(), QColor::green(), QColor::blue() を使って、hsvHue() を呼び出す前に QColor オブジェクトが保持しているRGB値を確認します。
  2. QColor::setHsv() を使用して色を直接HSVで設定する。 もし特定の色相を厳密に制御したいのであれば、RGBから色を作成するのではなく、直接 QColor::setHsv(hue, saturation, value) を使って色を設定することを検討してください。これにより、意図した色相が確実に設定され、hsvHue() が期待通りの値を返すことを保証できます。
    QColor preciseRed;
    preciseRed.setHsv(0, 255, 255); // 純粋な赤 (Hue=0)
    qDebug() << preciseRed.hsvHue(); // 0
    
    QColor customMagenta;
    customMagenta.setHsv(300, 200, 255); // 色相300 (Hue=300)
    qDebug() << customMagenta.hsvHue(); // 300
    
  3. 浮動小数点版の関数を試す。 hsvHueF()qreal (double) 型の戻り値を返すため、より高い精度で色相を取得できます。必要に応じて、こちらを利用して誤差を許容範囲に収めるかどうかを判断してください。

QColor オブジェクトが無効 (Invalid) な場合

問題の現象
hsvHue() を呼び出すと予期しない結果になったり、プログラムがクラッシュしたりする(稀)。

原因
QColor オブジェクトが「無効 (Invalid)」な状態である可能性があります。無効な色とは、例えば範囲外のRGB値で初期化された場合などです。Qtのドキュメントによると、無効な色の使用結果は未定義です。

  1. isValid() で色を検証する。 hsvHue() を呼び出す前に QColor::isValid() を使って、色が有効な状態であるかを確認する習慣をつけましょう。
    QColor myColor(500, 0, 0); // 無効なRGB値
    if (myColor.isValid()) {
        qDebug() << myColor.hsvHue();
    } else {
        qDebug() << "QColorオブジェクトが無効です。";
    }
    
    通常、QColor のコンストラクタや setRgb() などは、不正な引数が渡されても例外を発生させず、単にオブジェクトを無効な状態にするだけなので、この確認が重要です。


QColor::hsvHue() は、QColor オブジェクトの色相成分(Hue)を取得するために使われます。これは、色の分析、色の調整、色のフィルタリングなど、さまざまな場面で役立ちます。

以下にいくつかの具体的なプログラミング例を示します。

例1: さまざまな色の色相を取得する

この例では、定義済みの色とカスタムRGB色の色相を取得し、その結果を表示します。

#include <QCoreApplication>
#include <QColor>
#include <QDebug> // qInfo() を使用するために必要

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    // 1. 定義済みの色の色相
    QColor redColor(Qt::red);
    QColor greenColor(Qt::green);
    QColor blueColor(Qt::blue);
    QColor yellowColor(Qt::yellow);
    QColor cyanColor(Qt::cyan);
    QColor magentaColor(Qt::magenta);

    qInfo() << "--- 定義済みの色の色相 ---";
    qInfo() << "赤 (Red) の色相:" << redColor.hsvHue();     // 0
    qInfo() << "緑 (Green) の色相:" << greenColor.hsvHue(); // 120
    qInfo() << "青 (Blue) の色相:" << blueColor.hsvHue();    // 240
    qInfo() << "黄 (Yellow) の色相:" << yellowColor.hsvHue();// 60
    qInfo() << "シアン (Cyan) の色相:" << cyanColor.hsvHue(); // 180
    qInfo() << "マゼンタ (Magenta) の色相:" << magentaColor.hsvHue(); // 300

    // 2. 無彩色の色相
    QColor blackColor(Qt::black);
    QColor whiteColor(Qt::white);
    QColor grayColor(Qt::gray);

    qInfo() << "\n--- 無彩色の色相 ---";
    qInfo() << "黒 (Black) の色相:" << blackColor.hsvHue(); // -1
    qInfo() << "白 (White) の色相:" << whiteColor.hsvHue(); // -1
    qInfo() << "灰色 (Gray) の色相:" << grayColor.hsvHue();  // -1

    // 3. カスタムRGB色の色相
    QColor customColor1(255, 128, 0); // オレンジ
    QColor customColor2(0, 200, 50);  // 明るい緑

    qInfo() << "\n--- カスタムRGB色の色相 ---";
    qInfo() << "カスタムカラー1 (オレンジ) の色相:" << customColor1.hsvHue(); // 約30
    qInfo() << "カスタムカラー2 (明るい緑) の色相:" << customColor2.hsvHue(); // 約140

    return a.exec();
}

解説
この例では、Qt::GlobalColor 列挙型を使って定義済みの色を生成し、その色相を hsvHue() で取得しています。また、RGB値から作成したカスタムカラーの色相も取得しています。無彩色(黒、白、灰色)の場合、hsvHue()-1 を返すことに注意してください。

例2: 色相に基づいて色を分類する

この例では、複数の色を定義し、それぞれをその色相(赤系、緑系、青系など)に基づいて分類します。

#include <QCoreApplication>
#include <QColor>
#include <QDebug>
#include <QList>

// 色相に基づいて色を分類する関数
QString classifyColorByHue(const QColor& color) {
    int hue = color.hsvHue();

    if (hue == -1) {
        return "無彩色 (Achromatic)";
    } else if ((hue >= 345 && hue <= 359) || (hue >= 0 && hue < 15)) {
        return "赤系 (Red-ish)";
    } else if (hue >= 15 && hue < 45) {
        return "オレンジ系 (Orange-ish)";
    } else if (hue >= 45 && hue < 75) {
        return "黄系 (Yellow-ish)";
    } else if (hue >= 75 && hue < 150) {
        return "緑系 (Green-ish)";
    } else if (hue >= 150 && hue < 210) {
        return "シアン系 (Cyan-ish)";
    } else if (hue >= 210 && hue < 270) {
        return "青系 (Blue-ish)";
    } else if (hue >= 270 && hue < 345) {
        return "マゼンタ系 (Magenta-ish)";
    } else {
        return "その他 (Other)";
    }
}

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    QList<QColor> colors;
    colors.append(QColor(255, 0, 0));       // 純粋な赤
    colors.append(QColor(255, 100, 0));     // オレンジがかった赤
    colors.append(QColor(0, 255, 0));       // 純粋な緑
    colors.append(QColor(100, 255, 0));     // 黄緑
    colors.append(QColor(0, 0, 255));       // 純粋な青
    colors.append(QColor(150, 0, 255));     // 紫
    colors.append(QColor(128, 128, 128));   // 灰色
    colors.append(QColor(255, 255, 255));   // 白

    qInfo() << "--- 色相による色分類 ---";
    for (const QColor& color : colors) {
        qInfo() << QString("RGB(%1, %2, %3) -> Hue: %4 -> 分類: %5")
                       .arg(color.red()).arg(color.green()).arg(color.blue())
                       .arg(color.hsvHue())
                       .arg(classifyColorByHue(color));
    }

    return a.exec();
}

解説
classifyColorByHue 関数は、QColor::hsvHue() の戻り値に基づいて色を大まかなカテゴリに分類します。赤は色相環の0度付近にあり、色相環は359度で一周するため、赤系の色は0度周辺と350度周辺の両方でチェックしています。これにより、色相によるフィルタリングやグループ化のロジックを実装できます。

例3: QPainterで描画する色を色相に基づいて動的に変更する

この例はGUIアプリケーションの一部となるため、基本的な QWidget を使用します。

#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QTimer>
#include <QColor>

// 色相を時間とともに変化させるカスタムウィジェット
class HueChangingWidget : public QWidget {
    Q_OBJECT // シグナル/スロットを使用するために必要

public:
    HueChangingWidget(QWidget *parent = nullptr) : QWidget(parent) {
        currentHue = 0;
        // 1秒ごとに色相を変化させるタイマーを設定
        QTimer *timer = new QTimer(this);
        connect(timer, &QTimer::timeout, this, &HueChangingWidget::changeHue);
        timer->start(100); // 100ミリ秒ごとに更新
        setWindowTitle("QColor::hsvHue() による色相変化");
    }

protected:
    void paintEvent(QPaintEvent *event) override {
        Q_UNUSED(event);
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);

        // 現在の色相に基づいて色を作成
        // saturation (彩度) と value (明度) は最大に設定
        QColor dynamicColor;
        dynamicColor.setHsv(currentHue, 255, 255);

        // 色相値を取得し表示
        painter.setPen(Qt::NoPen);
        painter.setBrush(dynamicColor);
        painter.drawRect(rect()); // ウィジェット全体をその色で塗りつぶす

        painter.setPen(Qt::black);
        painter.drawText(20, 30, QString("現在の色相 (Hue): %1").arg(dynamicColor.hsvHue()));
    }

private slots:
    void changeHue() {
        currentHue = (currentHue + 5) % 360; // 色相を5度ずつ増加させ、360で一周させる
        update(); // paintEvent を再描画
    }

private:
    int currentHue;
};

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);

    HueChangingWidget widget;
    widget.resize(400, 300);
    widget.show();

    return a.exec();
}

#include "main.moc" // MOC (Meta-Object Compiler) ファイルをインクルード

解説
この例では、QTimer を使って定期的に currentHue の値を更新し、その値を使って QColor::setHsv() で新しい色を生成しています。そして、生成された QColor オブジェクトの hsvHue() を呼び出して、現在の色相値を画面に表示しています。これにより、色相が動的に変化する様子を確認できます。



int QColor::hsvHue() の代替手段

qreal QColor::hsvHueF() を使用する(浮動小数点精度)

hsvHue() は色相を0〜359の整数で返しますが、QColor::hsvHueF() は**浮動小数点数(qreal、通常は double)**で色相を返します。これは、より高い精度で色相を扱いたい場合に非常に有用です。

  • 欠点:
    • 整数値での扱いが必要な場合、手動で丸める必要がある。
  • 利点:
    • 色の連続的な変化をより正確に表現できる。
    • 計算における丸め誤差を最小限に抑えることができる。

使用例:

#include <QCoreApplication>
#include <QColor>
#include <QDebug>

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    QColor myColor(255, 127, 0); // オレンジに近い色
    qreal hueFloat = myColor.hsvHueF();
    int hueInt = myColor.hsvHue();

    qInfo() << "色相 (浮動小数点):" << hueFloat; // 例: 29.999...
    qInfo() << "色相 (整数):" << hueInt;       // 例: 30

    // 非常に微妙な色の違いでも色相を区別できる
    QColor colorA(255, 0, 1); // ほぼ純粋な赤
    QColor colorB(255, 1, 0); // わずかに緑がかった赤

    qInfo() << "Color A の色相 (浮動小数点):" << colorA.hsvHueF();
    qInfo() << "Color B の色相 (浮動小数点):" << colorB.hsvHueF();

    return a.exec();
}

getHsv() または getHsvF() を使用してHSV成分を一括で取得する

QColor クラスは、色相だけでなく彩度(Saturation)と明度(Value)も同時に取得できるオーバーロードされた関数を提供しています。

  • void QColor::getHsvF(qreal *h, qreal *s, qreal *v, qreal *a = nullptr) const
  • void QColor::getHsv(int *h, int *s, int *v, int *a = nullptr) const

これらの関数は、引数としてポインタを取り、各HSV成分の値をそのポインタが指す場所に格納します。

  • 欠点:
    • ポインタを扱う必要があるため、少し冗長に感じるかもしれない。
  • 利点:
    • 色相、彩度、明度を一度の呼び出しで取得できるため、コードが簡潔になる場合がある。
    • 特に、色相だけでなく彩度や明度も考慮する必要がある場合に便利。

使用例:

#include <QCoreApplication>
#include <QColor>
#include <QDebug>

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    QColor myColor(100, 150, 200); // 青みがかった灰色

    int h_int, s_int, v_int;
    myColor.getHsv(&h_int, &s_int, &v_int);
    qInfo() << "整数値HSV: Hue =" << h_int << ", Saturation =" << s_int << ", Value =" << v_int;

    qreal h_float, s_float, v_float;
    myColor.getHsvF(&h_float, &s_float, &v_float);
    qInfo() << "浮動小数点HSV: Hue =" << h_float << ", Saturation =" << s_float << ", Value =" << v_float;

    return a.exec();
}

hslHue() または hslHueF() を使用する(HSLモデル)

もしHSVモデルではなく、HSL(Hue-Saturation-Lightness)モデルの色相が必要な場合は、QColor::hslHue() または QColor::hslHueF() を使用します。HSVとHSLは色相成分の定義が似ていますが、彩度と明度(Lightness)の解釈が異なります。HSLのLightnessは色の明るさをより直感的に表現し、白と黒の中間点が50%になるように設計されています。

  • 考慮点:
    • HSVとHSLのどちらのモデルがアプリケーションの要件に適しているかを理解する必要がある。
  • 利点:
    • Lightnessの概念がUIデザインなどでより自然な場合がある。

使用例:

#include <QCoreApplication>
#include <QColor>
#include <QDebug>

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    QColor myColor(100, 150, 200); // 青みがかった灰色

    qInfo() << "HSV Hue:" << myColor.hsvHue();
    qInfo() << "HSL Hue:" << myColor.hslHue(); // HSV Hueと近い値になることが多いが、全く同じとは限らない

    QColor pureRed;
    pureRed.setRgb(255, 0, 0); // 純粋な赤
    qInfo() << "純粋な赤 (HSV Hue):" << pureRed.hsvHue(); // 0
    qInfo() << "純粋な赤 (HSL Hue):" << pureRed.hslHue(); // 0

    QColor middleGray;
    middleGray.setRgb(128, 128, 128); // 中間的な灰色
    qInfo() << "中間的な灰色 (HSV Hue):" << middleGray.hsvHue(); // -1
    qInfo() << "中間的な灰色 (HSL Hue):" << middleGray.hslHue(); // -1 (無彩色は同様に-1)

    return a.exec();
}

RGB値から手動でHSV変換を実装する(稀なケース)

非常に特殊な要件があり、Qtの組み込み関数では対応できない場合(例えば、標準とは異なるHSV変換アルゴリズムを使用したい場合など)、RGB値(QColor::red(), green(), blue())を取得し、自分でHSV変換のロジックを実装することも可能です。

  • 欠点:
    • 車輪の再発明であり、Qtのテスト済みの実装を利用しないため、バグのリスクが高まる。
    • パフォーマンスがQtの最適化された内部実装に劣る可能性がある。
    • 一般的には推奨されない。
  • 利点:
    • 変換ロジックを完全に制御できる。

HSV変換の一般的なアルゴリズム:

  1. RGB値を0.0〜1.0の範囲に正規化する。
  2. R, G, B の最大値 (Max) と最小値 (Min) を見つける。
  3. 差 (Delta) = Max - Min を計算する。
  4. 色相 (Hue) の計算:
    • Delta = 0 の場合(無彩色)、Hue = 0 (または未定義)。
    • R = Max の場合、Hue = 60 * (((G - B) / Delta) mod 6)。
    • G = Max の場合、Hue = 60 * ((B - R) / Delta + 2)。
    • B = Max の場合、Hue = 60 * ((R - G) / Delta + 4)。
    • 結果を360で正規化する(負の値の場合は360を加える)。
  5. 彩度 (Saturation) の計算:
    • Max = 0 の場合、Saturation = 0。
    • それ以外の場合、Saturation = Delta / Max。
  6. 明度 (Value) の計算:
    • Value = Max。

注意: Qtの hsvHue() は、無彩色の場合は -1 を返します。手動で実装する場合は、この挙動も考慮する必要があります。

ほとんどの場合、以下のいずれかの方法で十分です。

  • HSLモデルの色相が必要な場合: QColor::hslHue() または QColor::hslHueF()
  • 色相と同時に彩度や明度も取得したい場合: QColor::getHsv() または QColor::getHsvF()
  • 精度が必要な場合: QColor::hsvHueF()