Qt QColor::hslHueF()徹底解説:HSL色相の取得と活用法

2025-05-27

QColor::hslHueF()は、QtのQColorクラスのメンバー関数で、色のHSL(Hue, Saturation, Lightness)モデルにおける「Hue(色相)」成分をfloat型で取得するために使用されます。

HSL モデルとは?

HSLは、色を表現するためのモデルの一つで、人間が色を認識する方法により近いと言われています。以下の3つの成分で構成されます。

  • Lightness (輝度/明度): 色の明るさを表します。輝度が高いほど色が明るく(白に近づく)、低いほど色が暗く(黒に近づく)なります。
  • Saturation (彩度): 色の鮮やかさを表します。彩度が高いほど色が鮮やかになり、低いほどくすんだ色(灰色に近づく)になります。
  • Hue (色相): 色の種類を表します。赤、オレンジ、黄、緑、青、紫といった色のスペクトルを円周上に配置し、その角度で表現します。通常、0度から359度の範囲で表現されますが、hslHueF()は浮動小数点数で、0.0から1.0の範囲で値を返します。
    • 赤は0.0 (または1.0)
    • 緑は約0.33
    • 青は約0.67 といった値になります。

この関数は、現在のQColorオブジェクトが持つ色をHSLモデルに変換し、その中のHue成分を0.0から1.0の範囲のfloat値として返します。


#include <QColor>
#include <QDebug>

int main() {
    QColor redColor(Qt::red); // 赤色
    float redHue = redColor.hslHueF();
    qDebug() << "赤色のHSL Hue (float):" << redHue; // おおよそ 0.0 を出力

    QColor greenColor(Qt::green); // 緑色
    float greenHue = greenColor.hslHueF();
    qDebug() << "緑色のHSL Hue (float):" << greenHue; // おおよそ 0.3333 を出力

    QColor blueColor(Qt::blue); // 青色
    float blueHue = blueColor.hslHueF();
    qDebug() << "青色のHSL Hue (float):" << blueHue; // おおよそ 0.6666 を出力

    QColor grayColor(Qt::gray); // 灰色
    float grayHue = grayColor.hslHueF();
    qDebug() << "灰色のHSL Hue (float):" << grayHue; // 彩度がないため、-1.0 を出力 (Qtのバージョンによる)
                                                  // または、不定な値になることがあります。
                                                  // 一般的に、無彩色(灰色など)のHueは意味を持ちません。
    return 0;
}

なぜ float バージョンがあるのか?

QColorには、色成分を整数(0-255や0-359)で取得・設定する関数と、浮動小数点数(0.0-1.0)で取得・設定する関数が両方用意されています。hslHueF()のようにFが末尾につくものは、浮動小数点数バージョンです。

これは、色の計算や、他のシステム(例えばOpenGLなど)で色を扱う際に、0.0から1.0の範囲で統一された浮動小数点数形式がより便利であるためです。



無彩色(灰色、白、黒など)のHue値

問題
無彩色(Saturationが0、つまり灰色、白、黒)の QColor オブジェクトに対して hslHueF() を呼び出すと、意図しない、あるいは不定な値が返されることがあります。

原因
HSLモデルにおいて、色相(Hue)は色の「種類」を表します。しかし、無彩色は「色」を持たないため、色相という概念が適用できません。数学的に、彩度が0の場合、Hueの計算式は不定になります。Qtの実装では、このような場合に hslHueF()-1.0 を返すことがあります(Qtのバージョンや内部実装に依存)。他の無意味な浮動小数点数が返されることもあります。

トラブルシューティング
hslHueF() の戻り値を使用する前に、色の彩度(Saturation)をチェックすることが重要です。

  • QColor::lightnessF()QColor::lightness() を使って、その色が白(lightnessF() が1.0)または黒(lightnessF() が0.0)であるかを判断することもできます。
  • QColor::hslSaturationF()0.0 または非常に小さい値である場合、hslHueF() の結果は意味を持ちません。

コード例

QColor color(Qt::gray); // 灰色
float hue = color.hslHueF();
float saturation = color.hslSaturationF();

if (saturation < 0.0001f) { // 非常に小さい値を許容範囲とする
    qDebug() << "この色は無彩色です。Hue値は意味を持ちません。";
} else {
    qDebug() << "Hue:" << hue;
}

RGBAからHSLへの変換誤差

問題
RGB値で定義された色をHSLに変換する際、浮動小数点数の精度によるごくわずかな誤差が生じることがあります。これは通常大きな問題にはなりませんが、厳密な比較を行う場合に考慮する必要があります。

原因
RGBからHSLへの変換は、複数の数学的な計算(除算など)を伴います。浮動小数点演算は本質的に丸め誤差を含むため、完全に正確な結果が得られない場合があります。

トラブルシューティング

  • 例: qFuzzyCompare(hue1, hue2)qAbs(hue1 - hue2) < epsilon
  • 厳密な等価比較(==)は避けるべきです。代わりに、許容誤差(イプシロン)範囲内での比較を行います。

HSL値の範囲の誤解

問題
hslHueF() は0.0から1.0の範囲の値を返しますが、これを度数法(0-359度)や、他のシステムで使われる異なる範囲(例:一部のグラフィックソフトウェアでは0-255)と混同することがあります。

原因
異なる色空間モデルやライブラリでは、Hueの表現範囲が異なります。QtのhslHueF()は、内部的な一貫性のために0.0-1.0の正規化された値を使用しています。

トラブルシューティング

  • 例:度数法に変換する場合 hue * 360.0f
  • 0.0-1.0の範囲であると明確に理解し、必要に応じて他の範囲に変換します。

問題
QColor オブジェクトが setHslF() などで明示的にHSL値で設定されていない場合でも、内部的にはRGB値が保持されています。hslHueF() を呼び出すと、そのRGB値からHSL値が計算されます。もし元のRGB値が非常に特殊な場合、期待通りのHSL値にならないことがあります。

原因
ほとんどの場合、この問題は発生しません。Qtの変換は非常に堅牢です。しかし、例えば非常に広い色域を持つディスプレイの色を扱う場合など、ごくまれにRGBからHSLへのマッピングが期待通りにならないことがあります。

  • もし特殊な色空間を扱う必要があれば、Qtのドキュメントや関連する色空間の理論を確認し、手動で変換ロジックを実装することも検討します。
  • 通常のケースでは、QColor のRGB-HSL変換は信頼できます。


基本的な Hue 値の取得

最も基本的な使い方です。特定の色の QColor オブジェクトから、そのHSL Hue(色相)成分を浮動小数点数で取得します。

#include <QColor>
#include <QDebug> // デバッグ出力用

int main() {
    // 1. 赤色
    QColor red(255, 0, 0); // RGBで赤を定義
    float redHue = red.hslHueF();
    qDebug() << "赤色のHSL Hue (0.0-1.0):" << redHue; // 出力例: 0

    // 2. 緑色
    QColor green(0, 255, 0); // RGBで緑を定義
    float greenHue = green.hslHueF();
    qDebug() << "緑色のHSL Hue (0.0-1.0):" << greenHue; // 出力例: 0.333333

    // 3. 青色
    QColor blue(0, 0, 255); // RGBで青を定義
    float blueHue = blue.hslHueF();
    qDebug() << "青色のHSL Hue (0.0-1.0):" << blueHue; // 出力例: 0.666667

    // 4. マゼンタ (赤と青の混合)
    QColor magenta(255, 0, 255);
    float magentaHue = magenta.hslHueF();
    qDebug() << "マゼンタのHSL Hue (0.0-1.0):" << magentaHue; // 出力例: 0.833333

    // 5. 任意のRGB色
    QColor customColor(100, 150, 200); // 任意のRGB値
    float customHue = customColor.hslHueF();
    qDebug() << "カスタム色のHSL Hue (0.0-1.0):" << customHue; // 出力例: 0.583333

    return 0;
}

解説
hslHueF() は、RGBで定義された色を内部的にHSLに変換し、そのHue成分を0.0から1.0の範囲で返します。Hueの「0.0」は赤、「0.333...」は緑、「0.666...」は青に対応します。

Hue値を用いた色の分類

取得したHue値を使って、色がどの系統に属するかを判断する例です。

#include <QColor>
#include <QDebug>

// Hue値から色の種類を判定する関数
QString getColorCategory(const QColor& color) {
    float hue = color.hslHueF();
    float saturation = color.hslSaturationF();

    // 無彩色の判定: 彩度が非常に低い場合は無彩色とみなす
    if (saturation < 0.0001f) {
        if (color.lightnessF() < 0.1f) return "黒";
        if (color.lightnessF() > 0.9f) return "白";
        return "灰色";
    }

    // Hue値に基づいて色を分類
    if (hue >= 0.0f && hue < (1.0f / 12.0f) || hue >= (11.0f / 12.0f) && hue <= 1.0f) {
        return "赤系";
    } else if (hue >= (1.0f / 12.0f) && hue < (3.0f / 12.0f)) {
        return "オレンジ系";
    } else if (hue >= (3.0f / 12.0f) && hue < (5.0f / 12.0f)) {
        return "黄系";
    } else if (hue >= (5.0f / 12.0f) && hue < (7.0f / 12.0f)) {
        return "緑系";
    } else if (hue >= (7.0f / 12.0f) && hue < (9.0f / 12.0f)) {
        return "青系";
    } else if (hue >= (9.0f / 12.0f) && hue < (11.0f / 12.0f)) {
        return "紫系";
    }
    return "不明";
}

int main() {
    qDebug() << "赤:" << getColorCategory(QColor(255, 0, 0));
    qDebug() << "オレンジ:" << getColorCategory(QColor(255, 165, 0));
    qDebug() << "黄:" << getColorCategory(QColor(255, 255, 0));
    qDebug() << "緑:" << getColorCategory(QColor(0, 128, 0));
    qDebug() << "青:" << getColorCategory(QColor(0, 0, 255));
    qDebug() << "紫:" << getColorCategory(QColor(128, 0, 128));
    qDebug() << "灰色:" << getColorCategory(QColor(128, 128, 128));
    qDebug() << "黒:" << getColorCategory(QColor(0, 0, 0));
    qDebug() << "白:" << getColorCategory(QColor(255, 255, 255));

    return 0;
}

解説
この例では、hslHueF()hslSaturationF() を組み合わせて使用しています。まず彩度が低い場合は無彩色(黒、白、灰色)と判断し、そうでない場合はHue値の範囲に基づいて色を分類しています。Hueの範囲を12分割し、それぞれのセグメントに名前を付けています。

色の調整:Hueを維持したままSaturationやLightnessを変更

hslHueF() はHueを取得する関数ですが、そのHue値を元に、他のHSL成分を調整して新しい色を作成する際に利用できます。

#include <QColor>
#include <QDebug>

int main() {
    QColor originalColor(100, 150, 200); // 元の色 (青みがかった灰色)

    // 元の色のHSL成分を取得
    float originalHue = originalColor.hslHueF();
    float originalSaturation = originalColor.hslSaturationF();
    float originalLightness = originalColor.lightnessF(); // HSLのLを取得

    qDebug() << "元の色: Hue=" << originalHue
             << ", Saturation=" << originalSaturation
             << ", Lightness=" << originalLightness;

    // 例1: Hueを維持し、彩度を上げる(より鮮やかに)
    QColor brighterSaturationColor;
    // setHslF() でHSL値を設定。HueとLightnessは元のまま、Saturationを上げる
    brighterSaturationColor.setHslF(originalHue, originalSaturation * 1.5f, originalLightness);
    qDebug() << "彩度を上げた色: Hue=" << brighterSaturationColor.hslHueF()
             << ", Saturation=" << brighterSaturationColor.hslSaturationF()
             << ", Lightness=" << brighterSaturationColor.lightnessF();
    qDebug() << "  -> RGB:" << brighterSaturationColor.red() << brighterSaturationColor.green() << brighterSaturationColor.blue();


    // 例2: Hueを維持し、明度を下げる(より暗く)
    QColor darkerLightnessColor;
    // HueとSaturationは元のまま、Lightnessを下げる
    darkerLightnessColor.setHslF(originalHue, originalSaturation, originalLightness * 0.5f);
    qDebug() << "明度を下げた色: Hue=" << darkerLightnessColor.hslHueF()
             << ", Saturation=" << darkerLightnessColor.hslSaturationF()
             << ", Lightness=" << darkerLightnessColor.lightnessF();
    qDebug() << "  -> RGB:" << darkerLightnessColor.red() << darkerLightnessColor.green() << darkerLightnessColor.blue();

    // 例3: Hueのみを変更(色相環を移動)
    QColor shiftedHueColor;
    // Hueを0.25だけずらす (Hueは0.0-1.0でループする)
    float newHue = fmod(originalHue + 0.25f, 1.0f); // 1.0を超えたら0.0に戻る
    shiftedHueColor.setHslF(newHue, originalSaturation, originalLightness);
    qDebug() << "Hueをずらした色: Hue=" << shiftedHueColor.hslHueF()
             << ", Saturation=" << shiftedHueColor.hslSaturationF()
             << ", Lightness=" << shiftedHueColor.lightnessF();
    qDebug() << "  -> RGB:" << shiftedHueColor.red() << shiftedHueColor.green() << shiftedHueColor.blue();

    return 0;
}

解説
この例では、ある色の hslHueF() で得られたHue値を保持したまま、彩度(hslSaturationF())や明度(lightnessF())を変更して新しい色を生成しています。setHslF() は、HSL値を直接設定するための非常に便利な関数です。色相環を移動させる場合、Hueは0.0から1.0でループするため、fmod 関数(浮動小数点数の剰余)を使って適切に範囲内に収める処理が重要です。

Hueは色の種類を表すため、例えば色相環を一周するようなグラデーションやアニメーションを作成する際に非常に有効です。

// Qt Widgetsアプリケーションの例 (QPaintEvent内で描画)
#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QTimer> // アニメーション用

class ColorCyclingWidget : public QWidget {
    Q_OBJECT // シグナル/スロットを使用するために必要

public:
    ColorCyclingWidget(QWidget* parent = nullptr) : QWidget(parent), currentHue(0.0f) {
        setWindowTitle("HSL Hue Cycling Example");
        resize(400, 200);

        // 60fps でアニメーションを更新
        QTimer* timer = new QTimer(this);
        connect(timer, &QTimer::timeout, this, &ColorCyclingWidget::updateHue);
        timer->start(1000 / 60); // 1秒間に60回更新
    }

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

        // 現在のHue値を使って色を作成
        // 彩度と明度は固定 (0.8と0.5)
        QColor currentColor;
        currentColor.setHslF(currentHue, 0.8f, 0.5f);

        // ウィジェット全体をその色で塗りつぶす
        painter.fillRect(rect(), currentColor);

        // 現在のHue値を表示
        painter.setPen(Qt::white); // テキスト色を白に
        painter.drawText(rect(), Qt::AlignCenter, QString("Current Hue: %1").arg(currentHue, 0, 'f', 3));
    }

private slots:
    void updateHue() {
        // Hueを少しずつ増加させ、1.0を超えたら0.0に戻す
        currentHue += 0.005f; // 変化の速度
        if (currentHue > 1.0f) {
            currentHue -= 1.0f; // 1.0を超えたらラップアラウンド
        }
        update(); // paintEvent() を再描画
    }

private:
    float currentHue; // 現在の色相値 (0.0-1.0)
};

#include "main.moc" // mocファイルを含める

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    ColorCyclingWidget w;
    w.show();
    return a.exec();
}

解説
この例では、QTimer を使って定期的に updateHue() スロットを呼び出し、currentHue の値を少しずつ変化させています。setHslF() を使って Hue を変化させることで、彩度と明度を保ったまま色相環を滑らかに一周するアニメーションを実現しています。paintEvent() では、そのHue値を持つ色でウィジェットを塗りつぶしています。



QColor::hueF() / QColor::hue() (HSV色空間のHue)

  • int QColor::hue() const: HSV Hueを整数(0-359)で返します。
  • float QColor::hueF() const: HSV Hueを浮動小数点数(0.0-1.0)で返します。

hslHueF() との違い

  • 通常、hslHueF()hueF() は同じ色に対してほぼ同じHue値を返しますが、計算方法のわずかな違いや浮動小数点誤差により、厳密には異なる結果になる可能性があります。特に、彩度が低い色や黒に近い色では、Hueの値の解釈に違いが出ることがあります。
  • HSV: 特定の色の「最も鮮やかな状態」から、黒(Value=0)または白(Saturation=0)に向かって変化するモデル。色の明るさ(Value)が最大(Value=1)で最も鮮やかになります。
  • HSL: 人間が色を認識する明るさ(Lightness)に近い。中間的な明るさ(Lightness=0.5)で最も鮮やかになります。
  • HSLとHSVはどちらも「Hue」という概念を持ちますが、彩度(Saturation)と明度/輝度(Value/Lightness)の定義が異なります。

利用シーン

  • 特定のグラフィックツールや標準がHSVをベースにしている場合。
  • もしHSVモデルでのHueが必要な場合、こちらを使用します。

コード例

#include <QColor>
#include <QDebug>

int main() {
    QColor color(100, 150, 200); // 任意のRGB色

    float hslHue = color.hslHueF();
    float hsvHue = color.hueF(); // HSVのHueを取得

    qDebug() << "HSL Hue (0.0-1.0):" << hslHue;
    qDebug() << "HSV Hue (0.0-1.0):" << hsvHue;

    // ほとんどの場合、同じか非常に近い値になる
    // 出力例:
    // HSL Hue (0.0-1.0): 0.583333
    // HSV Hue (0.0-1.0): 0.583333

    QColor black(0, 0, 0);
    qDebug() << "黒のHSL Hue:" << black.hslHueF(); // -1.0 または不定
    qDebug() << "黒のHSV Hue:" << black.hueF();   // -1.0 または不定

    return 0;
}

QColor::getHslF() を使って複数のHSL成分を一度に取得

void QColor::getHslF(float *h, float *s, float *l, float *a = nullptr) const

hslHueF() との違い

  • 関数呼び出しのオーバーヘッドをわずかに減らせる可能性があります(ただし、通常は無視できるレベル)。
  • Hue単独でなく、他のHSL成分も同時に必要な場合に便利です。

利用シーン

  • HSLモデルで色を完全に分解して分析する場合。
  • 色の彩度や明度も同時に調べて、Hue値が有効かどうかを判断する場合。

コード例

#include <QColor>
#include <QDebug>

int main() {
    QColor color(100, 150, 200);

    float h, s, l, a;
    color.getHslF(&h, &s, &l, &a);

    qDebug() << "取得したHSLFA:";
    qDebug() << "  Hue:" << h;
    qDebug() << "  Saturation:" << s;
    qDebug() << "  Lightness:" << l;
    qDebug() << "  Alpha:" << a;

    // 無彩色のチェックも可能
    if (s < 0.0001f) {
        qDebug() << "この色は無彩色なので、Hueは意味を持ちません。";
    }

    return 0;
}

QColor::toHsl() / QColor::toHsv() で新しい QColor オブジェクトを作成

  • QColor QColor::toHsv() const: 現在の QColor オブジェクトのHSV表現を持つ新しい QColor オブジェクトを返します。
  • QColor QColor::toHsl() const: 現在の QColor オブジェクトのHSL表現を持つ新しい QColor オブジェクトを返します。

これらの関数で返される QColor オブジェクトは、内部的にHSL/HSVとして設定されているため、それぞれの色空間の成分を直接(例えば hslHueF()hueF() で)取得できます。

hslHueF() との違い

  • しかし、その変換された色空間の全ての成分を後で操作したい場合や、明示的に色空間を表現したい場合に有効です。
  • 変換された QColor オブジェクトに対して再度 hslHueF() を呼び出すことになるため、直接 hslHueF() を呼び出すよりも効率が悪い場合があります。
  • これはHueだけを直接取得するのではなく、色全体を特定のモデルに「変換したコピー」を得る方法です。

利用シーン

  • HSL/HSVベースで新しい色を生成するが、元のRGB色から変換を開始したい場合。
  • 色の内部表現をRGBからHSL/HSVに切り替えたい場合。

コード例

#include <QColor>
#include <QDebug>

int main() {
    QColor originalColor(100, 150, 200);

    // HSL表現の新しいQColorオブジェクトを作成
    QColor hslColor = originalColor.toHsl();
    qDebug() << "HSLから得たHue (toHsl経由):" << hslColor.hslHueF();
    qDebug() << "HSLから得たSaturation (toHsl経由):" << hslColor.hslSaturationF();

    // HSV表現の新しいQColorオブジェクトを作成
    QColor hsvColor = originalColor.toHsv();
    qDebug() << "HSVから得たHue (toHsv経由):" << hsvColor.hueF();
    qDebug() << "HSVから得たValue (toHsv経由):" << hsvColor.valueF(); // HSVのV

    return 0;
}

hslHueF() との違い

  • 浮動小数点演算やエッジケース(無彩色など)の処理を全て自分で管理する必要があり、エラーの可能性が高まります。
  • Qtの変換ロジックは十分にテストされており、ほとんどのユースケースで正確です。
  • 車輪の再発明になる可能性が高い。

利用シーン

  • パフォーマンスが非常にクリティカルで、Qtの内部実装以上の最適化が必要な場合(ごくまれ)。
  • 特定の研究目的や、既存のフレームワークに厳密に準拠する必要がある場合。
// RGBからHSLへの手動変換アルゴリズムの概略
// HSL計算は複雑なので、ここでは擬似コード/説明に留めます。
// (QColorの内部実装はより堅牢なものです)
/*
float calculateHue(int r, int g, int b) {
    float R = r / 255.0f;
    float G = g / 255.0f;
    float B = b / 255.0f;

    float Cmax = qMax(qMax(R, G), B);
    float Cmin = qMin(qMin(R, G), B);
    float delta = Cmax - Cmin;

    float H = 0.0f;

    if (delta == 0) {
        H = -1.0f; // 無彩色の場合
    } else if (Cmax == R) {
        H = fmod(((G - B) / delta), 6.0f);
    } else if (Cmax == G) {
        H = ((B - R) / delta) + 2.0f;
    } else { // Cmax == B
        H = ((R - G) / delta) + 4.0f;
    }
    H = H / 6.0f; // 0-1の範囲に正規化

    // HSLのLightness計算 (ここではHueと無関係だが、HSLでは必要)
    // float L = (Cmax + Cmin) / 2.0f;

    return H;
}
*/