使用 Core Graphics 使用 AppleScriptObjC 更改图像色彩空间配置文件

Posted

技术标签:

【中文标题】使用 Core Graphics 使用 AppleScriptObjC 更改图像色彩空间配置文件【英文标题】:Using Core Graphics to change an images colorspace profile using AppleScriptObjC 【发布时间】:2019-06-22 06:33:36 【问题描述】:

我已经有了一些代码,可以使用 NSIMage 和 NSColorSpace 完成我需要的大部分工作。不幸的是,我正在尝试重新创建在 Photoshop 中发生的色彩空间/配置文件更改,它比 NSColorSpace 可以做的要复杂一些。你可以在这里看到那个帖子: Using ApplescriptObjC to convert color spaces of an image using NSColorSpace and iccProfileData

所以我需要帮助的是要么从 CGColorSpace 添加以下内容,要么重新创建脚本的某些部分,以便它们从一开始就使用 Core Graphics。我希望完成的功能是: CGColorRenderingIntent 使用 kCGRenderingIntentPerceptual kCGColorConversionBlackPointCompensation 加上使用抖动作为这种色彩空间转换的一部分,但我似乎无法在 Apple Objective-C 文档中找到一个选项。 NSColor 确实有 NSColorRenderingIntentPerceptual 但似乎在 NSColor 下没有 BlackPointCompensation。

我想我已经确定了构建此脚本所需的所有部分。我认为剧本已经写到一半了。我只是需要一些帮助将最后几位粘合在一起。

我相信脚本仍然需要将配置文件打开到 NSData(该文件是对我正在使用的 ICC 配置文件的 POSIX 文件引用)

set theData to current application's NSData's dataWithContentsOfFile:theFile

现在我需要打开图片,我希望无论使用 NSColor 还是 CGColor 都是一样的:

set theInput to (choose file with prompt "Choose RGB file")
set theOutput to (choose file name default name "Untitled.jpg")
set theImage to current application's NSImage's alloc()'s initWithContentsOfURL:theInput
set imageRep to theImage's representations()'s objectAtIndex:0

这是我看到的最需要帮助的代码行。这实际上是使用 NSColorSpace 进行颜色转换的地方:

set targetSpace to current application's NSColorSpace's alloc's initWithICCProfileData:theData

似乎我应该将 CGColorSpaceCreateICCBased 与 CGDataProviderRef 和 theFile 一起使用,但我怀疑我是否可以将它们替换为 NSColorSpace 和 initWithICCProfileData。我还需要使用 kCGRenderingIntentPerceptual 和 kCGColorConversionBlackPointCompensation 将 CGColorRenderingIntent 移植到这条线或新线(如果该选项甚至存在,则使用抖动)。

我不确定接下来的两行是否需要更新,但我很确定第三行可以保持不变(或者我真的很愚蠢,请原谅我)。

set theProps to current application's NSDictionary's dictionaryWithObjects:1.0, true forKeys:current application's NSImageCompressionFactor, current application's NSImageProgressive
set jpegData to bitmapRep's representationUsingType:(current application's NSJPEGFileType) |properties|:theProps
jpegData's writeToURL:theOutput atomically:true

因此输入将是具有通用 sRGB 配置文件的 RGB,输出将是具有特定 CMYK 配置文件(确切地说是 GRACoL2013_CRPC6.icc)的 CMYK 文件。

【问题讨论】:

您能否在 AppleScriptObjC 中使用 CGColorRenderingIntent() 值得怀疑,因为它接受使用 C 数据类型的参数,而这些参数不会桥接到 AppleScript。 感谢 CJK 为我调查此问题。你会建议我的选择是什么?尝试使用 NSColorRenderingIntentPerceptual 看看我可以多接近 Photoshop 版本?将图像文件和颜色配置文件作为参数并基于配置文件输出具有新颜色空间的文件的命令行工具放在一起会有多困难? 您可以使用 图像事件 做到这一点。如果您使用自定义配置文件设置,您将无法指定黑点补偿。但如果您的机器上保存了特定的 ICC 配置文件,我相信您可以加载它进行嵌入。 【参考方案1】:

输入是带有通用 sRGB 配置文件的 RGB,输出是带有特定 CMYK 配置文件的 CMYK 文件 (GRACoL2013_CRPC6.icc)

如果这准确地概括了目标,您应该能够使用 Image Events 来做到这一点,这是一个 AppleScriptable 无面程序来处理图像。

玩弄了 Image Events,但嵌入一个新的颜色配置文件——这应该是可能的——似乎没有采用,并且原始颜色配置文件仍然存在。

所以我编写了 AppleScriptObjC 等效项:

use framework "Foundation"
use framework "AppKit"
use scripting additions

property this : a reference to the current application
property nil : a reference to missing value
property _1 : a reference to reference

property NSBitmapImageRep : a reference to NSBitmapImageRep of this
property NSColorSpace : a reference to NSColorSpace of this
property NSData : a reference to NSData of this
property NSImage : a reference to NSImage of this
property NSString : a reference to NSString of this
property NSURL : a reference to NSURL of this

property JPEG : a reference to 3
property PNG : a reference to 4
property NSFileType : nil, nil, "jpg", "png"
property options : NSImageCompressionFactor:0.75, NSImageProgressive:true ¬
    , NSImageColorSyncProfileData:a reference to iccData

property NSColorRenderingIntent : Default:0, AbsoluteColorimetric:1 ¬
    , RelativeColorimetric:2, Perceptual:3, Saturation:4
--------------------------------------------------------------------------------
# IMPLEMENTATION:
set iccProfile to loadICCProfile("~/Path/To/GRACoL2013_CRPC6.icc")
set image to _NSImage("~/Path/To/SourceImage.jpg")
set path of image to "~/Path/To/OutputImage.jpg" -- omit to overwrite source
set iccData to iccProfile's space's ICCProfileData()

my (write image for iccProfile given properties:contents of options)
--------------------------------------------------------------------------------
# HANDLERS & SCRIPT OBJECTS:
# __NSURL__()
#   Takes a posix +filepath and returns an NSURL object reference
to __NSURL__(filepath)
    local filepath

    try
        NSURL's fileURLWithPath:((NSString's ¬
            stringWithString:filepath)'s ¬
            stringByStandardizingPath())
    on error
        missing value
    end try
end __NSURL__

# new()
#   Instantiates a new NSObject
on new(_nsObject)
    local _nsObject
    _nsObject's alloc()
end new

# _NSImage()
#   Creates a new NSImage instance with image data loaded from the +filepath
on _NSImage(filepath)
    local filepath

    script
        property file : __NSURL__(filepath)
        property data : new(NSImage)
        property kind : JPEG
        property path : nil -- write path (nil = overwrite source)
        property size : nil
        property name extension : NSFileType's item kind

        to init()
            my (data's initWithContentsOfURL:(my file))
        end init

        to lock()
            tell my data to lockFocus()
        end lock

        to unlock()
            tell my data to unlockFocus()
        end unlock
    end script

    tell the result
        init()
        set its size to its data's |size|() as list
        return it
    end tell
end _NSImage

# ICCProfile()
#   Loads a ColorSync profile from the +filepath and creates a new NSColorSpace
#   instance 
to loadICCProfile(fp)
    local fp

    script
        property file : __NSURL__(fp)
        property data : NSData's dataWithContentsOfURL:(my file)
        property space : new(NSColorSpace)
        property mode : NSColorRenderingIntent's Perceptual

        to init()
            (my space)'s initWithICCProfileData:(my data)
        end init
    end script

    tell the result
        init()
        return it
    end tell
end loadICCProfile

# write
#   Writes out the +_NSImage data optionally converting it to a new colorspace
to write _NSImage for ICC : missing value ¬
    given properties:(opt as record) : missing value
    local _NSImage, ICC, kind, path, options


    set ImageRep to new(NSBitmapImageRep)

    _NSImage's lock()

    ImageRep's initWithFocusedViewRect:0, 0, _NSImage's size
    ImageRep's bitmapImageRepByConvertingToColorSpace:(ICC's space) ¬
        renderingIntent:(ICC's mode)
    result's representationUsingType:(_NSImage's kind) |properties|:opt
    set ImageRep to the result

    _NSImage's unlock()


    set fURL to __NSURL__(_NSImage's path)
    if fURL = missing value then set fURL to NSImage's file
    set ext to _NSImage's name extension
    if fURL's pathExtension() as text ≠ ext then ¬
        fURL's URLByDeletingPathExtension()'s ¬
        URLByAppendingPathExtension:ext

    ImageRep's writeToURL:fURL atomically:yes
    if the result = true then return fURL's |path|() as text
    false
end write
---------------------------------------------------------------------------❮END❯

正如您所指出的,在转换颜色空间时,Core Graphics 的kCGColorConversionBlackPointCompensation 似乎没有等效的 Foundation 类选项。因此,我可能没有为您提供任何您无法做到的脚本方面的内容。然而,我观察到的是,如果在从网站上获取它们之后尝试“按原样”使用它们,GRACoL 颜色配置文件会导致 AppleScript 引擎崩溃。无论出于何种原因,必须首先在 ColorSync Utility.app 中打开配置文件,然后保存 (Save As...) 或导出 (Export.. .)。覆盖原始文件就可以了,之后AppleScript 就会出现使用它的内容。对于系统上已保存的其他配置文件,这似乎不是问题。

【讨论】:

非常感谢 CJK!我会试一试,让你知道它是怎么回事。不确定黑点补偿的重要性。我已经开始尝试构建 ImageMagick 的便携式版本以嵌入到我的脚本中,IM 可以完成我需要的一切(以及很多我不需要的),但它真的很笨拙。我希望有一个不仅仅是 Windows 的官方便携版本。但这既不是这里也不是那里。无论如何,我会弄清楚如何转换色彩空间并将正确的配置文件附加到图像! 所以我用系统上的真实路径替换了你的占位符路径,当我运行脚本时,这段代码出现错误:``` 告诉结果 init() return it end tell end loadICCProfile ``` 变量结果未定义。这很奇怪,因为它甚至不像 Script Debugger 所看到的那样作为变量,它看起来更像是被视为基于颜色编码的属性。因为它是处理 ICC 配置文件的代码,所以我使用 ColorSync 实用程序保留了它,但没有任何效果。 @ChrisNorman 这是一个奇怪的错误。 result 属性,正如颜色编码所暗示的那样:它指的是前一个 AppleScript 表达式的输出(通常是前一行,但在这种情况下是直接在前面的脚本对象)。处理程序为脚本对象执行init(),然后将该脚本对象作为其输出返回。对于未定义的result,不会创建脚本对象,这似乎是不可能的。而且我无法重新创建错误。您是否更改了脚本中的其他任何内容?如果您在 Script Editor 中运行它会怎样? macOS 版本? 在脚本编辑器下运行良好。一定是一个奇怪的 SD 错误或什么的。 我发现 Script Debugger 有时令人沮丧,因为它定义了自己的 AppleScript 术语,有时会与某些脚本发生冲突。也就是说,我的脚本在 SD 中也可以正常运行,所以我不知道问题出在你系统上 SD 的确切位置。如果您将其保存为已编译的脚本 (.scpt),它可能与 SD 存储在脚本中的数据以及属性和脚本对象有关,而 Script Editor 不会(一个我将所有脚本保存为.applescript 文本文件的原因)。

以上是关于使用 Core Graphics 使用 AppleScriptObjC 更改图像色彩空间配置文件的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Core Graphics 绘制箭头?

使用 Core Graphics 在 UIImage 上绘制矩形

使用 Core Graphics 制作类似画笔的纹理

使用 Core Graphics 使用 AppleScriptObjC 更改图像色彩空间配置文件

使用 Core Graphics 在 UIImageView 上绘制一个矩形

如何使用 Core Graphics 绘制点?