Fast-Neural-Style-TransferをiOS、MacOSで使えるようにする　CoreML変換

# Fast-Neural-Style-TransferをiOSで使う方法です
<img width="400" src="https://user-images.githubusercontent.com/23278992/155708074-ab651a7c-b882-40f1-9ce5-a94e80bac62d.jpg"> <img width="400" src="https://user-images.githubusercontent.com/23278992/155708089-ee888836-3f18-41a1-97fd-72e17e604c9a.jpg">
<img width="400" src="https://user-images.githubusercontent.com/23278992/155707184-403ad161-6354-4ce4-87d4-284e323b1261.jpg"> <img width="400" src="https://user-images.githubusercontent.com/23278992/155708401-f76481ad-1de7-4262-acc2-9dcb61c89784.jpg">
<img width="400" src="https://user-images.githubusercontent.com/23278992/155707199-b77b2583-c355-4406-bc9a-3248492df2c7.jpg"> <img width="400" src="https://user-images.githubusercontent.com/23278992/155706861-97e629a0-4322-4924-94ed-cb10c966bfb8.jpg">

### 変換済みモデル

https://github.com/john-rocky/CoreML-Models

### 変換スクリプト

https://colab.research.google.com/drive/1f6KOh9fuOmCP9vbNldqmyX6lqReGVtr4?usp=sharing

# 画風変換アプリを作ろう

Fast-Neural-Style-Transferは画風変換ができるモデルです。
iOSで使えば画像やビデオの画風変換アプリができます。

https://github.com/eriklindernoren/Fast-Neural-Style-Transfer

# CoreMLに変換するとiOS、MacOSデバイスで使える


# 方法

Pytorch版のリポジトリをクローン、リポジトリのリンクから事前トレーニング済みの重みをダウンロードします。

事前トレーニング済みの重みからモデルを復元します。

```python
from models import TransformerNet
import torch

transform = style_transform()
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
transformer = TransformerNet().to(device)
transformer.load_state_dict(torch.load("style-transfer-models/mosaic_10000.pth"))
transformer.eval()
```

変換します。

```python
import coremltools as ct
example_input = torch.randn(1,3,960,640).cuda()
traced_model = torch.jit.trace(transformer, example_input)

mlmodel = ct.convert(traced_model, inputs=[ct.ImageType(name="input", shape=example_input.shape,bias=[-0.485/0.229,-0.456/0.224,-0.406/0.225],scale=1.0/255.0/0.226)])
```

モデルの入力は正規化されているので、CoreMLの入力も同じように前処理されるように設定しています。

モデルからニューラールネットワークビルダーを作り、出力名を確認します。

```python
spec = mlmodel.get_spec()
builder = ct.models.neural_network.NeuralNetworkBuilder(spec=spec)
builder.spec.description
```

モデルの出力をRGB画像にするには、出力も前処理に合わせて非正規化する必要があります。
カラーチャネルごとにテンソルを前処理の逆にアクティベートし、再結合するレイヤーを追加します。
var_293は元のモデルの出力名です。

```python
builder.add_split(name="split", input_name="var_293", output_names=["split_1","split_2","split_3"])
builder.add_activation(name="activation_1",non_linearity="LINEAR",input_name="split_1",output_name="activation_out_1",params=[255*0.226,0.485*255])
builder.add_activation(name="activation_2",non_linearity="LINEAR",input_name="split_2",output_name="activation_out_2",params=[255*0.226,0.456*255])
builder.add_activation(name="activation_3",non_linearity="LINEAR",input_name="split_3",output_name="activation_out_3",params=[255*0.226,0.406*255])
builder.add_stack(name="stack", input_names=["activation_out_1","activation_out_2","activation_out_3"], output_name="stack_out", axis=0)
builder.add_squeeze(name="squeeze", input_name="stack_out", output_name="squeeze_out", axes = None, squeeze_all = True)
```

出力をRGB画像に設定します。

```python
from coremltools.proto import FeatureTypes_pb2 as ft
from coremltools.models.utils import save_spec

builder.spec.description.output.pop()
builder.spec.description.output.add()
output = builder.spec.description.output[0]
output.name = "squeeze_out"

output.type.imageType.colorSpace = ft.ImageFeatureType.ColorSpace.Value('RGB')
output.type.imageType.width = 640 
output.type.imageType.height = 960

save_spec(builder.spec, 'fast-style-transfer-mosaic.mlmodel')
```

![スクリーンショット 2022-02-25 10.54.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/2322dd78-26e6-b237-899e-aeea942ea151.png)

# Swiftで実行

```swift
let model = try! fast-style-transfer-mosaic(configuration: MLModelConfiguration()).model
let vnModel = try! VNCoreMLModel(for: model)
let request = VNCoreMLRequest(model: vnModel)
let handler = VNImageRequestHandler(ciImage: image, options: [:])
try! handler.perform([request])
guard let result = request.result?.first as? VNPixelBufferObservation else { return }
let pixelBuffer = result.pixelBuffer
```

# 動画に適用

![Feb-25-2022 21-39-17](https://user-images.githubusercontent.com/23278992/155716570-612681d7-732e-4ea0-bbeb-89ecca7db43f.gif)
🐣
****
フリーランスエンジニアです。
お仕事のご相談こちらまで
rockyshikoku@gmail.com

Core MLやARKitを使ったアプリを作っています。
機械学習／AR関連の情報を発信しています。

[Twitter](https://twitter.com/JackdeS11)
[Medium](https://medium.com/@rockyshikoku)