我已经取得了一些进展,可以基本上将其视为对我的问题的回答,虽然有些地方有点不同,而且我认为这种方法并不是很快。我希望能听到任何人的意见,看看如何使这段代码更快。在下面的代码中,似乎调整图像大小占用了大部分时间,我得到了大量的 ovveride outputImage 部分的调用,但我不知道为什么会这样。不幸的是,当我运行下面的拉普拉斯金字塔函数时,它需要大约5秒钟才能完成一个275x300的照片。这太糟糕了,我有点不知道如何加速。我怀疑重采样滤波器是罪魁祸首。然而,我不熟练,不知道如何使它更快。
首先,是自定义过滤器:
第一个通过简单的重新缩放来调整图像大小。在这种情况下,我认为这是最好的调整大小技术,因为在调整大小时只复制像素。例如,如果我们有以下像素块并执行2.0缩放,则映射如下所示:
public class ResampleFilter: CIFilter
{
var inputImage : CIImage?
var inputScaleX: CGFloat = 1
var inputScaleY: CGFloat = 1
let warpKernel = CIWarpKernel(string:
"kernel vec2 resample(float inputScaleX, float inputScaleY)" +
" { " +
" float y = (destCoord().y / inputScaleY); " +
" float x = (destCoord().x / inputScaleX); " +
" return vec2(x,y); " +
" } "
)
override public var outputImage: CIImage!
{
if let inputImage = inputImage,
kernel = warpKernel
{
let arguments = [inputScaleX, inputScaleY]
let extent = CGRect(origin: inputImage.extent.origin,
size: CGSize(width: inputImage.extent.width*inputScaleX,
height: inputImage.extent.height*inputScaleY))
return kernel.applyWithExtent(extent,
roiCallback:
{
(index,rect) in
let sampleX = rect.origin.x/self.inputScaleX
let sampleY = rect.origin.y/self.inputScaleY
let sampleWidth = rect.width/self.inputScaleX
let sampleHeight = rect.height/self.inputScaleY
let sampleRect = CGRect(x: sampleX, y: sampleY, width: sampleWidth, height: sampleHeight)
return sampleRect
},
inputImage : inputImage,
arguments : arguments)
}
return nil
}
}
这是一个简单的差异混合。
public class DifferenceOfImages: CIFilter
{
var inputImage1 : CIImage?
var inputImage2 : CIImage?
var kernel = CIKernel(string:
"kernel vec4 Difference(__sample image1,__sample image2)" +
" { " +
" float colorR = image1.r - image2.r; " +
" float colorG = image1.g - image2.g; " +
" float colorB = image1.b - image2.b; " +
" return vec4(colorR,colorG,colorB,1); " +
" } "
)
var extentFunction: (CGRect, CGRect) -> CGRect =
{ (a: CGRect, b: CGRect) in return CGRectZero }
override public var outputImage: CIImage!
{
guard let inputImage1 = inputImage1,
inputImage2 = inputImage2,
kernel = kernel
else
{
return nil
}
let extent = extentFunction(inputImage1.extent,inputImage2.extent)
let arguments = [inputImage1,inputImage2]
return kernel.applyWithExtent(extent,
roiCallback:
{ (index, rect) in
return rect
},
arguments: arguments)
}
}
现在是一些函数定义:
这个函数只是按照Burt和Adelson论文中描述的相同的5点滤波器对图像进行高斯模糊。不确定如何消除似乎多余的尴尬边框像素。
public func GaussianFilter(ciImage: CIImage) -> CIImage
{
let kernelValues: [CGFloat] = [
0.0025, 0.0125, 0.0200, 0.0125, 0.0025,
0.0125, 0.0625, 0.1000, 0.0625, 0.0125,
0.0200, 0.1000, 0.1600, 0.1000, 0.0200,
0.0125, 0.0625, 0.1000, 0.0625, 0.0125,
0.0025, 0.0125, 0.0200, 0.0125, 0.0025 ]
let weightMatrix = CIVector(values: kernelValues,
count: kernelValues.count)
let filter = CIFilter(name: "CIConvolution5X5",
withInputParameters: [
kCIInputImageKey: ciImage,
kCIInputWeightsKey: weightMatrix])!
let final = filter.outputImage!
let rect = CGRect(x: 0, y: 0, width: ciImage.extent.size.width, height: ciImage.extent.size.height)
return final.imageByCroppingToRect(rect)
}
这个函数只是简化了resample的使用。您可以指定新图像的目标大小。在我看来,这比设置比例参数更容易处理。
public func resampleImage(inputImage: CIImage, sizeX: CGFloat, sizeY: CGFloat) -> CIImage
{
let inputWidth : CGFloat = inputImage.extent.size.width
let inputHeight : CGFloat = inputImage.extent.size.height
let scaleX = sizeX/inputWidth
let scaleY = sizeY/inputHeight
let resamplefilter = ResampleFilter()
resamplefilter.inputImage = inputImage
resamplefilter.inputScaleX = scaleX
resamplefilter.inputScaleY = scaleY
return resamplefilter.outputImage
}
这个函数只是简化了差分滤波器的使用。请注意它是
imageOne - ImageTwo
。
public func Difference(imageOne:CIImage,imageTwo:CIImage) -> CIImage
{
let generalFilter = DifferenceOfImages()
generalFilter.inputImage1 = imageOne
generalFilter.inputImage2 = imageTwo
generalFilter.extentFunction = { (fore, back) in return back.union(fore)}
return generalFilter.outputImage
}
这个函数计算每个金字塔的级别尺寸,并将它们存储在一个数组中。以后会有用。
public func LevelDimensions(image: CIImage,levels:Int) -> [[CGFloat]]
{
let inputWidth : CGFloat = image.extent.width
let inputHeight : CGFloat = image.extent.height
var levelSizes : [[CGFloat]] = [[inputWidth,inputHeight]]
for j in 1...(levels-1)
{
let temp = [floor(inputWidth/pow(2.0,CGFloat(j))),floor(inputHeight/pow(2,CGFloat(j)))]
levelSizes.append(temp)
}
return levelSizes
}
现在进入正题:这个函数将创建一个给定层数的高斯金字塔。
public func GaussianPyramid(image: CIImage,levels:Int) -> [CIImage]
{
let PyrLevel = LevelDimensions(image, levels: levels)
var GauPyr : [CIImage] = [image]
var I : CIImage
var J : CIImage
for j in 1 ... levels-1
{
J = GaussianFilter(GauPyr[j-1])
I = resampleImage(J, sizeX: PyrLevel[j][0], sizeY: PyrLevel[j][1])
GauPyr.append(I)
}
return GauPyr
}
最后,此函数使用给定的级别数创建拉普拉斯金字塔。请注意,在两个金字塔函数中,每个级别都存储在数组中。
public func LaplacianPyramid(image:CIImage,levels:Int) -> [CIImage]
{
let PyrLevel = LevelDimensions(image, levels:levels)
var LapPyr : [CIImage] = []
var I : CIImage
var J : CIImage
J = image
for j in 0 ... levels-2
{
let blur = GaussianFilter(J)
I = resampleImage(blur, sizeX: PyrLevel[j+1][0], sizeY: PyrLevel[j+1][1])
let diff = Difference(J,imageTwo: resampleImage(I, sizeX: PyrLevel[j][0], sizeY: PyrLevel[j][1]))
LapPyr.append(diff)
J = I
}
LapPyr.append(J)
return LapPyr
}
加速框架(Accelerate Framework)
https://developer.apple.com/videos/play/wwdc2013/713/ - juanjo