如何在不拉伸原始图像的情况下扩展 CIImage 的 space(边界)?
How do you extend the space (bounds) of a CIImage without stretching the original?
我正在对一张已经裁剪过的图片应用多个滤镜,我想要一个翻转后的副本与原始图片放在一起。这将使它的宽度增加一倍。
问题:如何扩展边界以使两者都适合? .cropped(to:CGRect) 将拉伸任何原始内容。存在现有内容的原因是因为我正在尝试尽可能多地使用 applyingFilter 来节省处理时间。这也是我裁剪未镜像的原始图像的原因。
下面是我的 CIImage "alphaMaskBlend2",它带有一个合成滤镜,并且对同一图像应用了一个变换,可以翻转它并调整它的位置。 sourceCore.extent 是我想要的最终图像的尺寸。
alphaMaskBlend2 = alphaMaskBlend2?.applyingFilter("CISourceAtopCompositing",
parameters: [kCIInputImageKey: (alphaMaskBlend2?.transformed(by: scaledImageTransform))!,
kCIInputBackgroundImageKey: alphaMaskBlend2!]).cropped(to: sourceCore.extent)
我研究过 LLDB 中变换的位置。我发现这个滤镜被裁剪后,最左边的图像被拉伸了。如果我使用clamped到同样的程度,然后我再次将图像重新裁剪到同样的程度,图像不再扭曲,但是图像的边界只有它应该的宽度的一半。
我能做到这一点的唯一方法是针对背景图像 (sourceCore) 进行合成,该背景图像的大小将是两幅图像的总和,然后再合成另一幅图像:
alphaMaskBlend2 = alphaMaskBlend2?.applyingFilter("CISourceAtopCompositing",
parameters: [kCIInputImageKey: alphaMaskBlend2!,
kCIInputBackgroundImageKey: sourceCore])
alphaMaskBlend2 = alphaMaskBlend2?.applyingFilter("CISourceAtopCompositing",
parameters: [kCIInputImageKey: (alphaMaskBlend2?.cropped(to: cropRect).transformed(by: scaledImageTransform))!,
kCIInputBackgroundImageKey: alphaMaskBlend2!])
问题是,这比必要的要贵。我什至通过基准测试对其进行了测试。如果我能用一个复合材料做到这一点,那就更有意义了。
虽然我可以"flip"一个CIImage
但我找不到使用现有CIFilter
到[=71=的方法] 它与原版并列。但是,只要具备编写自己的 CIKernel
的一些基本知识,您就可以做到。实现此目的的一个简单项目是 here.
此项目包含一个示例图像,并使用 CoreImage 和 GLKView
它:
- 通过转置
CIPerspectiveCorrection
的 Y "bottom/top" 坐标来翻转图像
- 使用
CIConstantColor
创建一个新的 "palette" 图像,然后使用 CICrop
将其裁剪为原始宽度的两倍
- 使用一个非常简单的 CIKernel(注册为 "Stitch" 实际拼接起来
翻转代码如下:
// use CIPerspectiveCorrection to "flip" on the Y axis
let minX:CGFloat = 0
let maxY:CGFloat = 0
let maxX = originalImage?.extent.width
let minY = originalImage?.extent.height
let flipFilter = CIFilter(name: "CIPerspectiveCorrection")
flipFilter?.setValue(CIVector(x: minX, y: maxY), forKey: "inputTopLeft")
flipFilter?.setValue(CIVector(x: maxX!, y: maxY), forKey: "inputTopRight")
flipFilter?.setValue(CIVector(x: minX, y: minY!), forKey: "inputBottomLeft")
flipFilter?.setValue(CIVector(x: maxX!, y: minY!), forKey: "inputBottomRight")
flipFilter?.setValue(originalImage, forKey: "inputImage")
flippedImage = flipFilter?.outputImage
创建调色板的代码如下:
let paletteFilter = CIFilter(name: "CIConstantColorGenerator")
paletteFilter?.setValue(CIColor(red: 0.7, green: 0.4, blue: 0.4), forKey: "inputColor")
paletteImage = paletteFilter?.outputImage
let cropFilter = CIFilter(name: "CICrop")
cropFilter?.setValue(paletteImage, forKey: "inputImage")
cropFilter?.setValue(CIVector(x: 0, y: 0, z: (originalImage?.extent.width)! * 2, w: (originalImage?.extent.height)!), forKey: "inputRectangle")
paletteImage = cropFilter?.outputImage
这里是注册和使用自定义的代码CIFilter
:
// register and use stitch filer
StitchedFilters.registerFilters()
let stitchFilter = CIFilter(name: "Stitch")
stitchFilter?.setValue(originalImage?.extent.width, forKey: "inputThreshold")
stitchFilter?.setValue(paletteImage, forKey: "inputPalette")
stitchFilter?.setValue(originalImage, forKey: "inputOriginal")
stitchFilter?.setValue(flippedImage, forKey: "inputFlipped")
finalImage = stitchFilter?.outputImage
演示项目中的所有代码(连同布局限制)都在 viewDidLoad
中,所以请将其放在它所属的位置!
下面是用于 (a) 创建一个名为 Stitch 的 CIFilter
子类并 (b) 注册它以便您可以像使用任何其他过滤器一样使用它的代码:
func openKernelFile(_ name:String) -> String {
let filePath = Bundle.main.path(forResource: name, ofType: ".cikernel")
do {
return try String(contentsOfFile: filePath!)
}
catch let error as NSError {
return error.description
}
}
let CategoryStitched = "Stitch"
class StitchedFilters: NSObject, CIFilterConstructor {
static func registerFilters() {
CIFilter.registerName(
"Stitch",
constructor: StitchedFilters(),
classAttributes: [
kCIAttributeFilterCategories: [CategoryStitched]
])
}
func filter(withName name: String) -> CIFilter? {
switch name {
case "Stitch":
return Stitch()
default:
return nil
}
}
}
class Stitch:CIFilter {
let kernel = CIKernel(source: openKernelFile("Stitch"))
var inputThreshold:Float = 0
var inputPalette: CIImage!
var inputOriginal: CIImage!
var inputFlipped: CIImage!
override var attributes: [String : Any] {
return [
kCIAttributeFilterDisplayName: "Stitch",
"inputThreshold": [kCIAttributeIdentity: 0,
kCIAttributeClass: "NSNumber",
kCIAttributeDisplayName: "Threshold",
kCIAttributeDefault: 0.5,
kCIAttributeMin: 0,
kCIAttributeSliderMin: 0,
kCIAttributeSliderMax: 1,
kCIAttributeType: kCIAttributeTypeScalar],
"inputPalette": [kCIAttributeIdentity: 0,
kCIAttributeClass: "CIImage",
kCIAttributeDisplayName: "Palette",
kCIAttributeType: kCIAttributeTypeImage],
"inputOriginal": [kCIAttributeIdentity: 0,
kCIAttributeClass: "CIImage",
kCIAttributeDisplayName: "Original",
kCIAttributeType: kCIAttributeTypeImage],
"inputFlipped": [kCIAttributeIdentity: 0,
kCIAttributeClass: "CIImage",
kCIAttributeDisplayName: "Flipped",
kCIAttributeType: kCIAttributeTypeImage]
]
}
override init() {
super.init()
}
override func setValue(_ value: Any?, forKey key: String) {
switch key {
case "inputThreshold":
inputThreshold = value as! Float
case "inputPalette":
inputPalette = value as! CIImage
case "inputOriginal":
inputOriginal = value as! CIImage
case "inputFlipped":
inputFlipped = value as! CIImage
default:
break
}
}
@available(*, unavailable) required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var outputImage: CIImage {
return kernel!.apply(
extent: inputPalette.extent,
roiCallback: {(index, rect) in return rect},
arguments: [
inputThreshold as Any,
inputPalette as Any,
inputOriginal as Any,
inputFlipped as Any
])!
}
}
最后,CIKernel
代码:
kernel vec4 stitch(float threshold, sampler palette, sampler original, sampler flipped) {
vec2 coord = destCoord();
if (coord.x < threshold) {
return sample(original, samplerCoord(original));
} else {
vec2 flippedCoord = coord - vec2(threshold, 0.0);
vec2 flippedCoordinate = samplerTransform(flipped, flippedCoord);
return sample(flipped, flippedCoordinate);
}
}
现在,其他人可能有更优雅的东西——甚至可能使用现有的 CIFilter——但这很有效。它仅使用 GPU,因此在性能方面,可以在 "real time" 中使用。我添加了不需要的代码(注册过滤器,使用字典来定义属性),使其更像是对那些刚开始创建 CIKernels
的人的教学练习,任何知道使用 CIFilters
的人都可以使用。如果您专注于内核代码,您会发现它看起来与 C
多么相似。
最后,一个警告。我只是将(Y 轴)翻转图像拼接到原始图像的右侧。如果您想要其他东西,则需要进行调整。
我正在对一张已经裁剪过的图片应用多个滤镜,我想要一个翻转后的副本与原始图片放在一起。这将使它的宽度增加一倍。
问题:如何扩展边界以使两者都适合? .cropped(to:CGRect) 将拉伸任何原始内容。存在现有内容的原因是因为我正在尝试尽可能多地使用 applyingFilter 来节省处理时间。这也是我裁剪未镜像的原始图像的原因。
下面是我的 CIImage "alphaMaskBlend2",它带有一个合成滤镜,并且对同一图像应用了一个变换,可以翻转它并调整它的位置。 sourceCore.extent 是我想要的最终图像的尺寸。
alphaMaskBlend2 = alphaMaskBlend2?.applyingFilter("CISourceAtopCompositing",
parameters: [kCIInputImageKey: (alphaMaskBlend2?.transformed(by: scaledImageTransform))!,
kCIInputBackgroundImageKey: alphaMaskBlend2!]).cropped(to: sourceCore.extent)
我研究过 LLDB 中变换的位置。我发现这个滤镜被裁剪后,最左边的图像被拉伸了。如果我使用clamped到同样的程度,然后我再次将图像重新裁剪到同样的程度,图像不再扭曲,但是图像的边界只有它应该的宽度的一半。
我能做到这一点的唯一方法是针对背景图像 (sourceCore) 进行合成,该背景图像的大小将是两幅图像的总和,然后再合成另一幅图像:
alphaMaskBlend2 = alphaMaskBlend2?.applyingFilter("CISourceAtopCompositing",
parameters: [kCIInputImageKey: alphaMaskBlend2!,
kCIInputBackgroundImageKey: sourceCore])
alphaMaskBlend2 = alphaMaskBlend2?.applyingFilter("CISourceAtopCompositing",
parameters: [kCIInputImageKey: (alphaMaskBlend2?.cropped(to: cropRect).transformed(by: scaledImageTransform))!,
kCIInputBackgroundImageKey: alphaMaskBlend2!])
问题是,这比必要的要贵。我什至通过基准测试对其进行了测试。如果我能用一个复合材料做到这一点,那就更有意义了。
虽然我可以"flip"一个CIImage
但我找不到使用现有CIFilter
到[=71=的方法] 它与原版并列。但是,只要具备编写自己的 CIKernel
的一些基本知识,您就可以做到。实现此目的的一个简单项目是 here.
此项目包含一个示例图像,并使用 CoreImage 和 GLKView
它:
- 通过转置
CIPerspectiveCorrection
的 Y "bottom/top" 坐标来翻转图像
- 使用
CIConstantColor
创建一个新的 "palette" 图像,然后使用CICrop
将其裁剪为原始宽度的两倍 - 使用一个非常简单的 CIKernel(注册为 "Stitch" 实际拼接起来
翻转代码如下:
// use CIPerspectiveCorrection to "flip" on the Y axis
let minX:CGFloat = 0
let maxY:CGFloat = 0
let maxX = originalImage?.extent.width
let minY = originalImage?.extent.height
let flipFilter = CIFilter(name: "CIPerspectiveCorrection")
flipFilter?.setValue(CIVector(x: minX, y: maxY), forKey: "inputTopLeft")
flipFilter?.setValue(CIVector(x: maxX!, y: maxY), forKey: "inputTopRight")
flipFilter?.setValue(CIVector(x: minX, y: minY!), forKey: "inputBottomLeft")
flipFilter?.setValue(CIVector(x: maxX!, y: minY!), forKey: "inputBottomRight")
flipFilter?.setValue(originalImage, forKey: "inputImage")
flippedImage = flipFilter?.outputImage
创建调色板的代码如下:
let paletteFilter = CIFilter(name: "CIConstantColorGenerator")
paletteFilter?.setValue(CIColor(red: 0.7, green: 0.4, blue: 0.4), forKey: "inputColor")
paletteImage = paletteFilter?.outputImage
let cropFilter = CIFilter(name: "CICrop")
cropFilter?.setValue(paletteImage, forKey: "inputImage")
cropFilter?.setValue(CIVector(x: 0, y: 0, z: (originalImage?.extent.width)! * 2, w: (originalImage?.extent.height)!), forKey: "inputRectangle")
paletteImage = cropFilter?.outputImage
这里是注册和使用自定义的代码CIFilter
:
// register and use stitch filer
StitchedFilters.registerFilters()
let stitchFilter = CIFilter(name: "Stitch")
stitchFilter?.setValue(originalImage?.extent.width, forKey: "inputThreshold")
stitchFilter?.setValue(paletteImage, forKey: "inputPalette")
stitchFilter?.setValue(originalImage, forKey: "inputOriginal")
stitchFilter?.setValue(flippedImage, forKey: "inputFlipped")
finalImage = stitchFilter?.outputImage
演示项目中的所有代码(连同布局限制)都在 viewDidLoad
中,所以请将其放在它所属的位置!
下面是用于 (a) 创建一个名为 Stitch 的 CIFilter
子类并 (b) 注册它以便您可以像使用任何其他过滤器一样使用它的代码:
func openKernelFile(_ name:String) -> String {
let filePath = Bundle.main.path(forResource: name, ofType: ".cikernel")
do {
return try String(contentsOfFile: filePath!)
}
catch let error as NSError {
return error.description
}
}
let CategoryStitched = "Stitch"
class StitchedFilters: NSObject, CIFilterConstructor {
static func registerFilters() {
CIFilter.registerName(
"Stitch",
constructor: StitchedFilters(),
classAttributes: [
kCIAttributeFilterCategories: [CategoryStitched]
])
}
func filter(withName name: String) -> CIFilter? {
switch name {
case "Stitch":
return Stitch()
default:
return nil
}
}
}
class Stitch:CIFilter {
let kernel = CIKernel(source: openKernelFile("Stitch"))
var inputThreshold:Float = 0
var inputPalette: CIImage!
var inputOriginal: CIImage!
var inputFlipped: CIImage!
override var attributes: [String : Any] {
return [
kCIAttributeFilterDisplayName: "Stitch",
"inputThreshold": [kCIAttributeIdentity: 0,
kCIAttributeClass: "NSNumber",
kCIAttributeDisplayName: "Threshold",
kCIAttributeDefault: 0.5,
kCIAttributeMin: 0,
kCIAttributeSliderMin: 0,
kCIAttributeSliderMax: 1,
kCIAttributeType: kCIAttributeTypeScalar],
"inputPalette": [kCIAttributeIdentity: 0,
kCIAttributeClass: "CIImage",
kCIAttributeDisplayName: "Palette",
kCIAttributeType: kCIAttributeTypeImage],
"inputOriginal": [kCIAttributeIdentity: 0,
kCIAttributeClass: "CIImage",
kCIAttributeDisplayName: "Original",
kCIAttributeType: kCIAttributeTypeImage],
"inputFlipped": [kCIAttributeIdentity: 0,
kCIAttributeClass: "CIImage",
kCIAttributeDisplayName: "Flipped",
kCIAttributeType: kCIAttributeTypeImage]
]
}
override init() {
super.init()
}
override func setValue(_ value: Any?, forKey key: String) {
switch key {
case "inputThreshold":
inputThreshold = value as! Float
case "inputPalette":
inputPalette = value as! CIImage
case "inputOriginal":
inputOriginal = value as! CIImage
case "inputFlipped":
inputFlipped = value as! CIImage
default:
break
}
}
@available(*, unavailable) required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var outputImage: CIImage {
return kernel!.apply(
extent: inputPalette.extent,
roiCallback: {(index, rect) in return rect},
arguments: [
inputThreshold as Any,
inputPalette as Any,
inputOriginal as Any,
inputFlipped as Any
])!
}
}
最后,CIKernel
代码:
kernel vec4 stitch(float threshold, sampler palette, sampler original, sampler flipped) {
vec2 coord = destCoord();
if (coord.x < threshold) {
return sample(original, samplerCoord(original));
} else {
vec2 flippedCoord = coord - vec2(threshold, 0.0);
vec2 flippedCoordinate = samplerTransform(flipped, flippedCoord);
return sample(flipped, flippedCoordinate);
}
}
现在,其他人可能有更优雅的东西——甚至可能使用现有的 CIFilter——但这很有效。它仅使用 GPU,因此在性能方面,可以在 "real time" 中使用。我添加了不需要的代码(注册过滤器,使用字典来定义属性),使其更像是对那些刚开始创建 CIKernels
的人的教学练习,任何知道使用 CIFilters
的人都可以使用。如果您专注于内核代码,您会发现它看起来与 C
多么相似。
最后,一个警告。我只是将(Y 轴)翻转图像拼接到原始图像的右侧。如果您想要其他东西,则需要进行调整。