Como obter o efeito de imagem Depth Effect trazido pelo iOS 16

autor: fenol

0x01 Prefácio

O sistema iOS 16 nos trouxe um incrível efeito de tela de bloqueio na área de trabalho: Efeito de Profundidade. Ele pode usar uma imagem comum como plano de fundo e, ao mesmo tempo, pode cobrir alguns componentes da área de trabalho em um local apropriado para formar um efeito de profundidade de campo (como mostrado na figura abaixo).

Então, podemos obter um efeito semelhante em nosso próprio aplicativo? A princípio pensei que o iOS 16 tivesse adicionado um novo controle UIKit, UIVisualEffectViewque pode ser implementado com algumas linhas de APIs simples como , mas no final descobri que não existe. Se a imagem fornecida for várias imagens que foram divididas em camadas, a implementação é simplesmente colocar o controle do relógio no meio, como um biscoito recheado. No entanto, na prática, descobriu-se que definir uma única imagem baixada aleatoriamente da Internet como fundo da tela de bloqueio também pode obter esse efeito. Lembrando o álbum do sistema iOS 16 que pode segmentar e arrastar diretamente o assunto na foto após pressioná-lo novamente, acho que deve ter usado algum algoritmo de segmentação de imagem para separar o primeiro plano do fundo, obtendo assim uma imagem com várias camadas. image

0x02 Segmentação de Imagem (Segmentação de Imagem)

O algoritmo de segmentação de imagem mais clássico é o algoritmo de divisor de águas (Watershed). A imagem que ele segmenta é muito precisa e o processamento das bordas é muito bom, mas requer traços manuais nas posições aproximadas do primeiro plano e do plano de fundo (apenas um traço está bom ). , o último algoritmo separará automaticamente o primeiro plano e o plano de fundo), o que não se aplica aos requisitos totalmente automáticos deste artigo. Nos últimos anos, surgiram muitas conquistas em aprendizado de máquina, uma das quais é a segmentação de imagens totalmente automatizada. Com certeza, após uma pesquisa simples, descobri que a Apple forneceu um modelo pré-treinado.

Visite o site oficial de aprendizado de máquina da Apple developer.apple.com/machine-lea... para baixar o modelo treinado DeeplabV3 . Arraste o arquivo de modelo para o projeto Xcode e você poderá visualizar algumas informações sobre ele após selecioná-lo:

imagem

Na verdade, aqui nos concentramos principalmente na entrada e na saída do modelo. Clique na guia Previsões e você verá que o modelo requer a entrada de uma imagem de 513 x 513 e a saída é uma matriz bidimensional com um tipo de membro Int32 e um tamanho de 513 x 513. Cada valor representa a classificação de pontos de pixel da imagem correspondente. A razão pela qual os membros aqui são Int32 em vez de simples Bool é porque o modelo pode dividir a imagem em muitas partes diferentes, não apenas no primeiro plano e no plano de fundo. Na prática, descobrimos que um valor 0 pode ser considerado o plano de fundo e um valor diferente de 0 é o primeiro plano.

imagem

O seguinte é o resultado obtido após executar a segmentação em uma imagem de amostra:

imagem

Ele é dividido em dois valores de 0 e 15, que são background e foreground respectivamente.

treino 0x03

O modelo já existe, o plano de implantação está quase pronto, e o próximo passo é a prática específica.

Depois que o modelo for arrastado para o projeto Xcode, o Xcode gerará automaticamente uma classe para nós: DeepLabV3. Podemos criar diretamente uma instância dele sem nenhuma importação :

    lazy var model = try! DeepLabV3(configuration: {
        let config = MLModelConfiguration()
        config.allowLowPrecisionAccumulationOnGPU = true
        config.computeUnits = .cpuAndNeuralEngine
        return config
    }())
复制代码

Em seguida, use esta instância para criar uma VNCoreMLRequestsolicitação para analisar a imagem por meio do mecanismo de aprendizado de máquina e obtenha o resultado no retorno de chamada:

    lazy var request = VNCoreMLRequest(model: try! VNCoreMLModel(for: model.model)) { [unowned self] request, error in
        if let results = request.results as? [VNCoreMLFeatureValueObservation] {
            // 最终的分割结果在 arrayValue 中
            if let feature = results.first?.featureValue, let arrayValue = feature.multiArrayValue {
                let width = arrayValue.shape[0].intValue
                let height = arrayValue.shape[1].intValue
                let stride = arrayValue.strides[0].intValue
                // ...
            }
            
        }
    }
复制代码

Por fim, VNImageRequestHandlercrie :

    private func segment() {
        if let image = self.imageView.image {
            imageSize = image.size
            DispatchQueue.global().async { [unowned self] in
                self.request.imageCropAndScaleOption = .scaleFill
                let handler = VNImageRequestHandler(cgImage: image.resize(to: .init(width: 513, height: 513)).cgImage!)
                try? handler.perform([self.request])
            }
        }
    }
复制代码

Perceber:

  1. O retorno de chamada da solicitação e o código que o manipulador inicia a solicitação estão no mesmo thread, aguardando o resultado de forma síncrona, portanto, é melhor despachar para a operação de subthread aqui
  2. A solicitação precisa definir imageCropAndScaleOption como .scallFill, caso contrário, cortará automaticamente a parte do meio por padrão e você obterá resultados inesperados

Digite a seguinte imagem de amostra,

arrayValueProcesse o resultado retornado em uma imagem em preto e branco:

imagem

Foi considerado bastante preciso. Claro, se você quiser usá-lo como uma máscara no código, você deve tratá-lo como uma imagem com um fundo totalmente transparente e um primeiro plano opaco:

imagem

Por fim, colocamos a imagem original na camada inferior, os demais controles no meio, e a imagem original + visualização da máscara na camada superior, formando o efeito final:

imagem

O princípio real por trás disso é o biscoito recheado:

Mais algumas renderizações:

pós-escrito 0x04

Claro, este modelo não é uma panaceia, e ainda existem limitações em aplicações específicas.Para fotos com pessoas, pode ser melhor segmentado, mas para fotos de paisagem como cenas grandes, pode não ser possível segmentá-lo. Uma demonstração deste artigo pode ser encontrada no Github .

Referências

  1. developer.apple.com/documentati…
  2. www.appcoda.com.tw/vision-pers…
  3. enlight.nyc/projects/im…

Este artigo foi publicado pelo NetEase Cloud Music Technology Team. Qualquer forma de reimpressão sem autorização é proibida. Recrutamos vários cargos técnicos durante todo o ano. Se você vai mudar de emprego e gosta de música em nuvem, junte-se a nós em grp.music-fe(at)corp.netease.com!

Acho que você gosta

Origin juejin.im/post/7197608023430283319
Recomendado
Clasificación