Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Greenscreen in ChromaLuma-Key-Editor #3

Open
fadyzaky opened this issue Nov 18, 2016 · 8 comments
Open

Greenscreen in ChromaLuma-Key-Editor #3

fadyzaky opened this issue Nov 18, 2016 · 8 comments

Comments

@fadyzaky
Copy link

Dear PGambrill,

I am trying to use the ChromaLuma editor for a green color, but i cant figure out what the code must be for that

chromaLuma!top_zone!FF2000!FF8778!FF8778, this is black
chromaLuma!top_zone!FFEBB4!FFF064!FFF064, this is white
but what is for example green like greenscreen?

thank you

@PGambrill
Copy link
Contributor

Hello fadyzaky,

This document explains how YCbCr values are calculated. Perhaps even more helpfully, it also provides a table of YCbCr values for common colors.

@fadyzaky
Copy link
Author

Dear PGambrill ,

Thank you very much, the document whas very usefull, now I understand how to manipulate the code to make greenscreen effect.

I Used:
chromaLuma!top_zone!FFBF35!FF6E00!FF790E

@musecreation
Copy link

This is a great script!

Regarding calculating the values - we are using a solid color (RGB) as a background in After Effects, then exporting the video to MP4 H.264 High Profile, L4.2. I have used both calculations within the aforementioned document to calculate both the RGB->YCbCr SDTV and HDTV modes and it just is not working to knock out the color. Which are the correct formulae to use for the BrightSign?

Do we have to take into account the "Computer Systems Considerations" equations or just go with the basic equations?

Also, the ChromaLuma script formats the values as [Luma][Cr][Cb] instead of CbCr - I am having to flip my two Cb/Cr values to be in a different order. Is that intentional?

Thank you!
Cheers,
Monica

@jpbrightsign
Copy link

Hi Monica,

I put together a doc and examples which may help you.

chromaluma.zip

J

@salvadornl
Copy link

HELP!! can somebody help me. I can not seem to find the string for RGB Blue(#0000FF). :(

I searched through the whole internet , tried some calculations I just do nit get how I get from these values to the separate hex values for lime Cb and Cr.

Is there anyone that could help me with this. it would be greatly appreciated .

@fiskolini
Copy link

I managed to make it to work. I didn't find any other place with this explanation, so I'm sharing the solution here so that it can be helpful for other people.

All the three properties expected by BrightSign are not common HEX colors (in fact, no colors at all!). They should represent a value that later their hardware will revert and consume for their logic to apply transparency.

Step 1: Transforming HEX into YCbCr

The luma, cb, and cr properties are tightly coupled to the YCbCr color space.

Y′ is the luma component and CB and CR are the blue-difference and red-difference chroma components.

I find it easier to understand (and read) if we convert it into RGB first, and then into YCbCr. Here's a Javascript sample code to do it.

type RGB = {
    red: number,
    green: number,
    blue: number
}

type YCbCr = {
    y: number,
    cb: number,
    cr: number
}

/**
 * Checks if given HEX is valid
 * @param {string} hex
 */
const isValidHex = function (hex) {
    if (typeof hex !== 'string') {
        return false;
    }

    const color = hex.replace('#', '');

    // Only hex with even number of + not longer than 6 chars
    return color.length % 2 === 0 && color.length <= 6;
}

/**
 * Converts given hex color into RGB
 * @param {string} hex
 */
const hex2rgb = function (hex: string): RGB {
    // HEX must be a string type
    if (!isValidHex(hex)) {
        throw new Error("Valid HEX value is expected, received '" + hex + "'");
    }

    const color = hex.replace('#', '');

    // Parse color chunk from hex
    const parse = function (val: string, start: number, end: number): number {
        return parseInt(val.slice(start, end) || '0', 16);
    };

    const red = parse(color, 0, 2);
    const green = parse(color, 2, 4);
    const blue = parse(color, 4, 6);

    return {
        red,
        green,
        blue
    };
};

/**
 * Convert given RGB into YCbCr color space
 * @param {RGB} rgb color
 */
const rgbToYCbCr = function (rgb: RGB): YCbCr {
    const {
        red,
        green,
        blue
    } = rgb;

    const y = red * .299 + green * .587 + blue * .114;
    const cb = red * -.169 + green * -.331 + blue * .500 + 128;
    const cr = red * .500 + green * -.419 + blue * -.081 + 128;

    return {
        y: Math.floor(y),
        cb: Math.floor(cb),
        cr: Math.floor(cr)
    };
};

const hex = '#00FF00'; // full green
const rgb = hex2rgb(hex);
const yCbCr = rgbToYCbCr(rgb);

console.log(yCbCr);
/*
{
  y: 149,
  cb: 43,
  cr: 21
}
*/

Step 2: Expand given values

BrightSign documentation mentions the following format to each of the values to apply later on luma, cb, and cr:

[8 bits of mask][8 bits of high-end range][8 bits of low-end range]

Note the following terms: high-end range and low-end range.

The range property becomes relevant when adjusting transparency because it allows us to define the limits of the color or tonal values that will be affected by the transparency adjustment.

For example, consider an image in grayscale where pixel values range from 0 to 255 (0 being black, 255 being white). If we want to make all pixels with a value of 200 or higher transparent, you are essentially defining a range where transparency will be applied. Pixels with values below 200 will remain opaque, while those with values of 200 or higher will become progressively more transparent.

This range value has to depend on the video target to apply Chroma Key later on. Let's pick 20 for the sake of example.

/**
 * Expand given value by applying range
 * @param {number} val
 * @param {?number} range
 */
const applyRange = function (val, range = 20) {
    return {
        low: val - range,
        high: val + range
    };
};

const yRange = applyRange(yCbCr.y);
const cbRange = applyRange(yCbCr.cb);
const crRange = applyRange(yCbCr.cr);

console.log({yRange, cbRange, crRange})
/*
{
  yRange: {
    high: 169,
    low: 129
  },
  cbRange: {
    high: 63,
    low: 23
  },
  crRange: {
    high: 41,
    low: 1
  }
}
*/

Step 3: Format values

Revisiting BrightSign documentation, we can see the following information:

Each integer value is arranged as follows: [8 bits of mask][8 bits of high-end range][8 bits of low-end range]. For example, a 0xff8040 value for luma would mask luma at 0xff (no change) and then apply a range from 0x40 to 0x80 for changing to transparent alpha. Note that chroma and luma keying work well with simple shapes and patterns, while complex patterns like hair or grass will not be masked effectively.

In this final step, we only need to pick all the converted values and transform them into their required format.

/**
 * Converts given decimal to HEX 8 bits
 * @param {number} dec
 */
const decimalToHex = function (dec: number) {
    // Ensure the decimal is within the valid range (0 to 255)
    var decimal = Math.max(0, Math.min(dec, 255));

    // Convert to hexadecimal and pad with zeros if necessary
    let hex = decimal.toString(16).toUpperCase();
    return (hex.length === 1 ? '0' : '') + hex;
};

/**
 * Applies BrightSign value
 * @param {ValueRange} rangedValue
 */
const formatRangeValue = function (rangedValue: ValueRange): string {
    const mask = 'FF';

    return `#${mask}${decimalToHex(rangedValue.high)}${decimalToHex(rangedValue.low)}`;
}

const lumaKey = formatRangeValue(yRange);
const cbKey = formatRangeValue(cbRange);
const crKey = formatRangeValue(crRange);

console.log({lumaKey, cbKey, crKey});
/*
{
  lumaKey: "#FFA981",
  cbKey: "#FF3F17",
  crKey: "#FF2901"
}
 */

This working code can be found in this Fiddle.
Hope that helps.

@BeQuied
Copy link

BeQuied commented Oct 9, 2024

The way it works is actually quite simple. All you need is a graphic like this.
https://en.wikipedia.org/wiki/YCbCr#/media/File:YCbCr-CbCr_Scaled_Y50.png
Ideally as a video clip of 10 seconds.

The Cb axis goes from 00h to FFh (from left to right).
The Cr axis goes from 00h to FFh (from bottom to top).
The centre is therefore at 80h/80h (128/128).
The commands to make colour areas transparent are e.g:
chromaLuma!top_zone!FFEB35!FFFF80!FF8000 - (red)
chromaLuma!top_zone!FFEB20!FFFFC0!FF6000
chromaLuma!top_zone!FFEB35!FFFF80!FFFF80 - (magenta)
chromaLuma!top_zone!FFEB35!FFFFC0!FFFFC0
chromaLuma!top_zone!FFEB35!FF8000!FF8000 - (green)
chromaLuma!top_zone!FFEB35!FF4000!FF4000
chromaLuma!top_zone!FFC000!FF8000!FFFF80 - (blue)
chromaLuma!top_zone!FFC00E!FF6000!FFFFA0
chromaLuma!top_zone!FFC040!FFA060!FFA060 - (hole in the centre)

So it is not individual colours that become transparent, but colour areas that can be restricted using the YCbCr values. I have not found a method to calculate these values, neither with YCbCr-SDTV nor YCbCr-HDTV and their variants. So, just try it out.

@BeQuied
Copy link

BeQuied commented Oct 9, 2024

Sorry, my mistake:
You can make individual colours transparent, or strongly limit the colour, e.g.

chromaLuma!under_zone!FFEB35!FFC0C0!FFC0C0
A few pixels in the centre of the magenta area.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants