Recent

Author Topic: ColorToYCbCr() and vice versa  (Read 2623 times)

prof7bit

  • Full Member
  • ***
  • Posts: 161
ColorToYCbCr() and vice versa
« on: September 28, 2021, 06:57:10 pm »
I have functions for converting TColor to and from YCbCr (YUV) with the profiles ITU-R BT.601, BT.709 and BT.2020. See below:

Are there any objections to trying to get these functions into the GraphUtil unit where the other color space conversions are?

This color space is especially useful when trying to determine whether a text color has a good contrast from the background (is readable) and also to adjust its brightness in YCbCr while maintaining its color, for example to automatically adapt syntax highlighter colors or plot colors when the user switches from a light to a dark theme, or stuff like that. This is what I am experimenting with currently.

HLS is not usable for this (I tried it) because the luminance is not weighted by the human perception, while YCbCr has this weighting built in, first experiments using this color space are very promising.

Here is my code (might still be subject to change):
Code: Pascal  [Select][+][-]
  1. { Conversion to and from YCbCr
  2.  
  3.   Copyright (C) 2021 Bernd Kreuss prof7bit@gmail.com
  4.  
  5.   This library is free software; you can redistribute it and/or modify it
  6.   under the terms of the GNU Library General Public License as published by
  7.   the Free Software Foundation; either version 2 of the License, or (at your
  8.   option) any later version with the following modification:
  9.  
  10.   As a special exception, the copyright holders of this library give you
  11.   permission to link this library with independent modules to produce an
  12.   executable, regardless of the license terms of these independent modules,and
  13.   to copy and distribute the resulting executable under terms of your choice,
  14.   provided that you also meet, for each linked independent module, the terms
  15.   and conditions of the license of that module. An independent module is a
  16.   module which is not derived from or based on this library. If you modify
  17.   this library, you may extend this exception to your version of the library,
  18.   but you are not obligated to do so. If you do not wish to do so, delete this
  19.   exception statement from your version.
  20.  
  21.   This program is distributed in the hope that it will be useful, but WITHOUT
  22.   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  23.   FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
  24.   for more details.
  25.  
  26.   You should have received a copy of the GNU Library General Public License
  27.   along with this library; if not, write to the Free Software Foundation,
  28.   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.
  29. }
  30. unit YCbCr;
  31.  
  32. {$mode ObjFPC}{$H+}
  33.  
  34. interface
  35.  
  36. uses
  37.   Graphics;
  38.  
  39. type
  40.   TItuRec = (
  41.     ituBT601,  // ITU-R BT.601, full range
  42.     ituBT709,  // ITU-R BT.709, full range
  43.     ituBT2020  // ITU-R BT.2020, full range
  44.   );
  45.  
  46. procedure ColorToYCbCr(C: TColor; out Y, Cb, Cr: Byte; Mode: TItuRec);
  47. function YCbCrToColor(Y, Cb, Cr: Byte; Mode: TItuRec): TColor;
  48.  
  49. implementation
  50. uses
  51.   Math;
  52.  
  53. type
  54.   TCoeff = array[0..10] of Double;
  55.  
  56. var
  57.   BT601: TCoeff;
  58.   BT709: TCoeff;
  59.   BT2020: TCoeff;
  60.  
  61. procedure ColorToYCbCr(C: TColor; out Y, Cb, Cr: Byte; constref K: TCoeff);
  62. var
  63.   RGB: LongInt;
  64.   R, G, B: Byte;
  65. begin
  66.   RGB := ColorToRGB(C);
  67.   R := Red(RGB);
  68.   G := Green(RGB);
  69.   B := Blue(RGB);
  70.   Y  := EnsureRange(Round(K[0] * R + K[1] * G + K[2] * B), 0, 255);
  71.   Cb := EnsureRange(Round(K[3] * R + K[4] * G +  0.5 * B + 128), 0, 255);
  72.   Cr := EnsureRange(Round( 0.5 * R + K[5] * G + K[6] * B + 128), 0, 255);
  73. end;
  74.  
  75. function YCbCrToColor(Y, Cb, Cr: Byte; constref K: TCoeff): TColor;
  76. var
  77.   Cb0, Cr0: Integer;
  78.   R, G, B: Byte;
  79. begin
  80.   Cb0 := Cb - 128;
  81.   Cr0 := Cr - 128;
  82.   R := EnsureRange(Round(Y + K[7] * Cr0              ), 0, 255);
  83.   G := EnsureRange(Round(Y + K[8] * Cr0 + K[9]  * Cb0), 0, 255);
  84.   B := EnsureRange(Round(Y              + K[10] * Cb0), 0, 255);
  85.   Result := RGBToColor(R, G, B);
  86. end;
  87.  
  88. procedure ColorToYCbCr(C: TColor; out Y, Cb, Cr: Byte; Mode: TItuRec);
  89. begin
  90.   case Mode of
  91.     ituBT601: ColorToYCbCr(C, Y, Cb, Cr, BT601);
  92.     ituBT709: ColorToYCbCr(C, Y, Cb, Cr, BT709);
  93.     ituBT2020: ColorToYCbCr(C, Y, Cb, Cr, BT2020);
  94.   end;
  95. end;
  96.  
  97. function YCbCrToColor(Y, Cb, Cr: Byte; Mode: TItuRec): TColor;
  98. begin
  99.   case Mode of
  100.     ituBT601: Result := YCbCrToColor(Y, Cb, Cr, BT601);
  101.     ituBT709: Result := YCbCrToColor(Y, Cb, Cr, BT709);
  102.     ituBT2020: Result := YCbCrToColor(Y, Cb, Cr, BT2020);
  103.   end;
  104. end;
  105.  
  106. procedure InitYCbCrCoef(out K: TCoeff; Kr, Kg: Double);
  107. begin                                 //  example values for BT601
  108.   K[0] := Kr;                         //  0.299
  109.   K[1] := Kg;                         //  0.587
  110.   K[2] := 1 - Kr - Kg; // Kb          //  0.114
  111.   K[3] := -Kr / (2 * (Kr + Kg));      // -0.1687
  112.   K[4] := -Kg / (2 * (Kr + Kg));      // -0.3313
  113.   K[5] := -Kg / (2 * (Kg + K[2]));    // -0.4187
  114.   K[6] := -K[2] / (2 * (Kg + K[2]));  // -0.0813
  115.   K[7] := 2 * (Kg + K[2]);            //  1.402
  116.   K[8] := -Kr * 2 * (Kg + K[2]) / Kg; // -0.7141
  117.   K[9] := -K[2] * 2 * (Kr + Kg) / Kg; // -0.3441
  118.   k[10] := 2 * (Kr + Kg);             //  1.772
  119. end;
  120.  
  121. initialization
  122.   InitYCbCrCoef(BT601, 0.299, 0.587);
  123.   InitYCbCrCoef(BT709, 0.2126, 0.7152);
  124.   InitYCbCrCoef(BT2020, 0.2627, 0.6780);
  125. end.
  126.  


wp

  • Hero Member
  • *****
  • Posts: 11853
Re: ColorToYCbCr() and vice versa
« Reply #1 on: September 28, 2021, 07:08:42 pm »
Hmmm, I'm not a friend of adding more and more rarely used stuff into the LCL which gets fatter and fatter with every release. Does Delphi have it? There are special color routines in package mbColorLib (including YCbCr). Isn't this enough?

prof7bit

  • Full Member
  • ***
  • Posts: 161
Re: ColorToYCbCr() and vice versa
« Reply #2 on: September 28, 2021, 07:19:22 pm »
Hmmm, I'm not a friend of adding more and more rarely used stuff into the LCL which gets fatter and fatter with every release. Does Delphi have it? There are special color routines in package mbColorLib (including YCbCr). Isn't this enough?
It would be part of some other functionality I am also working on which aims to automate the choosing of readable colors for syntax highlighters that work the same way on light and dark themes.

I'm not a friend of requiring 42 external packages for every absolutely basic function when I have smart linking. The batteries should be included, this is the key to success! And the limitations of Delphi should not be the benchmark by which we judge the omission of functionality in its much more powerful successor Lazarus.

And no, mbColorLib does not have YCbCr according to its documentation.
« Last Edit: September 28, 2021, 07:26:28 pm by prof7bit »

wp

  • Hero Member
  • *****
  • Posts: 11853
Re: ColorToYCbCr() and vice versa
« Reply #3 on: September 28, 2021, 07:40:29 pm »
It would be part of some other functionality I am also working on which aims to automate the choosing of readable colors for syntax highlighters that work the same way on light and dark themes.
Using it in the highlighters is a good reason to include the routines. But many things look exciting when a project starts, but are abandoned when progress gets tough. So, let's talk about it when your highlighter modification is finished - it's a matter of a few minutes to copy&paste this code into the GraphUtils unit, or to include the YCbCr unit as it is.

And no, mbColorLib does not have YCbCr according to its documentation.
You are free to fix it.

AlexTP

  • Hero Member
  • *****
  • Posts: 2384
    • UVviewsoft
Re: ColorToYCbCr() and vice versa
« Reply #4 on: September 28, 2021, 08:26:04 pm »
Agreed that we don't need the bloat, so it may be a unit in Lazarus CCR.

Agree that if you disappear, no one will fix those functions.

prof7bit

  • Full Member
  • ***
  • Posts: 161
Re: ColorToYCbCr() and vice versa
« Reply #5 on: September 29, 2021, 10:07:18 am »
Agreed that we don't need the bloat, so it may be a unit in Lazarus CCR.
Are you trying to provoke me into some kind of inappropriate reaction by needlessly insulting my code? Why are you starting your disagreement with "Agreed", is this passive agressive behavior representative for the "friendly community" here? Is it intentional to drive contributors away, because you already have too many contributors? What exactly is bloated in my implementation, can you please point this out?
Agree that if you disappear, no one will fix those functions.
Are you telling me that a simple matrix multiplication will degrade over time so that it needs fixing after a while? And then you won't find anybody anymore who can still remember the lost art of linear algebra?

It was a mistake to ask here in this forum instead of talking to actual devs.

This thread can be closed. Unsubscribing.

Bart

  • Hero Member
  • *****
  • Posts: 5275
    • Bart en Mariska's Webstek
Re: ColorToYCbCr() and vice versa
« Reply #6 on: September 29, 2021, 10:32:03 am »
It was a mistake to ask here in this forum instead of talking to actual devs.

You already talked to the devs: see reply #3.
So, we (the devs) are not saying it will never be in Lazarus, but we would like to see how it eventuelly will be used in the highlighters.

And, please don't be offended.

Bart

AlexTP

  • Hero Member
  • *****
  • Posts: 2384
    • UVviewsoft
Re: ColorToYCbCr() and vice versa
« Reply #7 on: September 29, 2021, 11:40:02 am »
Sorry that I insulted your code, I didn't want that. "Bloat" was bad word from my side.

 

TinyPortal © 2005-2018