Log in

View Full Version : Color space conversion


hello_hello
10th April 2018, 07:50
I have a function where I'm converting to YV24 and then using Overlay(), but I need to convert to YV24 first, so I can't use Overlay to automatically convert back to the original color space.

To convert back to an Avisynth 2.6 color space I'm using the following function:

function MatchColorFormat(clip Output, clip Source)
{
return \
Source.IsRGB24 ? Output.ConvertToRGB24() : \
Source.IsRGB32 ? Output.ConvertToRGB32() : \
Source.IsYV24 ? Output.ConvertToYV24() : \
Source.IsYV16 ? Output.ConvertToYV16() : \
Source.IsYUY2 ? Output.ConvertToYUY2() : \
Source.IsYV12 ? Output.ConvertToYV12() : \
Source.IsYV411 ? Output.ConvertToYV411() : \
Source.IsY8 ? Output.ConvertToY8() : \
Output.ConvertToYV12()
}

Is there any easy way to do something similar for Avisynth+ while supporting all the new color spaces and bit depths etc, or would it require creating a more convoluted version of the above function?

Thanks.

pinterf
10th April 2018, 08:26
I have a function where I'm converting to YV24 and then using Overlay(), but I need to convert to YV24 first, so I can't use Overlay to automatically convert back to the original color space.

To convert back to an Avisynth 2.6 color space I'm using the following function:
[...]
Is there any easy way to do something similar for Avisynth+ while supporting all the new color spaces and bit depths etc, or would it require creating a more convoluted version of the above function?

Thanks.

In AVS+ the conversion is not necessarily occurs.
There is a bool use444 parameter.

- use444 (not set)
-- RGB (planar/packed): no 444 conversion except modes "luma" and "chroma". Internally Overlay works with planes, for old packed RGB formats there is a lossless silent conversion to and from planar RGB(A)
-- greyscale, 420, 422, 444: no 444 conversion except modes "luma", "chroma" and "blend"
-- YUY2: no conversionless mode

- use444 = true
-- Convert everything to 4:4:4 internally. For RGB colorspaces PC or TV range will be decided upon the "pc_range" parameter

- use444 = false
-- Avoid internal conversion (like when parameter is not given), but it will show error message if conversionless mode is not available (like RGB input with mode="chroma")

hello_hello
10th April 2018, 10:13
pinterf,
Thanks for the reply.

I need to manually convert to YV24 first (or some other format without chroma subsampling), so I can crop Mod1 before using Overlay in the function, which means Overlay will output YV24, so I then need to convert back to the original color space myself. I guess the bitdepth wouldn't change so it'd only be the color space that needs converting.

It's actually not a major issue. The function in question just creates a cropping preview so it probably doesn't matter if the preview itself is in a different color space, but if possible I'd prefer to keep it the same.

What'd be nice is the equivalent of BlankClip(source), where BlankClip has the same properties as "source", only it'd be something like OutputClip.ConvertTo(source), where "OutputClip" is automatically converted to the same color space and bitdepth as "source". :) Just dreaming....

Thanks.

pinterf
10th April 2018, 10:39
You need then MatchColorFormat from raffriff42's script collection

hello_hello
10th April 2018, 11:02
You need then MatchColorFormat from raffriff42's script collection

I'll check it out.

Cheers.

raffriff42
10th April 2018, 12:56
Yep, MatchColorFormat is really handy is this situation. Download Links -
http://avisynth.nl/images/Utils-r41.avsi
https://raw.githubusercontent.com/raffriff42/AvisynthPlusUtilities/master/Utils-r41.avsi

MatchColorFormat: Match color format of source 'C' to template 'T'

@ matrix - ignored if not converting between RGB<>YUV; default "Rec601"
@ keepbits - if true, retain original bit depth; else (default) force bit depth to match 'T'
@ dither - if true, add dither when converting to lower bit depth; default false

function MatchColorFormat(clip C, clip T, string "matrix", bool "keepbits", bool "dither")(set 'Matrix'="Rec709" if you know you used Rec709 before, else use the default; ignore the other arguments)
(someday soon I'll write start a help thread on this collection)

wonkey_monkey
10th April 2018, 15:28
@ dither - if true, add dither when converting to lower bit depth; default false

Shouldn't dither be available when converting between formats at the same bit depth as well?

hello_hello
10th April 2018, 15:54
Yep, MatchColorFormat is really handy is this situation. Download Links -
http://avisynth.nl/images/Utils-r41.avsi
https://raw.githubusercontent.com/raffriff42/AvisynthPlusUtilities/master/Utils-r41.avsi

I've checked it out and added it to the script. It's much appreciated. You've obviously put a lot of work into those functions.
I renamed your function "MatchColorFormatPlus". I'll probably put it inside another function along with mine so the script can still be used with Avisynth 2.6. So far it works a treat. Or something along those lines.

Cheers.

function MatchFormat(clip Output, clip Template, string "Matrix", bool "KeepBits", bool "Dither")
{
return !IsAvsPlus() ? \
MatchColorFormat(Output, Template) : \
MatchColorFormatPlus(Output, Template, Matrix, KeepBits, Dither)
}


PS. I rediscovered the bool "not" operator the other day, so the IsReallyFloat function is now even more succinct. :)

function IsReallyFloat(val v)
{
return !IsInt(v) && IsFloat(v)
}

raffriff42
10th April 2018, 16:14
>I renamed your function "MatchColorFormatPlus".
Why don't you name yours MatchColorFormatClassic? :devil:

hello_hello
10th April 2018, 17:05
>I renamed your function "MatchColorFormatPlus".
Why don't you name yours MatchColorFormatClassic? :devil:

That sounds fair. If you happen to want to add a cropping preview to your collection of functions, feel free. If not, I won't be offended. Here's the finished version.

[* A couple of cropping preview functions. Simply use the Crop() function and add "p" to the beginning or end of the function name for a preview.

pCrop(CL, CT, CR, CB) draws lines to display the cropping preview. The lines being drawn are included in the area to be cropped. The width of the lines can be changed by modifying the following line in the pCrop function below.
CropLine = 1.
Change it to any positive integer.

CropP(CL, CT, CR, CB) overlays a transparent yellow border on the area to be cropped.

Right and bottom cropping can be expressed as a positive or negative number. The "align" argument can also be used so it doesn't need to be added and removed when enabling the preview. *]

# ========== pCrop Function ==========

function pCrop(clip c, int "CL", int "CT", int "CR", int "CB", bool "Align") {

CL = default(CL, 0)
CT = default(CT, 0)
CR = default(CR, 0)
CB = default(CB, 0)
Align = default(Align, false)
CropLine = 1

TheClip = c.ConvertToYV24()
CroppedClip = c.crop(CL, CT, CR, CB, align=Align).ConvertToYV24()

SourceWidth = width(TheClip)
SourceHeight = height(TheClip)
CroppedWidth = width(CroppedClip)
CroppedHeight = height(CroppedClip)

TextPosition = SourceHeight / 3
TextSize = SourceHeight / 27

CR2 = (CR <= 0) ? CR : -(SourceWidth-CL-CR)
CB2 = (CB <= 0) ? CB : -(SourceHeight-CT-CB)
CR3 = (CR >= 0) ? CR : (SourceWidth-CL+CR)
CB3 = (CB >= 0) ? CB : (SourceHeight-CT+CB)

CropLineL = (CL >= CropLine) ? CropLine : (CL < CropLine) ? CL : (CL >= 1) ? CropLine : 0
CropLineT = (CT >= CropLine) ? CropLine : (CT < CropLine) ? CT : (CT >= 1) ? CropLine : 0
CropLineR = (-CR2 >= CropLine) ? CropLine : (-CR2 < CropLine) ? -CR2 : (-CR2 >= 1) ? CropLine : 0
CropLineB = (-CB2 >= CropLine) ? CropLine : (-CB2 < CropLine) ? -CB2 : (-CB2 >= 1) ? CropLine : 0

BordersClip = BlankClip(CroppedClip)\
.AddBorders(CropLineL, CropLineT, CropLineR, CropLineB, color=$FFFF00)\
.AddBorders((CL-CropLineL), (CT-CropLineT), -(CR2+CropLineR), -(CB2+CropLineB))

MaskingClip = BlankClip(CroppedClip)\
.AddBorders(CropLineL, CropLineT, CropLineR, CropLineB, color=$FFFFFF)\
.AddBorders((CL-CropLineL), (CT-CropLineT), -(CR2+CropLineR), -(CB2+CropLineB))

MaskClip = MaskingClip.levels(16, 1.0, 235, 0, 255, coring=false)

STT1 = "Crop (" + string(CL) + ", " + string(CT) + ", " + string(CR2) + ", " + string(CB2) + ")\n\n" + "Crop (" + string(CL) + ", " + string(CT) + ", " + string(CR3) + ", " + string(CB3) + ")"

STT2 = "\n\n\n\nSource Resolution (" + string(SourceWidth) + ", " + string(SourceHeight) + ")\n\n" + "Cropped Resolution (" + string(CroppedWidth) + ", " + string(CroppedHeight) + ")"

CPreviewClip = \
Overlay(TheClip, BordersClip, mask=MaskClip)\
.subtitle(STT1, lsp=10, align=8, size=TextSize, y=TextPosition, text_color=$F0E68C)\
.subtitle(STT2, lsp=10, align=8, size=TextSize, y=TextPosition, text_color=$ADD8E6)

!IsAvsPlus() ? MatchColorFormatClassic(CPreviewClip, c) : MatchColorFormat(CPreviewClip, c) }

# ========== CropP Function ==========

function CropP(clip c, int "CL", int "CT", int "CR", int "CB", bool "Align") {

CL = default(CL, 0)
CT = default(CT, 0)
CR = default(CR, 0)
CB = default(CB, 0)
Align = default(Align, false)

CroppedClip = c.crop(CL, CT, CR, CB, align=Align)

SourceWidth = width(c)
SourceHeight = height(c)
CroppedWidth = width(CroppedClip)
CroppedHeight = height(CroppedClip)

TextPosition = SourceHeight / 3
TextSize = SourceHeight / 27

CR2 = (CR <= 0) ? CR : -(SourceWidth-CL-CR)
CB2 = (CB <= 0) ? CB : -(SourceHeight-CT-CB)
CR3 = (CR >= 0) ? CR : (SourceWidth-CL+CR)
CB3 = (CB >= 0) ? CB : (SourceHeight-CT+CB)

BordersClip = BlankClip(CroppedClip)\
.AddBorders(CL, CT, -CR2, -CB2, color=$FFFF00)

MaskingClip = BlankClip(CroppedClip)\
.AddBorders(CL, CT, -CR2, -CB2, color=$4b4b4b)

MaskClip = MaskingClip.levels(16, 1.0, 235, 0, 255, coring=false)

STT1 = "Crop (" + string(CL) + ", " + string(CT) + ", " + string(CR2) + ", " + string(CB2) + ")\n\n" + "Crop (" + string(CL) + ", " + string(CT) + ", " + string(CR3) + ", " + string(CB3) + ")"

STT2 = "\n\n\n\nSource Resolution (" + string(SourceWidth) + ", " + string(SourceHeight) + ")\n\n" + "Cropped Resolution (" + string(CroppedWidth) + ", " + string(CroppedHeight) + ")"

Overlay(c, BordersClip, mask=MaskClip)\
.subtitle(STT1, lsp=10, align=8, size=TextSize, y=TextPosition, text_color=$F0E68C)\
.subtitle(STT2, lsp=10, align=8, size=TextSize, y=TextPosition, text_color=$ADD8E6) }

# ========== Avisynth 2.6 Format Conversion Function =================================

function MatchColorFormatClassic(clip Output, clip Template) {

return \
Template.IsRGB24 ? Output.ConvertToRGB24() : \
Template.IsRGB32 ? Output.ConvertToRGB32() : \
Template.IsYV24 ? Output.ConvertToYV24() : \
Template.IsYV16 ? Output.ConvertToYV16() : \
Template.IsYUY2 ? Output.ConvertToYUY2() : \
Template.IsYV12 ? Output.ConvertToYV12() : \
Template.IsYV411 ? Output.ConvertToYV411() : \
Template.IsY8 ? Output.ConvertToY8() : \
Output.ConvertToYV12() }

# ========== Avisynth+ Format Conversion Function ==========

# Courtesy Of Raffriff42
# http://avisynth.nl/index.php/User:Raffriff42/Utils-r41_Quick_Reference

# Raffriff42's MatchColorFormat() function goes here

# ========== Is Avisynth+ Function ==========

# Courtesy Of Raffriff42
# http://avisynth.nl/index.php/User:Raffriff42/Utils-r41_Quick_Reference

# Raffriff42's IsAvsPlus() function goes here

StainlessS
10th April 2018, 17:18
Raff, Was not (I dont think) aware of IsAvsPlus() func, heres yours


##################################
### return true if running in Avisynth+, false otherwise
##
function IsAvsPlus()
{
sVer = LCase(VersionString)
return (FindStr(sVer, "avisynth+") > 0)
\ || (FindStr(sVer, "avisynthplus") > 0)
}


And Mine, (implemented inline)

IsAvsPlus=(FindStr(UCase(versionString),"AVISYNTH+")!=0)


Where VersionString for current avs+ produces "AviSynth+ 0.1 (r2664, MT, i386)".

When/where did avs+ VersionString contain text "avisynthplus", ?

Thanx.

EDIT: And would you know if such version supported Gavino GScript functionality.

raffriff42
10th April 2018, 17:55
"avisynthplus" never was in a version string AFAIK, it was there "just in case."

hello_hello
10th April 2018, 18:00
Raff, Was not (I dont think) aware of IsAvsPlus() func, heres yours

I found that amongst raff's functions, and it's also on the Avisynth wiki, but I assumed it was raff's. Maybe not....
http://avisynth.nl/index.php/Internal_functions#Version_functions

EDIT: And would you know if such version supported Gavino GScript functionality.

I wouldn't, but maybe Raff does.

StainlessS
10th April 2018, 18:01
avisynthplus" never was in a version string AFAIK, it was there "just in case".

Oh I see thanx, Belt, Braces, Bungee cord and Safety pins I guess.

raffriff42
11th April 2018, 03:29
!IsAvsPlus() ? MatchColorFormatClassic(CPreviewClip, c) : MatchColorFormat(CPreviewClip, c) }
MatchColorFormat (plus) uses functions that are AVS+ only. I know, sometimes you can get away with it, but if the AVS classic parser touches it for any reason, you will get an error, like for example, "I don't know what BitsPerComponent means."

When writing backwards-compatible scripts (which I don't, much), I like to wrap any AVS+ code in Eval (http://avisynth.nl/index.php/Internal_functions#Eval) strings:!IsAvsPlus() ? Nop
\ : Eval(""" Import("Utils-r41.avsi") """)
[...]
!IsAvsPlus() ? MatchColorFormatClassic(...)
\ : Eval(""" MatchColorFormat(...) """)


# Raffriff42's MatchColorFormat() function goes here
Copying out individual functions from Utils-r41.avsi (if that is what you are doing) is not a good idea. The script as a whole is very "inbred," calling other functions in the same file all over the place. If you try to copy any single function, you will probably get a cascade of "I don't know what <x> means" errors. Just Import("Utils-r41.avsi") and be done with it :)

raffriff42
11th April 2018, 03:58
And would you know if such version supported Gavino GScript functionality.Did you know AVS+ supports GScript (http://avisynth.nl/index.php/GScript#Avisynth.2B) (no plugin required and no String wrapper) which I made use of in my VU meter script (https://forum.doom9.org/showthread.php?t=175370) (the for loop)

StainlessS
11th April 2018, 04:09
Yep thankyou, figured it out quite recently, see here, marked in blue.
https://forum.doom9.org/showthread.php?p=1838155#post1838155

I like to have script working in avs/avs+, which linked script does.

hello_hello
11th April 2018, 04:18
MatchColorFormat (plus) uses functions that are AVS+ only. I know, sometimes you can get away with it, but if the AVS classic parser touches it for any reason, you will get an error, like for example, "I don't know what BitsPerComponent means."

Yes it works. I only have Avisynth 2.6 installed at the moment. If I remove the exclamation mark preceding IsAvsPlus, I get an "I don't know what BitsPerComponent means." error.

Whether or not it'd make a difference I don't know, but it's why I put MatchColorFormatClassic first in the "if, then, else" order. Maybe I should use Eval to be sure.

Copying out individual functions from Utils-r41.avsi (if that is what you are doing) is not a good idea. The script as a whole is very "inbred," calling other functions in the same file all over the place. If you try to copy any single function, you will probably get a cascade of "I don't know what <x> means" errors. Just Import("Utils-r41.avsi") and be done with it :)

Including other functions with a script (as long as they're not huge) seems like a good idea to me. That way the script is self contained. I did check to make sure MatchColorFormat and IsAvsPlus don't rely on other functions, and when I include other functions with a script I usually give them unique names, so if a modified version of an original function ends up in the auto-loading folder, there's no unexpected surprises.
In this case I named them, pCrop_IsAvsPlus, pCrop_MatchColorFormatClassic, and pCrop_MatchColorFormatPlus, although I didn't name them the same way in my previous post so as not to confuse the issue.

I know if scripts load functions from other scripts and those functions are modified, all scripts using those functions will therefore be loading the modified versions, and that may be a good thing, or not. I guess there's pros and cons either way.

hello_hello
11th April 2018, 12:12
In my defence, nobody else noticed....

I know how it happened.... when I first began putting the cropping preview function together I was cropping the source clip, or the "base clip" eventually used in Overlay() and by the time I settled on the simplest way to do it, I'd forgotten manually converting the source clip to another format was no longer necessary. Which means I can let Overlay() do the conversion back to the original color space after-all. Sigh......

Oh well.... at least I learned a bit more. Here's the modified cropping preview function without the need for IsAvsPlus(), MatchColorFormatClassic(), or MatchColorFormat(). Assuming it works for Avisynth+ too. Anyone know if I need to specifically set use444 = true for Overlay in Avisynth+ to have it work the same way as Avisynth 2.6? I'm not 100% sure after reading pinterf's post #2 again.

[* A couple of cropping preview functions. Simply use the Crop() function and add "p" to the beginning or end of the function name for a preview.

pCrop(CL, CT, CR, CB) draws lines to display the cropping preview. The lines being drawn are included in the area to be cropped. The width of the lines can be changed by modifying the following line in the pCrop function below.
CropLine = 1.
Change it to any positive integer.

CropP(CL, CT, CR, CB) overlays a transparent yellow border on the area to be cropped.

Right and bottom cropping can be expressed as a positive or negative number. The "align" argument can also be used so it doesn't need to be added and removed when enabling the preview. *]

# ========== pCrop Function ==========

function pCrop(clip c, int "CL", int "CT", int "CR", int "CB", bool "Align") {

CL = default(CL, 0)
CT = default(CT, 0)
CR = default(CR, 0)
CB = default(CB, 0)
Align = default(Align, false)
CropLine = 1

CroppedClip = c.crop(CL, CT, CR, CB, align=Align).ConvertToYV24()

SourceWidth = width(c)
SourceHeight = height(c)
CroppedWidth = width(CroppedClip)
CroppedHeight = height(CroppedClip)

TextPosition = SourceHeight / 3
TextSize = SourceHeight / 27

CR2 = (CR <= 0) ? CR : -(SourceWidth-CL-CR)
CB2 = (CB <= 0) ? CB : -(SourceHeight-CT-CB)
CR3 = (CR >= 0) ? CR : (SourceWidth-CL+CR)
CB3 = (CB >= 0) ? CB : (SourceHeight-CT+CB)

CropLineL = (CL >= CropLine) ? CropLine : (CL < CropLine) ? CL : (CL >= 1) ? CropLine : 0
CropLineT = (CT >= CropLine) ? CropLine : (CT < CropLine) ? CT : (CT >= 1) ? CropLine : 0
CropLineR = (-CR2 >= CropLine) ? CropLine : (-CR2 < CropLine) ? -CR2 : (-CR2 >= 1) ? CropLine : 0
CropLineB = (-CB2 >= CropLine) ? CropLine : (-CB2 < CropLine) ? -CB2 : (-CB2 >= 1) ? CropLine : 0

BordersClip = BlankClip(CroppedClip)\
.AddBorders(CropLineL, CropLineT, CropLineR, CropLineB, color=$FFFF00)\
.AddBorders((CL-CropLineL), (CT-CropLineT), -(CR2+CropLineR), -(CB2+CropLineB))

MaskingClip = BlankClip(CroppedClip)\
.AddBorders(CropLineL, CropLineT, CropLineR, CropLineB, color=$FFFFFF)\
.AddBorders((CL-CropLineL), (CT-CropLineT), -(CR2+CropLineR), -(CB2+CropLineB))

MaskClip = MaskingClip.levels(16, 1.0, 235, 0, 255, coring=false)

STT1 = "Crop (" + string(CL) + ", " + string(CT) + ", " + string(CR2) + ", " + string(CB2) + ")\n\n" + "Crop (" + string(CL) + ", " + string(CT) + ", " + string(CR3) + ", " + string(CB3) + ")"

STT2 = "\n\n\n\nSource Resolution (" + string(SourceWidth) + ", " + string(SourceHeight) + ")\n\n" + "Cropped Resolution (" + string(CroppedWidth) + ", " + string(CroppedHeight) + ")"

Overlay(c, BordersClip, mask=MaskClip)\
.subtitle(STT1, lsp=10, align=8, size=TextSize, y=TextPosition, text_color=$F0E68C)\
.subtitle(STT2, lsp=10, align=8, size=TextSize, y=TextPosition, text_color=$ADD8E6) }

# ========== CropP Function ==========

function CropP(clip c, int "CL", int "CT", int "CR", int "CB", bool "Align") {

CL = default(CL, 0)
CT = default(CT, 0)
CR = default(CR, 0)
CB = default(CB, 0)
Align = default(Align, false)

CroppedClip = c.crop(CL, CT, CR, CB, align=Align)

SourceWidth = width(c)
SourceHeight = height(c)
CroppedWidth = width(CroppedClip)
CroppedHeight = height(CroppedClip)

TextPosition = SourceHeight / 3
TextSize = SourceHeight / 27

CR2 = (CR <= 0) ? CR : -(SourceWidth-CL-CR)
CB2 = (CB <= 0) ? CB : -(SourceHeight-CT-CB)
CR3 = (CR >= 0) ? CR : (SourceWidth-CL+CR)
CB3 = (CB >= 0) ? CB : (SourceHeight-CT+CB)

BordersClip = BlankClip(CroppedClip)\
.AddBorders(CL, CT, -CR2, -CB2, color=$FFFF00)

MaskingClip = BlankClip(CroppedClip)\
.AddBorders(CL, CT, -CR2, -CB2, color=$4b4b4b)

MaskClip = MaskingClip.levels(16, 1.0, 235, 0, 255, coring=false)

STT1 = "Crop (" + string(CL) + ", " + string(CT) + ", " + string(CR2) + ", " + string(CB2) + ")\n\n" + "Crop (" + string(CL) + ", " + string(CT) + ", " + string(CR3) + ", " + string(CB3) + ")"

STT2 = "\n\n\n\nSource Resolution (" + string(SourceWidth) + ", " + string(SourceHeight) + ")\n\n" + "Cropped Resolution (" + string(CroppedWidth) + ", " + string(CroppedHeight) + ")"

Overlay(c, BordersClip, mask=MaskClip)\
.subtitle(STT1, lsp=10, align=8, size=TextSize, y=TextPosition, text_color=$F0E68C)\
.subtitle(STT2, lsp=10, align=8, size=TextSize, y=TextPosition, text_color=$ADD8E6) }

GMJCZP
11th April 2018, 15:44
@ raffriff42
I suggest you put links in your signature on your contributions, thank you for your efforts.

StainlessS
11th April 2018, 16:09
@ raffriff42
I suggest you put links in your signature on your contributions, thank you for your efforts.

+ 1 on that, its always nice to know who to blame.

EDIT: MatchColorFormat sounds great, prime candidate for builtin methinks.