Alles, was ich tun möchte, ist, die grundlegende Arkit-Ansicht in eine Schwarzweiß-Ansicht umzuwandeln. Im Moment ist die Grundansicht einfach normal und ich habe keine Ahnung, wie der Filter hinzugefügt wird. Idealerweise wird beim Aufnehmen eines Screenshots der Schwarzweißfilter zum Screenshot hinzugefügt.

import UIKit
import SceneKit
import ARKit

class ViewController: UIViewController, ARSCNViewDelegate {

    @IBOutlet var sceneView: ARSCNView!

    override func viewDidLoad() {
        super.viewDidLoad()
        sceneView.delegate = self
        sceneView.showsStatistics = true
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        let configuration = ARWorldTrackingConfiguration()
        sceneView.session.run(configuration)
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        sceneView.session.pause()
    }

    @IBAction func changeTextColour(){
        let snapShot = self.augmentedRealityView.snapshot()
        UIImageWriteToSavedPhotosAlbum(snapShot, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
    }
}
4
user8105388 19 Apr. 2018 im 07:01

4 Antworten

Beste Antwort

ARSCNView-Snapshot filtern: Wenn Sie einen Schwarzweiß-Screenshot Ihres ARSCNView erstellen möchten, können Sie so etwas tun, das in GrayScale ein UIImage zurückgibt und dabei {{X2} } bezieht sich auf ein ARSCNView:

/// Converts A UIImage To A High Contrast GrayScaleImage
///
/// - Returns: UIImage
func highContrastBlackAndWhiteFilter() -> UIImage?
{
    //1. Convert It To A CIIamge
    guard let convertedImage = CIImage(image: self) else { return nil }

    //2. Set The Filter Parameters
    let filterParameters = [kCIInputBrightnessKey: 0.0,
                            kCIInputContrastKey:   1.1,
                            kCIInputSaturationKey: 0.0]

    //3. Apply The Basic Filter To The Image
    let imageToFilter = convertedImage.applyingFilter("CIColorControls", parameters: filterParameters)

    //4. Set The Exposure
    let exposure =  [kCIInputEVKey: NSNumber(value: 0.7)]

    //5. Process The Image With The Exposure Setting
    let processedImage = imageToFilter.applyingFilter("CIExposureAdjust", parameters: exposure)

    //6. Create A CG GrayScale Image
    guard let grayScaleImage = CIContext().createCGImage(processedImage, from: processedImage.extent) else { return nil }

    return UIImage(cgImage: grayScaleImage, scale: self.scale, orientation: self.imageOrientation)
}

Ein Beispiel für die Verwendung könnte daher folgendermaßen aussehen:

 override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

    //1. Create A UIImageView Dynamically
    let imageViewResult = UIImageView(frame: CGRect(x: 0, y: 0, width: self.view.bounds.width, height: self.view.bounds.height))
    self.view.addSubview(imageViewResult)

    //2. Create The Snapshot & Get The Black & White Image
    guard let snapShotImage = self.augmentedRealityView.snapshot().highContrastBlackAndWhiteFilter() else { return }
    imageViewResult.image = snapShotImage

    //3. Remove The ImageView After A Delay Of 5 Seconds
    DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
        imageViewResult.removeFromSuperview()
    }

}

Was zu einem Ergebnis wie diesem führt:

enter image description here

Um Ihren Code wiederverwendbar zu machen, können Sie auch ein extension von `UIImage erstellen:

//------------------------
//MARK: UIImage Extensions
//------------------------

extension UIImage
{

    /// Converts A UIImage To A High Contrast GrayScaleImage
    ///
    /// - Returns: UIImage
    func highContrastBlackAndWhiteFilter() -> UIImage?
    {
        //1. Convert It To A CIIamge
        guard let convertedImage = CIImage(image: self) else { return nil }

        //2. Set The Filter Parameters
        let filterParameters = [kCIInputBrightnessKey: 0.0,
                                kCIInputContrastKey:   1.1,
                                kCIInputSaturationKey: 0.0]

        //3. Apply The Basic Filter To The Image
        let imageToFilter = convertedImage.applyingFilter("CIColorControls", parameters: filterParameters)

        //4. Set The Exposure
        let exposure =  [kCIInputEVKey: NSNumber(value: 0.7)]

        //5. Process The Image With The Exposure Setting
        let processedImage = imageToFilter.applyingFilter("CIExposureAdjust", parameters: exposure)

        //6. Create A CG GrayScale Image
        guard let grayScaleImage = CIContext().createCGImage(processedImage, from: processedImage.extent) else { return nil }

        return UIImage(cgImage: grayScaleImage, scale: self.scale, orientation: self.imageOrientation)
    }

}

Was Sie dann einfach so verwenden können:

guard let snapShotImage = self.augmentedRealityView.snapshot().highContrastBlackAndWhiteFilter() else { return }

Denken Sie daran, dass Sie Ihre Erweiterung über Ihrem class declaration platzieren sollten, z.

extension UIImage{

}

class ViewController: UIViewController, ARSCNViewDelegate {

}

Basierend auf dem in Ihrer Frage angegebenen Code hätten Sie also ungefähr Folgendes:

/// Creates A Black & White ScreenShot & Saves It To The Photo Album
@IBAction func changeTextColour(){

    //1. Create A Snapshot
    guard let snapShotImage = self.augmentedRealityView.snapshot().highContrastBlackAndWhiteFilter() else { return }

    //2. Save It The Photos Album
    UIImageWriteToSavedPhotosAlbum(snapShotImage, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)

}

///Calback To Check Whether The Image Has Been Saved
@objc func image(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {

    if let error = error {
        print("Error Saving ARKit Scene \(error)")
    } else {
        print("ARKit Scene Successfully Saved")
    }
}

Live-Rendering in Schwarzweiß: Mit dieser brillanten Antwort hier von diviaki war ich auch Mit den folgenden Methoden kann der gesamte Kamera-Feed in Schwarzweiß gerendert werden:

1. Registrieren Sie sich für das ARSessionDelegate wie folgt:

 augmentedRealitySession.delegate = self

2 .. Fügen Sie dann im folgenden Rückruf für Delegierte Folgendes hinzu:

 //-----------------------
 //MARK: ARSessionDelegate
 //-----------------------

 extension ViewController: ARSessionDelegate{

 func session(_ session: ARSession, didUpdate frame: ARFrame) {

        /*
        Full Credit To https://stackoverflow.com/questions/45919745/reliable-access-and-modify-captured-camera-frames-under-scenekit
        */

        //1. Convert The Current Frame To Black & White
        guard let currentBackgroundFrameImage = augmentedRealityView.session.currentFrame?.capturedImage,
              let pixelBufferAddressOfPlane = CVPixelBufferGetBaseAddressOfPlane(currentBackgroundFrameImage, 1) else { return }

        let x: size_t = CVPixelBufferGetWidthOfPlane(currentBackgroundFrameImage, 1)
        let y: size_t = CVPixelBufferGetHeightOfPlane(currentBackgroundFrameImage, 1)
        memset(pixelBufferAddressOfPlane, 128, Int(x * y) * 2)

      }

 }

Was den Kamera-Feed erfolgreich in Schwarzweiß rendert:

enter image description here

Filterelemente einer SCNS-Szene in Schwarzweiß:

Wie @Confused zu Recht sagte: Wenn Sie entschieden haben, dass das cameraFeed farbig sein soll, der Inhalt Ihres AR Experience jedoch schwarz-weiß sein soll, können Sie einen Filter direkt auf ein {{X2 anwenden }} mit der Eigenschaft filters, die einfach:

Ein Array von Core Image-Filtern, die auf den gerenderten Inhalt des Knotens angewendet werden sollen.

Nehmen wir zum Beispiel an, wir erstellen dynamisch 3 SCNNodes mit einem Sphere Geometry und können ein CoreImageFilter direkt auf diese anwenden, wie folgt:

/// Creates 3 Objects And Adds Them To The Scene (Rendering Them In GrayScale)
func createObjects(){

    //1. Create An Array Of UIColors To Set As The Geometry Colours
    let colours = [UIColor.red, UIColor.green, UIColor.yellow]

    //2. Create An Array Of The X Positions Of The Nodes
    let xPositions: [CGFloat] = [-0.3, 0, 0.3]

    //3. Create The Nodes & Add Them To The Scene
    for i in 0 ..< 3{

        let sphereNode = SCNNode()
        let sphereGeometry = SCNSphere(radius: 0.1)
        sphereGeometry.firstMaterial?.diffuse.contents = colours[i]
        sphereNode.geometry = sphereGeometry
        sphereNode.position = SCNVector3( xPositions[i], 0, -1.5)
        augmentedRealityView.scene.rootNode.addChildNode(sphereNode)

        //a. Create A Black & White Filter
        guard let blackAndWhiteFilter = CIFilter(name: "CIColorControls", withInputParameters: [kCIInputSaturationKey:0.0]) else { return }
        blackAndWhiteFilter.name = "bw"
        sphereNode.filters = [blackAndWhiteFilter]
        sphereNode.setValue(CIFilter(), forKeyPath: "bw")
    }

}

Was zu einem Ergebnis wie dem folgenden führt:

enter image description here

Eine vollständige Liste dieser Filter finden Sie unter: CoreImage-Filterreferenz

Beispielprojekt: Hier ist ein vollständiges Beispielprojekt, das Sie herunterladen und erkunden können für sich selbst.

Ich hoffe es hilft...

9
BlackMirrorz 6 Apr. 2019 im 00:56

Wenn Sie den Filter in Echtzeit anwenden möchten, verwenden Sie am besten {{X0 }}. Techniken werden für die Nachbearbeitung verwendet und ermöglichen es uns, einen SCNView Inhalt in mehreren Durchgängen zu rendern - genau das, was wir brauchen (erst eine Szene rendern, dann einen Effekt darauf anwenden).

Hier ist das Beispielprojekt.


Plist Setup

Zuerst müssen wir eine Technik in einer .plist - Datei beschreiben.

Hier ist ein Screenshot eines plist, den ich mir ausgedacht habe (zur besseren Visualisierung):

plist describing an SCNTechnique

Und hier ist die Quelle:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>sequence</key>
    <array>
        <string>apply_filter</string>
    </array>
    <key>passes</key>
    <dict>
        <key>apply_filter</key>
        <dict>
            <key>metalVertexShader</key>
            <string>scene_filter_vertex</string>
            <key>metalFragmentShader</key>
            <string>scene_filter_fragment</string>
            <key>draw</key>
            <string>DRAW_QUAD</string>
            <key>inputs</key>
            <dict>
                <key>scene</key>
                <string>COLOR</string>
            </dict>
            <key>outputs</key>
            <dict>
                <key>color</key>
                <string>COLOR</string>
            </dict>
        </dict>
    </dict>
</dict>

Das Thema von SCNTechnique s ist sehr umfangreich und ich werde nur schnell auf die Dinge eingehen, die wir für den vorliegenden Fall benötigen. Um wirklich zu verstehen, wozu sie in der Lage sind, empfehle ich, umfassende Dokumentation von Apple zu Techniken zu lesen .

Technikbeschreibung

passes ist ein Wörterbuch mit einer Beschreibung der Durchgänge, die ein SCNTechnique ausführen soll.

sequence ist ein Array, das eine Reihenfolge angibt, in der diese Durchläufe mit ihren Schlüsseln ausgeführt werden.

Sie geben hier nicht den Haupt-Render-Durchgang an (dh was auch immer gerendert wird, ohne SCNTechnique s anzuwenden) - es ist impliziert und auf die resultierende Farbe kann mit der Konstante COLOR zugegriffen werden (mehr dazu gleich).

Der einzige "zusätzliche" Durchgang (neben dem Hauptdurchgang), den wir durchführen werden, ist apply_filter, der Farben in Schwarzweiß umwandelt (er kann beliebig benannt werden, stellen Sie einfach sicher, dass er denselben Schlüssel hat in passes und sequence).

Nun zur Beschreibung des apply_filter Passes selbst.

Beschreibung des Renderpasses

metalVertexShader und metalFragmentShader - Namen von Metal Shader-Funktionen, die zum Zeichnen verwendet werden sollen.

draw definiert, was der Pass rendern soll. DRAW_QUAD steht für:

Rendern Sie nur ein Rechteck, das die gesamten Grenzen der Ansicht abdeckt. Verwenden Sie diese Option zum Zeichnen von Durchläufen, die Bildpuffer verarbeiten, die von früheren Durchläufen ausgegeben wurden.

Was grob gesagt bedeutet, dass wir ein einfaches "Bild" ohne Renderpass rendern werden.

inputs gibt Eingaberessourcen an, die wir in Shadern verwenden können. Wie ich bereits sagte, bezieht sich COLOR auf Farbdaten, die von einem Haupt-Renderpass bereitgestellt werden.

outputs gibt die Ausgaben an. Es kann color, depth oder stencil sein, aber wir benötigen nur eine color Ausgabe. Der Wert COLOR bedeutet, dass wir einfach ausgedrückt "direkt" auf dem Bildschirm rendern werden (im Gegensatz zum Rendern in Zwischenziele zum Beispiel).


Metall-Shader

Erstellen Sie eine .metal Datei mit folgendem Inhalt:

#include <metal_stdlib>
using namespace metal;
#include <SceneKit/scn_metal>

struct VertexInput {
    float4 position [[ attribute(SCNVertexSemanticPosition) ]];
    float2 texcoord [[ attribute(SCNVertexSemanticTexcoord0) ]];
};

struct VertexOut {
    float4 position [[position]];
    float2 texcoord;
};

// metalVertexShader
vertex VertexOut scene_filter_vertex(VertexInput in [[stage_in]])
{
    VertexOut out;
    out.position = in.position;
    out.texcoord = float2((in.position.x + 1.0) * 0.5 , (in.position.y + 1.0) * -0.5);
    return out;
}

// metalFragmentShader
fragment half4 scene_filter_fragment(VertexOut vert [[stage_in]],
                                    texture2d<half, access::sample> scene [[texture(0)]])
{
    constexpr sampler samp = sampler(coord::normalized, address::repeat, filter::nearest);
    constexpr half3 weights = half3(0.2126, 0.7152, 0.0722);

    half4 color = scene.sample(samp, vert.texcoord);
    color.rgb = half3(dot(color.rgb, weights));

    return color;
}

Beachten Sie, dass die Funktionsnamen für Fragment- und Vertex-Shader dieselben Namen sein sollten, die in der Datei plist im Passdeskriptor angegeben sind.

Weitere Informationen zur Bedeutung der Strukturen VertexInput und VertexOut finden Sie unter > SCNProgram Dokumentation.

Die angegebene Scheitelpunktfunktion kann so ziemlich in jedem DRAW_QUAD Renderpass verwendet werden. Grundsätzlich erhalten wir normalisierte Koordinaten des Bildschirmbereichs (auf die mit vert.texcoord im Fragment-Shader zugegriffen wird).

In der Fragmentfunktion geschieht die ganze "Magie". Dort können Sie die Textur bearbeiten, die Sie vom Hauptdurchgang erhalten haben. Mit diesem Setup können Sie möglicherweise eine Menge Filter / Effekte und mehr implementieren.

In unserem Fall habe ich eine Grundformel für die Entsättigung (Nullsättigung) verwendet, um die Schwarz-Weiß-Farben zu erhalten.


Schnelles Setup

Jetzt können wir all dies endlich in ARKit / SceneKit verwenden.

let plistName = "SceneFilterTechnique" // the name of the plist you've created

guard let url = Bundle.main.url(forResource: plistName, withExtension: "plist") else {
    fatalError("\(plistName).plist does not exist in the main bundle")
}

guard let dictionary = NSDictionary(contentsOf: url) as? [String: Any] else {
    fatalError("Failed to parse \(plistName).plist as a dictionary")
}

guard let technique = SCNTechnique(dictionary: dictionary) else {
    fatalError("Failed to initialize a technique using \(plistName).plist")
}

Und setzen Sie es einfach als technique des ARSCNView.

sceneView.technique = technique

Das ist es. Jetzt wird die gesamte Szene in Graustufen einschließlich gerendert, wenn Schnappschüsse aufgenommen werden.

enter image description here

10
Lësha Turkowski 29 Apr. 2018 im 20:27

Das snapshot Objekt sollte ein UIImage sein. Wenden Sie Filter auf dieses UIImage Objekt an, indem Sie das CoreImage Framework importieren, und wenden Sie dann Core Image-Filter darauf an. Sie sollten die Belichtungs- und Kontrollwerte auf dem Bild anpassen. Weitere Implementierungsdetails finden Sie unter Antwort. Unter iOS6 können Sie auch den Filter CIColorMonochrome verwenden, um den gleichen Effekt zu erzielen.

Hier ist der Apfel Dokumentation für alle verfügbaren Filter. Klicken Sie auf jeden der Filter, um die visuellen Effekte auf das Bild beim Anwenden des Filters zu erfahren.

Hier ist der schnelle 4-Code.

 func imageBlackAndWhite() -> UIImage?
    {
        if let beginImage = CoreImage.CIImage(image: self)
        {
            let paramsColor: [String : Double] = [kCIInputBrightnessKey: 0.0,
                                                  kCIInputContrastKey:   1.1,
                                                  kCIInputSaturationKey: 0.0]
            let blackAndWhite = beginImage.applyingFilter("CIColorControls", parameters: paramsColor)

            let paramsExposure: [String : AnyObject] = [kCIInputEVKey: NSNumber(value: 0.7)]
            let output = blackAndWhite.applyingFilter("CIExposureAdjust", parameters: paramsExposure)

            guard let processedCGImage = CIContext().createCGImage(output, from: output.extent) else {
                return nil
            }

            return UIImage(cgImage: processedCGImage, scale: self.scale, orientation: self.imageOrientation)
        }
        return nil
    }
0
Arun Balakrishnan 26 Apr. 2018 im 06:01

Dies ist möglicherweise der einfachste und schnellste Weg, dies zu tun:

Wenden Sie einen CoreImage-Filter auf die Szene an:

https://developer.apple.com/documentation/scenekit/scnnode/1407949-filters

Dieser Filter vermittelt einen sehr guten Eindruck von einem Schwarzweißfoto mit guten Übergängen durch Grautöne: https://developer.apple.com/library/content/documentation/GraphicsImaging/Reference/CoreImageFilterReference/index.html#//apple_/ doc / filter / ci / CIPhotoEffectMono

Sie können auch diesen verwenden und Ergebnisse erhalten, die sich leicht im Farbton verschieben lassen:

https://developer.apple.com/library/content/documentation/GraphicsImaging/Reference/CoreImageFilterReference/index.html#//apple_ref/doc/filter/ci/CIColorMonochrome

Und hier, auf Japanisch, ist der Beweis dafür, dass Filter und SceneKit ARKit zusammenarbeiten: http: //appleengine.hatenablog .com / entry / advent20171215

0
Confused 30 Apr. 2018 im 11:30