배경색을 기준으로 글꼴 색 결정


248

사용자가 일부 섹션의 배경색을 사용자 정의 할 수 있지만 글꼴 색상이 아닌 옵션 색상을 사용자 정의 할 수있는 시스템 (예 : 옵션 수를 최소로 유지)을 사용하는 경우 프로그래밍 방식으로 "조명"또는 " 어두운 "글꼴 색상이 필요합니까?

알고리즘이 있다고 확신하지만 색상, 광도 등을 스스로 알아낼 수는 없습니다.

답변:


447

비슷한 문제가 발생했습니다. 컬러 스케일 / 히트 맵에 텍스트 레이블을 표시하기 위해 대비 글꼴 색상을 선택하는 좋은 방법을 찾아야했습니다. 그것은 보편적 인 방법이어야하고 생성 된 색상은 "보기 좋게"되어야하는데, 이는 단순한 보완적인 색상 생성이 좋은 해결책이 아니라는 것을 의미합니다. 때로는보기 어렵고 매우 강렬한 색상을 생성하기도했습니다.

오랜 시간 동안 테스트하고이 문제를 해결하려고 시도한 후 "어두운"색은 흰색 글꼴을, "밝은"색은 검정색 글꼴을 선택하는 것이 가장 좋은 방법이라는 것을 알았습니다.

다음은 C #에서 사용하는 함수의 예입니다.

Color ContrastColor(Color color)
{
    int d = 0;

    // Counting the perceptive luminance - human eye favors green color... 
    double luminance = ( 0.299 * color.R + 0.587 * color.G + 0.114 * color.B)/255;

    if (luminance > 0.5)
       d = 0; // bright colors - black font
    else
       d = 255; // dark colors - white font

    return  Color.FromArgb(d, d, d);
}

이것은 여러 가지 다양한 컬러 스케일 (무지개, 회색조, 열, 얼음 등)에 대해 테스트되었으며 내가 찾은 유일한 "유니버설"방법입니다.

편집
계산 공식 변경a 을 "지각 휘도"로 했습니다. 정말 좋아 보입니다! 이미 내 소프트웨어로 구현했는데 멋지게 보입니다.

Edit 2 @WebSeed는이 알고리즘의 훌륭한 작동 예를 제공했습니다. http://codepen.io/WebSeed/full/pvgqEq/


14
중요하지는 않지만 밝기를 계산하는 더 나은 기능을 원할 수도 있습니다. stackoverflow.com/questions/596216/…
Josh Lee

1
완벽 해 보일 것 같습니다.
Joseph Daigle

정확히 내가 오늘 필요한 것.
Chris W

당신의 지각 휘도 가중치는 어디에서 오는가?
Mat


23

누군가가 GaceK의 답변 버전을 더 짧고 이해하기 쉬운 경우를 대비하여 :

public Color ContrastColor(Color iColor)
{
   // Calculate the perceptive luminance (aka luma) - human eye favors green color... 
   double luma = ((0.299 * iColor.R) + (0.587 * iColor.G) + (0.114 * iColor.B)) / 255;

   // Return black for bright colors, white for dark colors
   return luma > 0.5 ? Color.Black : Color.White;
}

참고 : 루마 의 반전을 제거했습니다. 값 (밝은 색상이 더 높은 값을 갖도록하기 위해 나에게 더 자연스럽고 '기본'계산 방법이기도합니다.

나는에서 GaceK와 같은 상수를 사용 여기에 그들이 나를 위해 큰 일을하기 때문이다.

다음 서명을 사용하여 이를 확장 방법 으로 구현할 수도 있습니다 .

public static Color ContrastColor(this Color iColor)

그런 다음을 통해 호출 할 수 있습니다 foregroundColor = background.ContrastColor().)


11

@Gacek에게 감사합니다 . Android 버전은 다음과 같습니다.

@ColorInt
public static int getContrastColor(@ColorInt int color) {
    // Counting the perceptive luminance - human eye favors green color...
    double a = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255;

    int d;
    if (a < 0.5) {
        d = 0; // bright colors - black font
    } else {
        d = 255; // dark colors - white font
    }

    return Color.rgb(d, d, d);
}

개선 된 (더 짧은) 버전 :

@ColorInt
public static int getContrastColor(@ColorInt int color) {
    // Counting the perceptive luminance - human eye favors green color...
    double a = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255;
    return a < 0.5 ? Color.BLACK : Color.WHITE;
}

@Marcus Mangelsdorf의 변경 사항을 적용하면 ( 1 - ...부분을 제거 하고 이름 a을 다음 과 같이 변경하십시오 .luminance
Ridcully

첫 번째 것을 사용하면 알파도 캡처 할 수 있습니다.
Brill Pappin

9

Gacek의 답변에 대한 Swift 구현 :

func contrastColor(color: UIColor) -> UIColor {
    var d = CGFloat(0)

    var r = CGFloat(0)
    var g = CGFloat(0)
    var b = CGFloat(0)
    var a = CGFloat(0)

    color.getRed(&r, green: &g, blue: &b, alpha: &a)

    // Counting the perceptive luminance - human eye favors green color...
    let luminance = 1 - ((0.299 * r) + (0.587 * g) + (0.114 * b))

    if luminance < 0.5 {
        d = CGFloat(0) // bright colors - black font
    } else {
        d = CGFloat(1) // dark colors - white font
    }

    return UIColor( red: d, green: d, blue: d, alpha: a)
}

신속하게 r / g / b는 CGFloat이므로 휘도를 계산하기 위해 "/ 255"가 필요하지 않습니다. let luminance = 1-((0.299 * r) + (0.587 * g) + (0.114 * b))
SuperDuperTango

8

자바 스크립트 [ES2015]

const hexToLuma = (colour) => {
    const hex   = colour.replace(/#/, '');
    const r     = parseInt(hex.substr(0, 2), 16);
    const g     = parseInt(hex.substr(2, 2), 16);
    const b     = parseInt(hex.substr(4, 2), 16);

    return [
        0.299 * r,
        0.587 * g,
        0.114 * b
    ].reduce((a, b) => a + b) / 255;
};

6

이 게시물에 감사드립니다.

관심있는 사람이라면 델파이에서 그 함수의 예를 보자.

function GetContrastColor(ABGColor: TColor): TColor;
var
  ADouble: Double;
  R, G, B: Byte;
begin
  if ABGColor <= 0 then
  begin
    Result := clWhite;
    Exit; // *** EXIT RIGHT HERE ***
  end;

  if ABGColor = clWhite then
  begin
    Result := clBlack;
    Exit; // *** EXIT RIGHT HERE ***
  end;

  // Get RGB from Color
  R := GetRValue(ABGColor);
  G := GetGValue(ABGColor);
  B := GetBValue(ABGColor);

  // Counting the perceptive luminance - human eye favors green color...
  ADouble := 1 - (0.299 * R + 0.587 * G + 0.114 * B) / 255;

  if (ADouble < 0.5) then
    Result := clBlack  // bright colors - black font
  else
    Result := clWhite;  // dark colors - white font
end;

마지막 줄부터 네 번째에 작은 오류가 있습니다. 결과 : = clBlack 뒤에 세미콜론이 없어야합니다.
Andy k

5

이것은 유용한 답변입니다. 감사합니다!

SCSS 버전을 공유하고 싶습니다 :

@function is-color-light( $color ) {

  // Get the components of the specified color
  $red: red( $color );
  $green: green( $color );
  $blue: blue( $color );

  // Compute the perceptive luminance, keeping
  // in mind that the human eye favors green.
  $l: 1 - ( 0.299 * $red + 0.587 * $green + 0.114 * $blue ) / 255;
  @return ( $l < 0.5 );

}

이제 알고리즘을 사용하여 메뉴 링크의 호버 색상을 자동으로 만드는 방법을 알아 냈습니다. 가벼운 헤더는 더 호버링되며 그 반대도 마찬가지입니다.


3

플러터 구현

Color contrastColor(Color color) {
  if (color == Colors.transparent || color.alpha < 50) {
    return Colors.black;
  }
  double luminance = (0.299 * color.red + 0.587 * color.green + 0.114 * color.blue) / 255;
  return luminance > 0.5 ? Colors.black : Colors.white;
}

내가 한 것은 '정적'접두사였습니다. 감사!
Pete Alvin

2

당신이 그것을 쓰고 싶지 않다면 추악한 파이썬 :)

'''
Input a string without hash sign of RGB hex digits to compute
complementary contrasting color such as for fonts
'''
def contrasting_text_color(hex_str):
    (r, g, b) = (hex_str[:2], hex_str[2:4], hex_str[4:])
    return '000' if 1 - (int(r, 16) * 0.299 + int(g, 16) * 0.587 + int(b, 16) * 0.114) / 255 < 0.5 else 'fff'

2

나는 같은 문제가 있었지만 PHP 로 개발해야했다 . @Garek의 솔루션을 사용 했으며이 답변을 사용했습니다 .HEX 색상 코드를 RGB로 변환하려면 PHP에서 16 진수 색상을 RGB 값 으로 변환하십시오.

그래서 나는 그것을 공유하고 있습니다.

주어진 Background HEX 색상 으로이 기능을 사용하고 싶었지만 항상 '#'부터 시작하는 것은 아닙니다.

//So it can be used like this way:
$color = calculateColor('#804040');
echo $color;

//or even this way:
$color = calculateColor('D79C44');
echo '<br/>'.$color;

function calculateColor($bgColor){
    //ensure that the color code will not have # in the beginning
    $bgColor = str_replace('#','',$bgColor);
    //now just add it
    $hex = '#'.$bgColor;
    list($r, $g, $b) = sscanf($hex, "#%02x%02x%02x");
    $color = 1 - ( 0.299 * $r + 0.587 * $g + 0.114 * $b)/255;

    if ($color < 0.5)
        $color = '#000000'; // bright colors - black font
    else
        $color = '#ffffff'; // dark colors - white font

    return $color;
}

1

Kotlin / Android 확장 프로그램으로 :

fun Int.getContrastColor(): Int {
    // Counting the perceptive luminance - human eye favors green color...
    val a = 1 - (0.299 * Color.red(this) + 0.587 * Color.green(this) + 0.114 * Color.blue(this)) / 255
    return if (a < 0.5) Color.BLACK else Color.WHITE
}

1

objective-c에 대한 구현

+ (UIColor*) getContrastColor:(UIColor*) color {
    CGFloat red, green, blue, alpha;
    [color getRed:&red green:&green blue:&blue alpha:&alpha];
    double a = ( 0.299 * red + 0.587 * green + 0.114 * blue);
    return (a > 0.5) ? [[UIColor alloc]initWithRed:0 green:0 blue:0 alpha:1] : [[UIColor alloc]initWithRed:255 green:255 blue:255 alpha:1];
}

2
코드에 어떤 일이 일어나고 있는지 다른 사용자에게 알리려면 여기에 설명을 추가해야합니다.
Michael

1

iOS Swift 3.0 (UIColor 확장) :

func isLight() -> Bool
{
    if let components = self.cgColor.components, let firstComponentValue = components[0], let secondComponentValue = components[1], let thirdComponentValue = components[2] {
        let firstComponent = (firstComponentValue * 299)
        let secondComponent = (secondComponentValue * 587)
        let thirdComponent = (thirdComponentValue * 114)
        let brightness = (firstComponent + secondComponent + thirdComponent) / 1000

        if brightness < 0.5
        {
            return false
        }else{
            return true
        }
    }  

    print("Unable to grab components and determine brightness")
    return nil
}

1
이 기능은 예상대로 수행되지만 보푸라기 및 강제 주조에주의하십시오
RichAppz

1
좋아요, 아래 Swift 4에 대한 예를 확인하십시오. 코드가 축소 된 것을 볼 수 있습니다
RichAppz

1

스위프트 4 예제 :

extension UIColor {

    var isLight: Bool {
        let components = cgColor.components

        let firstComponent = ((components?[0]) ?? 0) * 299
        let secondComponent = ((components?[1]) ?? 0) * 587
        let thirdComponent = ((components?[2]) ?? 0) * 114
        let brightness = (firstComponent + secondComponent + thirdComponent) / 1000

        return !(brightness < 0.6)
    }

}

업데이트 - 0.6쿼리에 대한 더 나은 테스트 베드라는 것을 알았습니다.


RGB 색상 공간을 가정하기 때문에 실제로 여러 상황에서 실패 할 가능성이 큽니다. 요소의 수는 CGColor.components색상 공간 에 따라 다릅니다. 예를 들어 UIColor.whiteCGColor로 캐스트 할 때 [1.0, 1.0]전체 알파가있는 회색조 (완전한 흰색) 색상을 나타내는 두 가지만 있습니다. UIColor의 RGB 요소를 추출하는 더 좋은 방법은 다음과 같습니다.UIColor.getRed(_ red:, green:, blue:, alpha:)
Scott Matthewman

1

Google 클로저 라이브러리 에는 w3c 권장 사항을 참조 하는 알고리즘이 있습니다 ( http://www.w3.org/TR/AERT#color-contrast) . 그러나이 API에서는 제안 된 색상 목록을 시작점으로 제공합니다.

/**
 * Find the "best" (highest-contrast) of the suggested colors for the prime
 * color. Uses W3C formula for judging readability and visual accessibility:
 * http://www.w3.org/TR/AERT#color-contrast
 * @param {goog.color.Rgb} prime Color represented as a rgb array.
 * @param {Array<goog.color.Rgb>} suggestions Array of colors,
 *     each representing a rgb array.
 * @return {!goog.color.Rgb} Highest-contrast color represented by an array.
 */
goog.color.highContrast = function(prime, suggestions) {
  var suggestionsWithDiff = [];
  for (var i = 0; i < suggestions.length; i++) {
    suggestionsWithDiff.push({
      color: suggestions[i],
      diff: goog.color.yiqBrightnessDiff_(suggestions[i], prime) +
          goog.color.colorDiff_(suggestions[i], prime)
    });
  }
  suggestionsWithDiff.sort(function(a, b) { return b.diff - a.diff; });
  return suggestionsWithDiff[0].color;
};


/**
 * Calculate brightness of a color according to YIQ formula (brightness is Y).
 * More info on YIQ here: http://en.wikipedia.org/wiki/YIQ. Helper method for
 * goog.color.highContrast()
 * @param {goog.color.Rgb} rgb Color represented by a rgb array.
 * @return {number} brightness (Y).
 * @private
 */
goog.color.yiqBrightness_ = function(rgb) {
  return Math.round((rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000);
};


/**
 * Calculate difference in brightness of two colors. Helper method for
 * goog.color.highContrast()
 * @param {goog.color.Rgb} rgb1 Color represented by a rgb array.
 * @param {goog.color.Rgb} rgb2 Color represented by a rgb array.
 * @return {number} Brightness difference.
 * @private
 */
goog.color.yiqBrightnessDiff_ = function(rgb1, rgb2) {
  return Math.abs(
      goog.color.yiqBrightness_(rgb1) - goog.color.yiqBrightness_(rgb2));
};


/**
 * Calculate color difference between two colors. Helper method for
 * goog.color.highContrast()
 * @param {goog.color.Rgb} rgb1 Color represented by a rgb array.
 * @param {goog.color.Rgb} rgb2 Color represented by a rgb array.
 * @return {number} Color difference.
 * @private
 */
goog.color.colorDiff_ = function(rgb1, rgb2) {
  return Math.abs(rgb1[0] - rgb2[0]) + Math.abs(rgb1[1] - rgb2[1]) +
      Math.abs(rgb1[2] - rgb2[2]);
};

0

시각적 효과를 위해 색 공간을 조작하는 경우 일반적으로 RGB보다 HSL (색조, 채도 및 명도)에서 작업하기가 더 쉽습니다. 자연스럽게 만족스러운 효과를 내기 위해 RGB로 색상을 옮기는 것은 개념적으로 매우 어려운 경향이 있지만, HSL로 변환하고 조작 한 다음 다시 다시 변환하는 것은 개념 상 더 직관적이며 항상 더 나은 결과를 제공합니다.

Wikipedia에는 HSL과 밀접하게 관련된 HSV에 대한 좋은 소개 가 있습니다. 그리고 그물 주위에 변환을 수행하는 무료 코드가 있습니다 (예를 들어 여기에 자바 스크립트 구현이 있습니다 )

사용하는 정확한 변환은 맛의 문제이지만 개인적으로 Hue and Lightness 구성 요소를 반전하면 첫 번째 근사치로 좋은 고 대비 색상을 생성해야하지만 더 미묘한 효과를 쉽게 얻을 수 있다고 생각했습니다.


1
그렇습니다. 그러나 사람의 눈이 다른 색보다 훨씬 더 녹색을 많이 볼 수 있고 파란색은 덜 볼 수 있습니다 (따라서 이미지 형식에서 파란색이 더 적은 색 비트를 얻는 이유).
wchargin

1
과연. 우리가 HSL로 이사 할 예정이라면 YUV로 완전히 뛰어 들어 인간의 인식을 고려할 수도 있습니다.
David Bradbury

0

모든 색조 배경에 임의의 색조 텍스트를 사용하고 읽을 수 있는지 확인하십시오. 나는 항상 그것을한다. Javascript에는 읽을 수있는 텍스트 (색상 – STW * )에 대한 공식이 있습니다. 링크에서 언급 한 것처럼 공식은 역 감마 조정 계산의 변형이지만 약간 더 관리하기 쉬운 IMHO입니다. 해당 링크의 오른쪽에있는 메뉴와 관련 페이지는 텍스트와 배경에 대해 무작위로 생성 된 색상을 사용하며 항상 읽을 수 있습니다. 그렇습니다. 분명히 문제 없습니다.


0

알파를 캡처하는 Android 변형입니다.

(@ thomas-vos 감사합니다)

/**
 * Returns a colour best suited to contrast with the input colour.
 *
 * @param colour
 * @return
 */
@ColorInt
public static int contrastingColour(@ColorInt int colour) {
    // XXX /programming/1855884/determine-font-color-based-on-background-color

    // Counting the perceptive luminance - human eye favors green color...
    double a = 1 - (0.299 * Color.red(colour) + 0.587 * Color.green(colour) + 0.114 * Color.blue(colour)) / 255;
    int alpha = Color.alpha(colour);

    int d = 0; // bright colours - black font;
    if (a >= 0.5) {
        d = 255; // dark colours - white font
    }

    return Color.argb(alpha, d, d, d);
}

0

base@Gacek의 답변 R 버전 luminance(자신의 임계 값을 쉽게 적용 할 수 있음)

# vectorized
luminance = function(col) c(c(.299, .587, .114) %*% col2rgb(col)/255)

용법:

luminance(c('black', 'white', '#236FAB', 'darkred', '#01F11F'))
# [1] 0.0000000 1.0000000 0.3730039 0.1629843 0.5698039

0

에 기반 Gacek의 대답 하고 분석 한 후 @ 웹 시드의 예를WAVE의 브라우저 확장, 내가 명암비에 따라 검은 색 또는 흰색 텍스트 (W3C의에 정의 된 선택 다음 버전을 마련했습니다 웹 콘텐츠 접근성 지침 (WCAG) 2.1 ) 휘도 대신에.

이것은 코드입니다 (자바 스크립트).

// As defined in WCAG 2.1
var relativeLuminance = function (R8bit, G8bit, B8bit) {
  var RsRGB = R8bit / 255.0;
  var GsRGB = G8bit / 255.0;
  var BsRGB = B8bit / 255.0;

  var R = (RsRGB <= 0.03928) ? RsRGB / 12.92 : Math.pow((RsRGB + 0.055) / 1.055, 2.4);
  var G = (GsRGB <= 0.03928) ? GsRGB / 12.92 : Math.pow((GsRGB + 0.055) / 1.055, 2.4);
  var B = (BsRGB <= 0.03928) ? BsRGB / 12.92 : Math.pow((BsRGB + 0.055) / 1.055, 2.4);

  return 0.2126 * R + 0.7152 * G + 0.0722 * B;
};

var blackContrast = function(r, g, b) {
  var L = relativeLuminance(r, g, b);
  return (L + 0.05) / 0.05;
};

var whiteContrast = function(r, g, b) {
  var L = relativeLuminance(r, g, b);
  return 1.05 / (L + 0.05);
};

// If both options satisfy AAA criterion (at least 7:1 contrast), use preference
// else, use higher contrast (white breaks tie)
var chooseFGcolor = function(r, g, b, prefer = 'white') {
  var Cb = blackContrast(r, g, b);
  var Cw = whiteContrast(r, g, b);
  if(Cb >= 7.0 && Cw >= 7.0) return prefer;
  else return (Cb > Cw) ? 'black' : 'white';
};

실습 예제는 @WebSeed의 코드 펜 포크 에서 찾을 수 있으며 WAVE에서 대비가 낮은 0 오류를 생성합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.