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, UIVisualEffectView
que 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:
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.
O seguinte é o resultado obtido após executar a segmentação em uma imagem de amostra:
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 VNCoreMLRequest
solicitaçã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, VNImageRequestHandler
crie :
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:
- 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
- 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,
arrayValue
Processe o resultado retornado em uma imagem em preto e branco:
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:
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:
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
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!