采样 NSImage 并保持质量 (swift 3)
Sample NSImage and retain quality (swift 3)
我写了一个 NSImage 扩展来允许我对图像进行随机采样。我希望这些样本保留与原始图像相同的质量。但是,它们似乎混叠或略微模糊。这是一个示例 - 右侧绘制的原件和左侧的随机样本:
我现在正在 SpriteKit 中玩这个。这是我创建原始图像的方法:
let bg = NSImage(imageLiteralResourceName: "ref")
let tex = SKTexture(image: bg)
let sprite = SKSpriteNode(texture: tex)
sprite.position = CGPoint(x: size.width/2, y:size.height/2)
addChild(sprite)
下面是我创建示例的方式:
let sample = bg.sample(size: NSSize(width: 100, height: 100))
let sampletex = SKTexture(image:sample!)
let samplesprite = SKSpriteNode(texture:sampletex)
samplesprite.position = CGPoint(x: 60, y:size.height/2)
addChild(samplesprite)
这是创建示例的 NSImage 扩展(和 randomNumber 函数):
extension NSImage {
/// Returns the height of the current image.
var height: CGFloat {
return self.size.height
}
/// Returns the width of the current image.
var width: CGFloat {
return self.size.width
}
func sample(size: NSSize) -> NSImage? {
// Resize the current image, while preserving the aspect ratio.
let source = self
// Make sure that we are within a suitable range
var checkedSize = size
checkedSize.width = floor(min(checkedSize.width,source.size.width * 0.9))
checkedSize.height = floor(min(checkedSize.height, source.size.height * 0.9))
// Get random points for the crop.
let x = randomNumber(range: 0...(Int(source.width) - Int(checkedSize.width)))
let y = randomNumber(range: 0...(Int(source.height) - Int(checkedSize.height)))
// Create the cropping frame.
var frame = NSRect(x: x, y: y, width: Int(checkedSize.width), height: Int(checkedSize.height))
// let ref = source.cgImage.cropping(to:frame)
let ref = source.cgImage(forProposedRect: &frame, context: nil, hints: nil)
let rep = NSBitmapImageRep(cgImage: ref!)
// Create a new image with the new size
let img = NSImage(size: checkedSize)
// Set a graphics context
img.lockFocus()
defer { img.unlockFocus() }
// Fill in the sample image
if rep.draw(in: NSMakeRect(0, 0, checkedSize.width, checkedSize.height),
from: frame,
operation: NSCompositingOperation.copy,
fraction: 1.0,
respectFlipped: false,
hints: [NSImageHintInterpolation:NSImageInterpolation.high.rawValue]) {
// Return the cropped image.
return img
}
// Return nil in case anything fails.
return nil
}
}
func randomNumber(range: ClosedRange<Int> = 0...100) -> Int {
let min = range.lowerBound
let max = range.upperBound
return Int(arc4random_uniform(UInt32(1 + max - min))) + min
}
我已经尝试了大约 10 种不同的方法,结果似乎总是有点模糊。我什至检查了屏幕上的污迹。 :)
我怎样才能创建一个 NSImage 样本来保留原始源图像部分的确切质量?
在这种情况下,将插值模式切换为 NSImageInterpolation.none
显然就足够了。
正确处理绘制目标矩形也很重要。由于 cgImage(forProposedRect:...)
可能会更改建议的矩形,因此您应该使用基于它的目标矩形。您基本上应该使用 frame
的副本,它被 (-x, -y) 偏移,因此它相对于 (0, 0) 而不是 (x, y)。
我写了一个 NSImage 扩展来允许我对图像进行随机采样。我希望这些样本保留与原始图像相同的质量。但是,它们似乎混叠或略微模糊。这是一个示例 - 右侧绘制的原件和左侧的随机样本:
我现在正在 SpriteKit 中玩这个。这是我创建原始图像的方法:
let bg = NSImage(imageLiteralResourceName: "ref")
let tex = SKTexture(image: bg)
let sprite = SKSpriteNode(texture: tex)
sprite.position = CGPoint(x: size.width/2, y:size.height/2)
addChild(sprite)
下面是我创建示例的方式:
let sample = bg.sample(size: NSSize(width: 100, height: 100))
let sampletex = SKTexture(image:sample!)
let samplesprite = SKSpriteNode(texture:sampletex)
samplesprite.position = CGPoint(x: 60, y:size.height/2)
addChild(samplesprite)
这是创建示例的 NSImage 扩展(和 randomNumber 函数):
extension NSImage {
/// Returns the height of the current image.
var height: CGFloat {
return self.size.height
}
/// Returns the width of the current image.
var width: CGFloat {
return self.size.width
}
func sample(size: NSSize) -> NSImage? {
// Resize the current image, while preserving the aspect ratio.
let source = self
// Make sure that we are within a suitable range
var checkedSize = size
checkedSize.width = floor(min(checkedSize.width,source.size.width * 0.9))
checkedSize.height = floor(min(checkedSize.height, source.size.height * 0.9))
// Get random points for the crop.
let x = randomNumber(range: 0...(Int(source.width) - Int(checkedSize.width)))
let y = randomNumber(range: 0...(Int(source.height) - Int(checkedSize.height)))
// Create the cropping frame.
var frame = NSRect(x: x, y: y, width: Int(checkedSize.width), height: Int(checkedSize.height))
// let ref = source.cgImage.cropping(to:frame)
let ref = source.cgImage(forProposedRect: &frame, context: nil, hints: nil)
let rep = NSBitmapImageRep(cgImage: ref!)
// Create a new image with the new size
let img = NSImage(size: checkedSize)
// Set a graphics context
img.lockFocus()
defer { img.unlockFocus() }
// Fill in the sample image
if rep.draw(in: NSMakeRect(0, 0, checkedSize.width, checkedSize.height),
from: frame,
operation: NSCompositingOperation.copy,
fraction: 1.0,
respectFlipped: false,
hints: [NSImageHintInterpolation:NSImageInterpolation.high.rawValue]) {
// Return the cropped image.
return img
}
// Return nil in case anything fails.
return nil
}
}
func randomNumber(range: ClosedRange<Int> = 0...100) -> Int {
let min = range.lowerBound
let max = range.upperBound
return Int(arc4random_uniform(UInt32(1 + max - min))) + min
}
我已经尝试了大约 10 种不同的方法,结果似乎总是有点模糊。我什至检查了屏幕上的污迹。 :)
我怎样才能创建一个 NSImage 样本来保留原始源图像部分的确切质量?
在这种情况下,将插值模式切换为 NSImageInterpolation.none
显然就足够了。
正确处理绘制目标矩形也很重要。由于 cgImage(forProposedRect:...)
可能会更改建议的矩形,因此您应该使用基于它的目标矩形。您基本上应该使用 frame
的副本,它被 (-x, -y) 偏移,因此它相对于 (0, 0) 而不是 (x, y)。