อัลกอริทึมในการแปลง RGB เป็น HSV และ HSV เป็น RGB ในช่วง 0-255 สำหรับทั้งสองอย่าง


93

ฉันกำลังมองหาตัวแปลงพื้นที่สีจาก RGB เป็น HSV โดยเฉพาะสำหรับช่วง 0 ถึง 255 สำหรับช่องว่างสีทั้งสอง

คำตอบ:


138

ฉันใช้สิ่งเหล่านี้มานานแล้วไม่รู้ว่ามันมาจากไหน ณ จุดนี้ ... โปรดทราบว่าอินพุตและเอาต์พุตยกเว้นมุมเป็นองศาอยู่ในช่วง 0 ถึง 1.0

หมายเหตุ: รหัสนี้ไม่มีการตรวจสอบความสมบูรณ์ของอินพุต โปรดดำเนินการด้วยความระมัดระวัง!

typedef struct {
    double r;       // a fraction between 0 and 1
    double g;       // a fraction between 0 and 1
    double b;       // a fraction between 0 and 1
} rgb;

typedef struct {
    double h;       // angle in degrees
    double s;       // a fraction between 0 and 1
    double v;       // a fraction between 0 and 1
} hsv;

static hsv   rgb2hsv(rgb in);
static rgb   hsv2rgb(hsv in);

hsv rgb2hsv(rgb in)
{
    hsv         out;
    double      min, max, delta;

    min = in.r < in.g ? in.r : in.g;
    min = min  < in.b ? min  : in.b;

    max = in.r > in.g ? in.r : in.g;
    max = max  > in.b ? max  : in.b;

    out.v = max;                                // v
    delta = max - min;
    if (delta < 0.00001)
    {
        out.s = 0;
        out.h = 0; // undefined, maybe nan?
        return out;
    }
    if( max > 0.0 ) { // NOTE: if Max is == 0, this divide would cause a crash
        out.s = (delta / max);                  // s
    } else {
        // if max is 0, then r = g = b = 0              
        // s = 0, h is undefined
        out.s = 0.0;
        out.h = NAN;                            // its now undefined
        return out;
    }
    if( in.r >= max )                           // > is bogus, just keeps compilor happy
        out.h = ( in.g - in.b ) / delta;        // between yellow & magenta
    else
    if( in.g >= max )
        out.h = 2.0 + ( in.b - in.r ) / delta;  // between cyan & yellow
    else
        out.h = 4.0 + ( in.r - in.g ) / delta;  // between magenta & cyan

    out.h *= 60.0;                              // degrees

    if( out.h < 0.0 )
        out.h += 360.0;

    return out;
}


rgb hsv2rgb(hsv in)
{
    double      hh, p, q, t, ff;
    long        i;
    rgb         out;

    if(in.s <= 0.0) {       // < is bogus, just shuts up warnings
        out.r = in.v;
        out.g = in.v;
        out.b = in.v;
        return out;
    }
    hh = in.h;
    if(hh >= 360.0) hh = 0.0;
    hh /= 60.0;
    i = (long)hh;
    ff = hh - i;
    p = in.v * (1.0 - in.s);
    q = in.v * (1.0 - (in.s * ff));
    t = in.v * (1.0 - (in.s * (1.0 - ff)));

    switch(i) {
    case 0:
        out.r = in.v;
        out.g = t;
        out.b = p;
        break;
    case 1:
        out.r = q;
        out.g = in.v;
        out.b = p;
        break;
    case 2:
        out.r = p;
        out.g = in.v;
        out.b = t;
        break;

    case 3:
        out.r = p;
        out.g = q;
        out.b = in.v;
        break;
    case 4:
        out.r = t;
        out.g = p;
        out.b = in.v;
        break;
    case 5:
    default:
        out.r = in.v;
        out.g = p;
        out.b = q;
        break;
    }
    return out;     
}

13
@ Stargazer712 ถ้าคุณคิดเลขมันควรจะเป็น == แต่ถ้าคุณใช้สิ่งนั้นคุณอาจได้รับการร้องเรียนเกี่ยวกับการเปรียบเทียบการลอยตัว แม้ว่าในทางทฤษฎีจะเป็นไปไม่ได้ที่จะ> แต่การใช้ "> =" แทน "==" จะปิดข้อผิดพลาดของคอมไพเลอร์ที่ฉันได้รับบน Mac โดยใช้ llvm / Xcode
David H

4
@ เจอราร์ดออกเป็นองศา 60 คือ 1/6 ของวงกลมเต็ม นี่ไม่ใช่เรเดียน
David H

4
สาเหตุของ>=ข้อผิดพลาดและคอมไพเลอร์เนื่องจากdouble == doubleไม่ถูกต้องและผิดกฎหมายในคอมไพเลอร์ส่วนใหญ่ การจัดเก็บเลขคณิตของจุดลอยตัวและจุดลอยตัวหมายความว่าค่าสองค่าสามารถเท่ากันได้ในค่าโดยประมาณแต่ค่าที่จัดเก็บไว้จะไม่เท่ากันแม้ว่าจะมีสูตรเหมือนกันก็ตาม คุณควรจะทำabs(double_a - double_b) <= epsilonโดยที่ epsilon เป็นค่า1e-4นิยม
Brandon LeBlanc

1
สำหรับ rgb2hsv โดยที่ in.r> = max เหตุใดรหัสจึงไม่ใช้ตัวดำเนินการ mod ไม่ควรคำนวณ out.h เป็น "out.h = ((in.g - in.b) / delta)% 6;" เหรอ?
craigrf

5
@JoachimBrandonLeBlanc: "double == double ไม่ถูกต้องและผิดกฎหมายในคอมไพเลอร์ส่วนใหญ่"นั่นไม่เป็นความจริง เปรียบเทียบสองค่าจุดลอยตัวเพื่อความเท่าเทียมกันเป็นอย่างสมบูรณ์ที่ดีที่กำหนดและเป็นสิ่งที่จะทำตามกฎหมาย ไม่มีคอมไพเลอร์กระแสหลักและ / หรือคอมไพเลอร์ที่เข้ากันได้จะป้องกันไม่ให้คุณทำ ปัญหาคือคุณอาจไม่ได้รับคำตอบที่ต้องการจริงและมีแนวโน้มที่จะทำการเปรียบเทียบแบบหลวม ๆ
Lightness Races ใน Orbit

39

คุณยังสามารถลองใช้รหัสนี้โดยไม่ต้องลอย (เร็วกว่า แต่แม่นยำน้อยกว่า):

typedef struct RgbColor
{
    unsigned char r;
    unsigned char g;
    unsigned char b;
} RgbColor;

typedef struct HsvColor
{
    unsigned char h;
    unsigned char s;
    unsigned char v;
} HsvColor;

RgbColor HsvToRgb(HsvColor hsv)
{
    RgbColor rgb;
    unsigned char region, remainder, p, q, t;

    if (hsv.s == 0)
    {
        rgb.r = hsv.v;
        rgb.g = hsv.v;
        rgb.b = hsv.v;
        return rgb;
    }

    region = hsv.h / 43;
    remainder = (hsv.h - (region * 43)) * 6; 

    p = (hsv.v * (255 - hsv.s)) >> 8;
    q = (hsv.v * (255 - ((hsv.s * remainder) >> 8))) >> 8;
    t = (hsv.v * (255 - ((hsv.s * (255 - remainder)) >> 8))) >> 8;

    switch (region)
    {
        case 0:
            rgb.r = hsv.v; rgb.g = t; rgb.b = p;
            break;
        case 1:
            rgb.r = q; rgb.g = hsv.v; rgb.b = p;
            break;
        case 2:
            rgb.r = p; rgb.g = hsv.v; rgb.b = t;
            break;
        case 3:
            rgb.r = p; rgb.g = q; rgb.b = hsv.v;
            break;
        case 4:
            rgb.r = t; rgb.g = p; rgb.b = hsv.v;
            break;
        default:
            rgb.r = hsv.v; rgb.g = p; rgb.b = q;
            break;
    }

    return rgb;
}

HsvColor RgbToHsv(RgbColor rgb)
{
    HsvColor hsv;
    unsigned char rgbMin, rgbMax;

    rgbMin = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b);
    rgbMax = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b);

    hsv.v = rgbMax;
    if (hsv.v == 0)
    {
        hsv.h = 0;
        hsv.s = 0;
        return hsv;
    }

    hsv.s = 255 * long(rgbMax - rgbMin) / hsv.v;
    if (hsv.s == 0)
    {
        hsv.h = 0;
        return hsv;
    }

    if (rgbMax == rgb.r)
        hsv.h = 0 + 43 * (rgb.g - rgb.b) / (rgbMax - rgbMin);
    else if (rgbMax == rgb.g)
        hsv.h = 85 + 43 * (rgb.b - rgb.r) / (rgbMax - rgbMin);
    else
        hsv.h = 171 + 43 * (rgb.r - rgb.g) / (rgbMax - rgbMin);

    return hsv;
}

โปรดทราบว่าอัลกอริทึมนี้ใช้0-255เป็นช่วง (ไม่ใช่0-360) ตามที่ผู้เขียนคำถามนี้ร้องขอ


8
คุณสามารถแปลงสี RGB ที่เป็นไปได้ทั้งหมด 16,777,216 สีเป็น HSV และกลับมาเป็น RGB อีกครั้ง น่าเสียดายที่การใช้อัลกอริทึมนี้คุณจะพบว่าสีบางสีจะไม่สามารถไปกลับได้ บางทีพวกมันอาจจะดูเหมือนกัน แต่ในเชิงตัวเลขก็มีความแตกต่างอย่างมากเช่น (0, 237, 11) จะไป - กลับ (0, 237, 0) เป็นต้นซึ่งไม่ใช่กรณีนี้เมื่อใช้อัลกอริทึมของ David H ตามการคำนวณจุดลอยตัว .
Martin Liversage

3
@ rightaway717 - นี่ทำให้ฉันได้ครบวงจรบางทีคุณอาจใช้ 0-360 เป็น range? อัลกอริทึมนี้ (ขอบคุณ) ใช้ 0x00 - 0xFF เนื่องจากเป็นช่วง
Anne Quinn

@AnneQuinn ถูกต้อง! ฉันคาดว่ามันจะเป็น 0-360 แต่ฉันไม่มีความกระตือรือร้นมากพอที่จะคิดว่าอะไรผิดพลาดเมื่อฉันเห็นว่าคำตอบที่ยอมรับนั้นใช้งานได้จริง ฉันคิดว่า Leszek ควรพูดถึงช่วงสีในคำตอบแม้ว่าจะขอบคุณเขาที่โพสต์ไว้ก็ตาม
rightaway717

24

ฉันเขียนสิ่งนี้ใน HLSL สำหรับเอ็นจิ้นการเรนเดอร์ของเรามันไม่มีเงื่อนไข:

    float3  HSV2RGB( float3 _HSV )
    {
        _HSV.x = fmod( 100.0 + _HSV.x, 1.0 );                                       // Ensure [0,1[

        float   HueSlice = 6.0 * _HSV.x;                                            // In [0,6[
        float   HueSliceInteger = floor( HueSlice );
        float   HueSliceInterpolant = HueSlice - HueSliceInteger;                   // In [0,1[ for each hue slice

        float3  TempRGB = float3(   _HSV.z * (1.0 - _HSV.y),
                                    _HSV.z * (1.0 - _HSV.y * HueSliceInterpolant),
                                    _HSV.z * (1.0 - _HSV.y * (1.0 - HueSliceInterpolant)) );

        // The idea here to avoid conditions is to notice that the conversion code can be rewritten:
        //    if      ( var_i == 0 ) { R = V         ; G = TempRGB.z ; B = TempRGB.x }
        //    else if ( var_i == 2 ) { R = TempRGB.x ; G = V         ; B = TempRGB.z }
        //    else if ( var_i == 4 ) { R = TempRGB.z ; G = TempRGB.x ; B = V     }
        // 
        //    else if ( var_i == 1 ) { R = TempRGB.y ; G = V         ; B = TempRGB.x }
        //    else if ( var_i == 3 ) { R = TempRGB.x ; G = TempRGB.y ; B = V     }
        //    else if ( var_i == 5 ) { R = V         ; G = TempRGB.x ; B = TempRGB.y }
        //
        // This shows several things:
        //  . A separation between even and odd slices
        //  . If slices (0,2,4) and (1,3,5) can be rewritten as basically being slices (0,1,2) then
        //      the operation simply amounts to performing a "rotate right" on the RGB components
        //  . The base value to rotate is either (V, B, R) for even slices or (G, V, R) for odd slices
        //
        float   IsOddSlice = fmod( HueSliceInteger, 2.0 );                          // 0 if even (slices 0, 2, 4), 1 if odd (slices 1, 3, 5)
        float   ThreeSliceSelector = 0.5 * (HueSliceInteger - IsOddSlice);          // (0, 1, 2) corresponding to slices (0, 2, 4) and (1, 3, 5)

        float3  ScrollingRGBForEvenSlices = float3( _HSV.z, TempRGB.zx );           // (V, Temp Blue, Temp Red) for even slices (0, 2, 4)
        float3  ScrollingRGBForOddSlices = float3( TempRGB.y, _HSV.z, TempRGB.x );  // (Temp Green, V, Temp Red) for odd slices (1, 3, 5)
        float3  ScrollingRGB = lerp( ScrollingRGBForEvenSlices, ScrollingRGBForOddSlices, IsOddSlice );

        float   IsNotFirstSlice = saturate( ThreeSliceSelector );                   // 1 if NOT the first slice (true for slices 1 and 2)
        float   IsNotSecondSlice = saturate( ThreeSliceSelector-1.0 );              // 1 if NOT the first or second slice (true only for slice 2)

        return  lerp( ScrollingRGB.xyz, lerp( ScrollingRGB.zxy, ScrollingRGB.yzx, IsNotSecondSlice ), IsNotFirstSlice );    // Make the RGB rotate right depending on final slice index
    }

2
คุณมีวิธีอื่นในการแปลง (RGB2HSV) หรือไม่? ใช้แนวทางเดียวกัน?
Carlos Barcellos

8

นี่คือการใช้งาน C บนพื้นฐานของคอมพิวเตอร์กราฟิกและการสร้างแบบจำลองทางเรขาคณิตของ Agoston : การใช้งานและอัลกอริทึม p 304, กับH ∈ [0, 360] และS , V ∈ [0, 1]


มีรหัส C ที่คล้ายกันสำหรับการแปลงจาก HSV เป็น RGB หรือไม่? ขอบคุณ!
user3236841

@ user3236841 เทียมสำหรับที่อยู่บนหน้าก่อนหน้า (หน้า 303.) ของ Agoston ของคอมพิวเตอร์กราฟิกและการสร้างแบบจำลองทางเรขาคณิต: การดำเนินงานและขั้นตอนวิธี
Geremia

โปรดตรวจสอบ - นี่คือ HSL2RGB ไม่ใช่ HSV
Kromster

7

สิ่งนี้ควรอยู่ที่นี่: ใช้งานได้อยู่แล้ว และดูดีเมื่อเทียบกับด้านบน

รหัส hlsl

        float3 Hue(float H)
        {
            half R = abs(H * 6 - 3) - 1;
            half G = 2 - abs(H * 6 - 2);
            half B = 2 - abs(H * 6 - 4);
            return saturate(half3(R,G,B));
        }

        half4 HSVtoRGB(in half3 HSV)
        {
            return half4(((Hue(HSV.x) - 1) * HSV.y + 1) * HSV.z,1);
        }

float3 เป็นชนิดข้อมูล vector3 ที่มีความแม่นยำ 16 บิตเช่น float3 hue () ส่งคืนชนิดข้อมูล (x, y, z) เช่น (r, g, b) ครึ่งหนึ่งเหมือนกันโดยมีความแม่นยำครึ่งหนึ่ง 8 บิต, float4 คือ (r, g, b, a) 4 ค่า


3
ต้องการคำจำกัดความบางชนิดสำหรับhalf, half4, half3, float3ฯลฯ
Quuxplusone

1
half4 คือสี (r, g, b, a) หรือโฟลตความแม่นยำ 4x ครึ่งใด ๆ ก็สามารถเป็น full precision ได้เช่นกันมันเป็นแค่ vector4
aliential

อิ่มตัว () คืออะไร?
TatiOverflow

อิ่มตัว () มีอยู่ในการอ้างอิงรหัส HLSL: Saturate
aliential

คุณช่วยอธิบายคำสั่งส่งคืนใน HSVtoRGB ได้ไหม ดูเหมือนว่าจะเป็นเวกเตอร์ RGB 3 องค์ประกอบที่ส่งคืนโดย Hue คูณด้วยสเกลาร์ - ส่งผลให้มีบางอย่างเช่น [k r, k g, k * b, 1]
pathfinder

5

คำตอบของ @ fins มีปัญหาล้นใน Arduio เมื่อคุณลดความอิ่มตัวลง นี่คือค่าบางอย่างที่แปลงเป็น int เพื่อป้องกันสิ่งนั้น

typedef struct RgbColor
{
    unsigned char r;
    unsigned char g;
    unsigned char b;
} RgbColor;

typedef struct HsvColor
{
    unsigned char h;
    unsigned char s;
    unsigned char v;
} HsvColor;

RgbColor HsvToRgb(HsvColor hsv)
{
    RgbColor rgb;
    unsigned char region, p, q, t;
    unsigned int h, s, v, remainder;

    if (hsv.s == 0)
    {
        rgb.r = hsv.v;
        rgb.g = hsv.v;
        rgb.b = hsv.v;
        return rgb;
    }

    // converting to 16 bit to prevent overflow
    h = hsv.h;
    s = hsv.s;
    v = hsv.v;

    region = h / 43;
    remainder = (h - (region * 43)) * 6; 

    p = (v * (255 - s)) >> 8;
    q = (v * (255 - ((s * remainder) >> 8))) >> 8;
    t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8;

    switch (region)
    {
        case 0:
            rgb.r = v;
            rgb.g = t;
            rgb.b = p;
            break;
        case 1:
            rgb.r = q;
            rgb.g = v;
            rgb.b = p;
            break;
        case 2:
            rgb.r = p;
            rgb.g = v;
            rgb.b = t;
            break;
        case 3:
            rgb.r = p;
            rgb.g = q;
            rgb.b = v;
            break;
        case 4:
            rgb.r = t;
            rgb.g = p;
            rgb.b = v;
            break;
        default:
            rgb.r = v;
            rgb.g = p;
            rgb.b = q;
            break;
    }

    return rgb;
}

HsvColor RgbToHsv(RgbColor rgb)
{
    HsvColor hsv;
    unsigned char rgbMin, rgbMax;

    rgbMin = rgb.r < rgb.g ? (rgb.r < rgb.b ? rgb.r : rgb.b) : (rgb.g < rgb.b ? rgb.g : rgb.b);
    rgbMax = rgb.r > rgb.g ? (rgb.r > rgb.b ? rgb.r : rgb.b) : (rgb.g > rgb.b ? rgb.g : rgb.b);

    hsv.v = rgbMax;
    if (hsv.v == 0)
    {
        hsv.h = 0;
        hsv.s = 0;
        return hsv;
    }

    hsv.s = 255 * ((long)(rgbMax - rgbMin)) / hsv.v;
    if (hsv.s == 0)
    {
        hsv.h = 0;
        return hsv;
    }

    if (rgbMax == rgb.r)
        hsv.h = 0 + 43 * (rgb.g - rgb.b) / (rgbMax - rgbMin);
    else if (rgbMax == rgb.g)
        hsv.h = 85 + 43 * (rgb.b - rgb.r) / (rgbMax - rgbMin);
    else
        hsv.h = 171 + 43 * (rgb.r - rgb.g) / (rgbMax - rgbMin);

    return hsv;
}

4

นี่ไม่ใช่ C แต่ใช้ได้ผลอย่างแน่นอน วิธีอื่น ๆ ทั้งหมดที่ฉันเห็นในที่นี้ใช้ได้ผลโดยการแบ่งทุกอย่างเป็นส่วน ๆ ของรูปหกเหลี่ยมและประมาณ "มุม" จากนั้น การเริ่มต้นด้วยสมการอื่นโดยใช้โคไซน์แทนและการแก้สำหรับ hs และ v คุณจะได้รับความสัมพันธ์ระหว่าง hsv และ rgb ที่ดีกว่ามากและการทวีตจะราบรื่นขึ้น (ในราคาที่มันช้าลง)

ถือว่าทุกอย่างเป็นประเด็นลอย ถ้า rg และ b ไปจาก 0 เป็น 1, h ไปจาก 0 ถึง 2pi, v ไปจาก 0 ถึง 4/3 และ s ไปจาก 0 ถึง 2/3

รหัสต่อไปนี้เขียนด้วยภาษา Lua แปลเป็นอะไรก็ได้อย่างง่ายดาย

local hsv do
    hsv         ={}
    local atan2 =math.atan2
    local cos   =math.cos
    local sin   =math.sin

    function hsv.fromrgb(r,b,g)
        local c=r+g+b
        if c<1e-4 then
            return 0,2/3,0
        else
            local p=2*(b*b+g*g+r*r-g*r-b*g-b*r)^0.5
            local h=atan2(b-g,(2*r-b-g)/3^0.5)
            local s=p/(c+p)
            local v=(c+p)/3
            return h,s,v
        end
    end

    function hsv.torgb(h,s,v)
        local r=v*(1+s*(cos(h)-1))
        local g=v*(1+s*(cos(h-2.09439)-1))
        local b=v*(1+s*(cos(h+2.09439)-1))
        return r,g,b
    end

    function hsv.tween(h0,s0,v0,h1,s1,v1,t)
        local dh=(h1-h0+3.14159)%6.28318-3.14159
        local h=h0+t*dh
        local s=s0+t*(s1-s0)
        local v=v0+t*(v1-v0)
        return h,s,v
    end
end

คุณสามารถอธิบายที่มาของอัลกอริทึมนี้หรืออย่างน้อยก็ชี้ไปที่ความสัมพันธ์พื้นฐาน ฉันคาดว่าจะพบเฉดสีบางอย่างที่ประกอบด้วยส่วนประกอบ RGB เพียงชิ้นเดียว - อย่างไรก็ตามฟังก์ชัน hsv.torgb ระบุว่าสิ่งนี้เป็นไปไม่ได้ในอัลกอริทึมนี้ Wikipedia แสดงภาพของความสัมพันธ์ที่คาดหวังระหว่างHSV และ RGB
oclyke

2

รุ่น GLSL Shader ตามคำตอบของ Patapoms:

vec3 HSV2RGB( vec3 hsv )
{
    hsv.x = mod( 100.0 + hsv.x, 1.0 ); // Ensure [0,1[
    float   HueSlice = 6.0 * hsv.x; // In [0,6[
    float   HueSliceInteger = floor( HueSlice );
    float   HueSliceInterpolant = HueSlice - HueSliceInteger; // In [0,1[ for each hue slice
    vec3  TempRGB = vec3(   hsv.z * (1.0 - hsv.y), hsv.z * (1.0 - hsv.y * HueSliceInterpolant), hsv.z * (1.0 - hsv.y * (1.0 - HueSliceInterpolant)) );
    float   IsOddSlice = mod( HueSliceInteger, 2.0 ); // 0 if even (slices 0, 2, 4), 1 if odd (slices 1, 3, 5)
    float   ThreeSliceSelector = 0.5 * (HueSliceInteger - IsOddSlice); // (0, 1, 2) corresponding to slices (0, 2, 4) and (1, 3, 5)
    vec3  ScrollingRGBForEvenSlices = vec3( hsv.z, TempRGB.zx );           // (V, Temp Blue, Temp Red) for even slices (0, 2, 4)
    vec3  ScrollingRGBForOddSlices = vec3( TempRGB.y, hsv.z, TempRGB.x );  // (Temp Green, V, Temp Red) for odd slices (1, 3, 5)
    vec3  ScrollingRGB = mix( ScrollingRGBForEvenSlices, ScrollingRGBForOddSlices, IsOddSlice );
    float   IsNotFirstSlice = clamp( ThreeSliceSelector, 0.0,1.0 );                   // 1 if NOT the first slice (true for slices 1 and 2)
    float   IsNotSecondSlice = clamp( ThreeSliceSelector-1.0, 0.0,1. );              // 1 if NOT the first or second slice (true only for slice 2)
    return  mix( ScrollingRGB.xyz, mix( ScrollingRGB.zxy, ScrollingRGB.yzx, IsNotSecondSlice ), IsNotFirstSlice );    // Make the RGB rotate right depending on final slice index
}

1

ฉันไม่ใช่ผู้พัฒนา C ++ ดังนั้นฉันจะไม่ให้รหัส แต่ผมสามารถให้ง่ายhsv2rgb อัลกอริทึม (rgb2hsv ที่นี่ ) ซึ่งผมค้นพบในขณะนี้ - วิกิพีเดียการปรับปรุงผมมีคำอธิบาย: HSVและHLS การปรับปรุงที่สำคัญคือฉันสังเกต r, g, b เป็นฟังก์ชัน hue อย่างระมัดระวังและแนะนำฟังก์ชันรูปร่างที่ง่ายกว่าเพื่ออธิบาย (โดยไม่สูญเสียความแม่นยำ) อัลกอริทึม - ในอินพุตเรามี: h (0-255), s (0-255), v (0-255)

r = 255*f(5),   g = 255*f(3),   b = 255*f(1)

เราใช้ฟังก์ชัน f อธิบายดังนี้

f(n) = v/255 - (v/255)*(s/255)*max(min(k,4-k,1),0)

โดยที่ (mod สามารถคืนค่าส่วนเศษ k คือเลขทศนิยม)

k = (n+h*360/(255*60)) mod 6;

นี่คือตัวอย่าง / PoV ใน SO ใน JS: HSVและHSL


สวัสดีคามิล! ฉันกำลังพยายามใช้อัลกอริทึมของคุณและฉันมีคำถามเกี่ยวกับส่วนmin(k,4-k,1)นั้น เหตุใดจึงมีค่าสามค่าและเกิดอะไรขึ้นที่นี่? ขอบคุณล่วงหน้า!
Eugene Alexeev

@EugeneAlexeev ฉันแก้ไขบทความในวิกิพีเดีย (มีคนทำพัง) - และอัปเดตลิงก์ที่นี่ - เพื่อความเข้าใจที่ลึกซึ้งยิ่งขึ้นอ่านสิ่งนี้
Kamil Kiełczewski

1

นี่คือตัวแปลงออนไลน์พร้อมบทความหลังจากอธิบายอัลกอริทึมทั้งหมดสำหรับการแปลงสี

คุณอาจชอบเวอร์ชัน C สำเร็จรูป แต่ไม่ควรใช้นานและสามารถช่วยให้คนอื่นพยายามทำเช่นเดียวกันในภาษาอื่นหรือด้วยพื้นที่สีอื่น


0

ลิงค์นี้มีสูตรสำหรับสิ่งที่คุณต้องการ จากนั้นก็เป็นเรื่องของประสิทธิภาพ (เทคนิคเชิงตัวเลข) หากคุณต้องการให้เร็ว


0

นี่คือสิ่งที่ฉันเพิ่งเขียนเมื่อเช้านี้โดยอาศัยคณิตศาสตร์แบบเดียวกันกับด้านบน:

/* math adapted from: http://www.rapidtables.com/convert/color/rgb-to-hsl.htm
 * reasonably optimized for speed, without going crazy */
void rgb_to_hsv (int r, int g, int b, float *r_h, float *r_s, float *r_v) {
  float rp, gp, bp, cmax, cmin, delta, l;
  int cmaxwhich, cminwhich;

  rp = ((float) r) / 255;
  gp = ((float) g) / 255;
  bp = ((float) b) / 255;

  //debug ("rgb=%d,%d,%d rgbprime=%f,%f,%f", r, g, b, rp, gp, bp);

  cmax = rp;
  cmaxwhich = 0; /* faster comparison afterwards */
  if (gp > cmax) { cmax = gp; cmaxwhich = 1; }
  if (bp > cmax) { cmax = bp; cmaxwhich = 2; }
  cmin = rp;
  cminwhich = 0;
  if (gp < cmin) { cmin = gp; cminwhich = 1; }
  if (bp < cmin) { cmin = bp; cminwhich = 2; }

  //debug ("cmin=%f,cmax=%f", cmin, cmax);
  delta = cmax - cmin;

  /* HUE */
  if (delta == 0) {
    *r_h = 0;
  } else {
    switch (cmaxwhich) {
      case 0: /* cmax == rp */
        *r_h = HUE_ANGLE * (fmod ((gp - bp) / delta, 6));
      break;

      case 1: /* cmax == gp */
        *r_h = HUE_ANGLE * (((bp - rp) / delta) + 2);
      break;

      case 2: /* cmax == bp */
        *r_h = HUE_ANGLE * (((rp - gp) / delta) + 4);
      break;
    }
    if (*r_h < 0)
      *r_h += 360;
  }

  /* LIGHTNESS/VALUE */
  //l = (cmax + cmin) / 2;
  *r_v = cmax;

  /* SATURATION */
  /*if (delta == 0) {
    *r_s = 0;
  } else {
    *r_s = delta / (1 - fabs (1 - (2 * (l - 1))));
  }*/
  if (cmax == 0) {
    *r_s = 0;
  } else {
    *r_s = delta / cmax;
  }
  //debug ("rgb=%d,%d,%d ---> hsv=%f,%f,%f", r, g, b, *r_h, *r_s, *r_v);
}


void hsv_to_rgb (float h, float s, float v, int *r_r, int *r_g, int *r_b) {
  if (h > 360)
    h -= 360;
  if (h < 0)
    h += 360;
  h = CLAMP (h, 0, 360);
  s = CLAMP (s, 0, 1);
  v = CLAMP (v, 0, 1);
  float c = v * s;
  float x = c * (1 - fabsf (fmod ((h / HUE_ANGLE), 2) - 1));
  float m = v - c;
  float rp, gp, bp;
  int a = h / 60;

  //debug ("h=%f, a=%d", h, a);

  switch (a) {
    case 0:
      rp = c;
      gp = x;
      bp = 0;
    break;

    case 1:
      rp = x;
      gp = c;
      bp = 0;
    break;

    case 2:
      rp = 0;
      gp = c;
      bp = x;
    break;

    case 3:
      rp = 0;
      gp = x;
      bp = c;
    break;

    case 4:
      rp = x;
      gp = 0;
      bp = c;
    break;

    default: // case 5:
      rp = c;
      gp = 0;
      bp = x;
    break;
  }

  *r_r = (rp + m) * 255;
  *r_g = (gp + m) * 255;
  *r_b = (bp + m) * 255;

  //debug ("hsv=%f,%f,%f, ---> rgb=%d,%d,%d", h, s, v, *r_r, *r_g, *r_b);
}

ไม่มีคำจำกัดความสัญลักษณ์สำหรับ CLAMP และ HUE_ANGLE
Dmitry

0

ฉันสร้างการใช้งานที่เร็วกว่าโดยใช้ช่วง 0-1 สำหรับ RGBS และ V และช่วง 0-6 สำหรับ Hue (หลีกเลี่ยงการแบ่งส่วน) และจัดกลุ่มกรณีออกเป็นสองประเภท:

#include <math.h>
#include <float.h>

void fromRGBtoHSV(float rgb[], float hsv[])
{
//    for(int i=0; i<3; ++i)
//        rgb[i] = max(0.0f, min(1.0f, rgb[i]));

     hsv[0] = 0.0f;
     hsv[2] = max(rgb[0], max(rgb[1], rgb[2]));
     const float delta = hsv[2] - min(rgb[0], min(rgb[1], rgb[2]));

     if (delta < FLT_MIN)
         hsv[1] = 0.0f;
     else
     {
         hsv[1] = delta / hsv[2];
         if (rgb[0] >= hsv[2])
         {
             hsv[0] = (rgb[1] - rgb[2]) / delta;
             if (hsv[0] < 0.0f)
                 hsv[0] += 6.0f;
         }
         else if (rgb[1] >= hsv[2])
             hsv[0] = 2.0f + (rgb[2] - rgb[0]) / delta;
         else
             hsv[0] = 4.0f + (rgb[0] - rgb[1]) / delta;
     }    
}

void fromHSVtoRGB(const float hsv[], float rgb[])
{
    if(hsv[1] < FLT_MIN)
        rgb[0] = rgb[1] = rgb[2] = hsv[2];
    else
    {
        const float h = hsv[0];
        const int i = (int)h;
        const float f = h - i;
        const float p = hsv[2] * (1.0f - hsv[1]);

        if (i & 1) {
            const float q = hsv[2] * (1.0f - (hsv[1] * f));
            switch(i) {
            case 1:
                rgb[0] = q;
                rgb[1] = hsv[2];
                rgb[2] = p;
                break;
            case 3:
                rgb[0] = p;
                rgb[1] = q;
                rgb[2] = hsv[2];
                break;
            default:
                rgb[0] = hsv[2];
                rgb[1] = p;
                rgb[2] = q;
                break;
            }
        }
        else
        {
            const float t = hsv[2] * (1.0f - (hsv[1] * (1.0f - f)));
            switch(i) {
            case 0:
                rgb[0] = hsv[2];
                rgb[1] = t;
                rgb[2] = p;
                break;
            case 2:
                rgb[0] = p;
                rgb[1] = hsv[2];
                rgb[2] = t;
                break;
            default:
                rgb[0] = t;
                rgb[1] = p;
                rgb[2] = hsv[2];
                break;
            }
        }
    }
}

สำหรับช่วง 0-255 เพียง * 255.0f + 0.5f และกำหนดให้กับถ่านที่ไม่ได้ลงชื่อ (หรือหารด้วย 255.0 เพื่อให้ได้ค่าตรงกันข้าม)


0
// This pair of functions convert HSL to RGB and vice-versa.
// It's pretty optimized for execution speed

typedef unsigned char       BYTE
typedef struct _RGB
{
    BYTE R;
    BYTE G;
    BYTE B;
} RGB, *pRGB;
typedef struct _HSL
{
    float   H;  // color Hue (0.0 to 360.0 degrees)
    float   S;  // color Saturation (0.0 to 1.0)
    float   L;  // Luminance (0.0 to 1.0)
    float   V;  // Value (0.0 to 1.0)
} HSL, *pHSL;

float   *fMin       (float *a, float *b)
{
    return *a <= *b?  a : b;
}

float   *fMax       (float *a, float *b)
{
    return *a >= *b? a : b;
}

void    RGBtoHSL    (pRGB rgb, pHSL hsl)
{
// See https://en.wikipedia.org/wiki/HSL_and_HSV
// rgb->R, rgb->G, rgb->B: [0 to 255]
    float r =       (float) rgb->R / 255;
    float g =       (float) rgb->G / 255;
    float b =       (float) rgb->B / 255;
    float *min =    fMin(fMin(&r, &g), &b);
    float *max =    fMax(fMax(&r, &g), &b);
    float delta =   *max - *min;

// L, V [0.0 to 1.0]
    hsl->L = (*max + *min)/2;
    hsl->V = *max;
// Special case for H and S
    if (delta == 0)
    {
        hsl->H = 0.0f;
        hsl->S = 0.0f;
    }
    else
    {
// Special case for S
        if((*max == 0) || (*min == 1))
            hsl->S = 0;
        else
// S [0.0 to 1.0]
            hsl->S = (2 * *max - 2*hsl->L)/(1 - fabsf(2*hsl->L - 1));
// H [0.0 to 360.0]
        if      (max == &r)     hsl->H = fmod((g - b)/delta, 6);    // max is R
        else if (max == &g)     hsl->H = (b - r)/delta + 2;         // max is G
        else                    hsl->H = (r - g)/delta + 4;         // max is B
        hsl->H *= 60;
    }
}

void    HSLtoRGB    (pHSL hsl, pRGB rgb)
{
// See https://en.wikipedia.org/wiki/HSL_and_HSV
    float a, k, fm1, fp1, f1, f2, *f3;
// L, V, S: [0.0 to 1.0]
// rgb->R, rgb->G, rgb->B: [0 to 255]
    fm1 = -1;
    fp1 = 1;
    f1 = 1-hsl->L;
    a = hsl->S * *fMin(&hsl->L, &f1);
    k = fmod(0 + hsl->H/30, 12);
    f1 = k - 3;
    f2 = 9 - k;
    f3 = fMin(fMin(&f1, &f2), &fp1) ;
    rgb->R = (BYTE) (255 * (hsl->L - a * *fMax(f3, &fm1)));

    k = fmod(8 + hsl->H/30, 12);
    f1 = k - 3;
    f2 = 9 - k;
    f3 = fMin(fMin(&f1, &f2), &fp1) ;
    rgb->G = (BYTE) (255 * (hsl->L - a * *fMax(f3, &fm1)));

    k = fmod(4 + hsl->H/30, 12);
    f1 = k - 3;
    f2 = 9 - k;
    f3 = fMin(fMin(&f1, &f2), &fp1) ;
    rgb->B = (BYTE) (255 * (hsl->L - a * *fMax(f3, &fm1)));
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.