簡體   English   中英

處理 NSImage 中的像素 (Swift)

[英]Manipulate pixels in NSImage (Swift)

在尋找一種在 NSImage 中操作像素的方法時,我在https://gist.github.com/larsaugustin/af414f3637885f56c837b88c3fea1e6b找到了一種很有前途的方法

但是,當將圖像放入像素數組並將該數組轉換回 NSImage 時,結果會失真。

原圖: 原圖

放入像素陣列並轉換回來后的圖像: 轉換后的圖像

這是重現問題的代碼:

import Foundation
import AppKit

struct Pixel {
    var a: UInt8
    var r: UInt8
    var g: UInt8
    var b: UInt8
}

let fileManager : FileManager = FileManager.default
let fileURL : URL = fileManager.homeDirectoryForCurrentUser.appendingPathComponent("IMG_RGB_ORIG.png")

let imageData = try Data(contentsOf: fileURL)
let imageOrig = NSImage(data: imageData)

let pixels = imageToPixels(image: imageOrig!)

let imageConv = pixelsToImage(pixels: pixels,
                              width: Int(imageOrig!.size.width),
                              height: Int(imageOrig!.size.height))


func imageToPixels(image: NSImage) -> [Pixel] {
    var returnPixels = [Pixel]()

    let pixelData = (image.cgImage(forProposedRect: nil, context: nil, hints: nil)!).dataProvider!.data
    let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)

    for y in 0..<Int(image.size.height) {
        for x in 0..<Int(image.size.width) {
            let pos = CGPoint(x: x, y: y)

            let pixelInfo: Int = ((Int(image.size.width) * Int(pos.y) * 4) + Int(pos.x) * 4)

            let r = data[pixelInfo]
            let g = data[pixelInfo + 1]
            let b = data[pixelInfo + 2]
            let a = data[pixelInfo + 3]
            returnPixels.append(Pixel(a: a, r: r, g: g, b: b))
        }
    }
    return returnPixels
}

func pixelsToImage(pixels: [Pixel], width: Int, height: Int) -> NSImage? {
    guard width > 0 && height > 0 else { return nil }
    guard pixels.count == width * height else { return nil }

    let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
    let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue)
    let bitsPerComponent = 8
    let bitsPerPixel = 32

    var data = pixels
    guard let providerRef = CGDataProvider(data: NSData(bytes: &data,
                            length: data.count * MemoryLayout<Pixel>.size)
        )
        else { return nil }

    guard let cgim = CGImage(
        width: width,
        height: height,
        bitsPerComponent: bitsPerComponent,
        bitsPerPixel: bitsPerPixel,
        bytesPerRow: width * MemoryLayout<Pixel>.size,
        space: rgbColorSpace,
        bitmapInfo: bitmapInfo,
        provider: providerRef,
        decode: nil,
        shouldInterpolate: true,
        intent: .defaultIntent
        )
        else { return nil }

    return NSImage(cgImage: cgim, size: CGSize(width: width, height: height))
}

我使用的是 M1 mac,無論是否使用 Rosetta,結果都是一樣的。 問題似乎已經出現在轉換為像素陣列的過程中。 關於如何更正代碼的任何想法?

同時,我設法根據Objective-C: NSBitmapImageRep SetColor中描述的方法更改了 NSImage 中的像素,該方法在 NSBitmapImageRep 上使用了 setPixel() 方法。

下面的代碼說明了它是如何工作的(請不要在你的代碼中強制解包選項)。

import Foundation
import AppKit

let fileManager : FileManager = FileManager.default
let fileURL : URL = fileManager.homeDirectoryForCurrentUser.appendingPathComponent("IMG_RGB_ORIG.png")

let imgData = try Data(contentsOf: fileURL)
let imgOrig = NSImage(data: imgData)

let rep = NSBitmapImageRep(bitmapDataPlanes: nil,
                           pixelsWide: Int(imgOrig!.size.width),
                           pixelsHigh: Int(imgOrig!.size.height),
                           bitsPerSample: 8,
                           samplesPerPixel: 4,
                           hasAlpha: true,
                           isPlanar: false,
                           colorSpaceName: .deviceRGB,
                           bytesPerRow: Int(imgOrig!.size.width) * 4,
                           bitsPerPixel: 32)

let ctx = NSGraphicsContext.init(bitmapImageRep: rep!)
NSGraphicsContext.saveGraphicsState()
NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: rep!)
imgOrig!.draw(at: NSZeroPoint, from: NSZeroRect, operation: NSCompositingOperation.copy, fraction: 1.0)
ctx?.flushGraphics()
NSGraphicsContext.restoreGraphicsState()

for y in 0..<Int(imgOrig!.size.height) {
    for x in 0..<Int(imgOrig!.size.width) {
        // you can read the color of pixels like this:
        let color = rep!.colorAt(x: x, y: y)
        var colorR = color!.redComponent
        var colorG = color!.greenComponent
        var colorB = color!.blueComponent
        var colorA = color!.alphaComponent
    }
}

var yellowPixel : [Int] = [255, 255, 0  , 255]
var redPixel    : [Int] = [255, 0  , 0  , 255]
var greenPixel  : [Int] = [0  , 255, 0  , 255]
var bluePixel   : [Int] = [0  , 0  , 255, 255]

for y in 10..<Int(imgOrig!.size.height) {
    for x in 10..<Int(imgOrig!.size.width) {
        // you can change the color of pixels like this:
        rep!.setPixel(&yellowPixel, atX: x, y: y)
    }
}

let imgConv = NSImage(cgImage: rep!.cgImage!, size: NSSize(width: Int(imgOrig!.size.width), height: Int(imgOrig!.size.height)))

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM