반응형

TensorFlow Lite는 구글이 개발한 TensorFlow의 경량 버전으로, 모바일 기기 및 임베디드 시스템에서 딥러닝 모델을 실행하기 위해 최적화된 라이브러리입니다. TensorFlow Lite는 딥러닝 모델의 크기와 성능을 최적화하여 모바일 기기에서도 효율적으로 동작할 수 있도록 합니다.

< TensorFlow Lite 특징 >

경량화: TensorFlow Lite는 모바일 기기와 임베디드 시스템의 제한된 자원을 고려하여 딥러닝 모델의 크기와 연산량을 최적화합니다. 이를 통해 모바일 환경에서도 효율적으로 모델을 실행할 수 있습니다.
하드웨어 가속: TensorFlow Lite는 하드웨어 가속을 지원하여 모바일 기기의 GPU, DSP 등을 활용하여 딥러닝 연산을 가속화할 수 있습니다. 이로 인해 모델의 추론 속도가 향상됩니다.
모바일 지원: TensorFlow Lite는 Android 및 iOS와 같은 주요 모바일 플랫폼에서 사용할 수 있도록 지원합니다. 또한, 임베디드 보드와 같은 다양한 장치에서도 실행 가능합니다.
모델 컨버터: TensorFlow Lite는 TensorFlow 모델을 TensorFlow Lite 모델로 변환하는 컨버터를 제공합니다. 이를 통해 기존에 훈련된 TensorFlow 모델을 모바일에서 실행 가능한 형식으로 변환할 수 있습니다.
지원되는 모델 형식: TensorFlow Lite는 다양한 모델 형식을 지원합니다. TensorFlow 모델을 변환하여 사용할 수 있으며, TensorFlow 모델 최적화 도구를 사용하여 모델 크기를 최소화할 수도 있습니다.
On-device 추론: TensorFlow Lite는 모바일 기기에서 모델을 로드하고 추론을 직접 수행할 수 있습니다. 이를 통해 모바일 애플리케이션에서 실시간으로 딥러닝 모델을 활용할 수 있습니다.
Custom 모델 지원: TensorFlow Lite는 사용자 정의 연산자를 지원하므로 사용자가 원하는 연산자를 직접 구현하고 모델에 통합할 수 있습니다.
TensorFlow Lite는 모바일 애플리케이션에서 딥러닝을 활용하고자 하는 개발자들에게 매우 유용한 도구이며, 경량화된 딥러닝 모델을 사용하여 모바일 기기에서 실시간 추론을 구현하는 데에 적합합니다. 또한, TensorFlow Lite는 TensorFlow와 통합되어 있어 TensorFlow에서 훈련한 모델을 간편하게 모바일 환경에서 실행할 수 있도록 지원합니다.

 

//
//  MNIST - TENSORFLOWLITE
//
//  Created by netcanis on 2023/07/20.
//

import tensorflow as tf
import dataset_loader
import model_tester


# Load MNIST data
training_images, training_labels, test_images, test_labels = dataset_loader.load_dataset("data/MNIST")

# Reshape the data
# 배열의 차원을 변경하여 크기를 자동으로 변경한다. (-1은 해당 차원의 크기를 자동으로 조정하라는 뜻)
training_images = training_images.reshape(-1, 28, 28, 1)
test_images = test_images.reshape(-1, 28, 28, 1)

# Print the image shapes
# reshape - Training Images shape: (60000, 28, 28, 1)
# reshape - Test Images shape: (10000, 28, 28, 1)
print("reshape - Training Images shape:", training_images.shape)
print("reshape - Test Images shape:", test_images.shape)

# Normalize the pixel values
training_images = training_images / 255.0
test_images = test_images / 255.0

# Assign images and labels to x_train, y_train, x_test, y_test
x_train, y_train = training_images, training_labels
x_test, y_test = test_images, test_labels




#
# TensorFlow Lite 모델 저장 (tflite)
#

# TensorFlow Keras model
keras_model = tf.keras.Sequential([
    tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)),
    tf.keras.layers.MaxPooling2D((2, 2)),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(64, activation='relu'),
    tf.keras.layers.Dense(10, activation='softmax') # 0~9 총 10개 클래스
])

# Train the model
keras_model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])
keras_model.fit(x_train, y_train, epochs=5, validation_data=(x_test, y_test))

# Convert the model to TensorFlow Lite format
converter = tf.lite.TFLiteConverter.from_keras_model(keras_model)
tflite_model = converter.convert()

# Save the TensorFlow Lite model
with open('tflite_mnist_model.tflite', 'wb') as file:
    file.write(tflite_model)

print("Complete Save the TensorFlow Lite model.")




#
# TEST
#

model_file = "tflite_mnist_model.tflite"
model_tester.test_model("data/MNIST", model_file)

# Error rate: 1.44%

 

2023.07.19 - [AI] - MNIST 데이터셋 다운로드

2023.07.19 - [AI] - MNIST 데이터셋을 이미지 파일로 복원

2023.07.19 - [AI] - MNIST 데이터셋 로더

2023.07.19 - [AI] - MNIST 모델 테스터

2023.07.19 - [AI] - MINST - SVC(Support Vector Classifier)

2023.07.19 - [AI] - MNIST - RandomForestClassifier

2023.07.19 - [AI] - MNIST - Keras

2023.07.19 - [AI] - MNIST - TensorFlowLite

 

 

 

반응형

'개발 > AI,ML,ALGORITHM' 카테고리의 다른 글

SARSA  (0) 2023.08.28
Q-learning  (0) 2023.08.28
MNIST - Keras  (0) 2023.07.19
MNIST - RandomForestClassifier  (0) 2023.07.19
MINST - SVC(Support Vector Classifier)  (0) 2023.07.19
블로그 이미지

SKY STORY

,
반응형

Keras 모델 학습

Keras는 딥러닝 모델을 쉽게 구축하고 훈련할 수 있도록 설계된 오픈 소스 딥러닝 라이브러리입니다. François Chollet이 개발한 Keras는 사용자 친화적인 API를 제공하여 TensorFlow, Theano, CNTK 등의 백엔드 엔진에서 실행할 수 있습니다. 하지만 2019년 9월 기준으로 TensorFlow 2.0부터는 Keras가 TensorFlow의 공식 고수준 API로 통합되어 TensorFlow의 일부가 되었습니다.

 

< Keras 특징 >

사용자 친화적인 API: Keras는 직관적이고 간결한 API를 제공하여 신경망 모델을 쉽게 설계할 수 있도록 도와줍니다. 따라서 딥러닝 경험이 적은 사용자도 비교적 쉽게 모델을 구축하고 수정할 수 있습니다.
모듈화: Keras는 레이어, 손실 함수, 최적화 알고리즘 등을 모듈화하여 개별 구성 요소들을 조합하여 모델을 구축할 수 있도록 합니다. 이를 통해 모델의 재사용성이 높아집니다.
다양한 백엔드 지원: Keras는 다양한 딥러닝 백엔드 엔진을 지원합니다. 초기에는 Theano와 TensorFlow를 지원했으며, 현재는 TensorFlow가 공식 백엔드로 사용되고 있습니다.
모듈화된 구조: Keras는 레이어로 구성된 모델을 생성하는 함수형 API와 순차적으로 레이어를 쌓는 Sequential API를 제공합니다. 이를 통해 간단한 모델에서부터 복잡한 모델까지 다양한 구조의 신경망을 쉽게 만들 수 있습니다.
커뮤니티와 생태계: Keras는 활발한 커뮤니티와 풍부한 생태계를 갖추고 있습니다. 이로 인해 다양한 확장 기능, 사전 훈련된 모델, 유틸리티 등을 쉽게 활용할 수 있습니다.
분산 훈련 및 모델 배포: Keras는 TensorFlow를 백엔드로 사용하기 때문에 TensorFlow의 기능을 활용하여 분산 훈련과 모델 배포를 지원합니다.
가벼운 라이브러리: Keras는 간단하고 가벼운 딥러닝 라이브러리이기 때문에 처음 딥러닝을 배우는 데 적합하며, 초보자와 중급 사용자 모두에게 추천됩니다.
Keras는 이러한 특징들로 인해 딥러닝 모델 개발에 있어서 많은 사용자들에게 인기가 있으며, 다양한 응용 분야에서 활용되고 있습니다. TensorFlow를 기반으로 하기 때문에 TensorFlow와의 호환성이 뛰어난 것도 Keras의 장점 중 하나입니다.


//
//  MNIST - KERAS
//
//  Created by netcanis on 2023/07/20.
//

import numpy as np
import tensorflow as tf
import dataset_loader
import model_tester

from keras.models import load_model


# Load MNIST data
training_images, training_labels, test_images, test_labels = dataset_loader.load_dataset("data/MNIST")

# Reshape the data
# 배열의 차원을 변경하여 크기를 자동으로 변경한다. (-1은 해당 차원의 크기를 자동으로 조정하라는 뜻)
training_images = training_images.reshape(-1, 28, 28, 1)
test_images = test_images.reshape(-1, 28, 28, 1)

# Print the image shapes
# reshape - Training Images shape: (60000, 28, 28, 1)
# reshape - Test Images shape: (10000, 28, 28, 1)
print("reshape - Training Images shape:", training_images.shape)
print("reshape - Test Images shape:", test_images.shape)

# Normalize the pixel values
training_images = training_images / 255.0
test_images = test_images / 255.0

# Assign images and labels to x_train, y_train, x_test, y_test
x_train, y_train = training_images, training_labels
x_test, y_test = test_images, test_labels




#
# Keras model 파일(keras) 저장
#

# TensorFlow Keras model
keras_model = tf.keras.Sequential([
    tf.keras.layers.Reshape(target_shape=(28, 28) + (1,), input_shape=(28, 28)),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(128, activation='relu'),
    tf.keras.layers.Dense(10, activation='softmax') # 0~9 총 10개 클래스
])

# Train the model
keras_model.compile(optimizer='adam', 
                    loss='sparse_categorical_crossentropy', 
                    metrics=['accuracy'])
keras_model.fit(x_train, y_train, epochs=5, batch_size=32, validation_data=(x_test, y_test))

# Save the Keras model
keras_model.save('keras_mnist_model.h5')

print("Complete Save the Keras model.")




#
# TEST
#

model_file = "keras_mnist_model.h5"
model_tester.test_model("data/MNIST", model_file)

# Error rate: 2.26%

 

2023.07.19 - [AI] - MNIST 데이터셋 다운로드

2023.07.19 - [AI] - MNIST 데이터셋을 이미지 파일로 복원

2023.07.19 - [AI] - MNIST 데이터셋 로더

2023.07.19 - [AI] - MNIST 모델 테스터

2023.07.19 - [AI] - MINST - SVC(Support Vector Classifier)

2023.07.19 - [AI] - MNIST - RandomForestClassifier

2023.07.19 - [AI] - MNIST - Keras

2023.07.19 - [AI] - MNIST - TensorFlowLite

 

 

반응형

'개발 > AI,ML,ALGORITHM' 카테고리의 다른 글

Q-learning  (0) 2023.08.28
MNIST - TensorFlowLite  (0) 2023.07.19
MNIST - RandomForestClassifier  (0) 2023.07.19
MINST - SVC(Support Vector Classifier)  (0) 2023.07.19
MNIST 모델 테스터  (0) 2023.07.19
블로그 이미지

SKY STORY

,
반응형

이번에는 RandomForestClassifier 모델로 학습해 보았다.

 

RandomForestClassifier는 앙상블 학습(Ensemble Learning) 기법 중 하나인 랜덤 포레스트(Random Forest)를 구현한 분류(Classification) 모델입니다. 
랜덤 포레스트는 여러 개의 의사결정 트리(Decision Tree)를 결합하여 강력한 예측 모델을 만드는 알고리즘입니다.

< 랜덤 포레스트 특징 >

앙상블 학습: 랜덤 포레스트는 여러 개의 의사결정 트리를 동시에 학습하고 이들의 예측 결과를 결합하여 최종 예측을 수행합니다. 이렇게 여러 개의 모델을 결합하는 앙상블 학습은 일반적으로 단일 모델보다 더욱 강력한 예측 성능을 제공합니다.
의사결정 트리: 랜덤 포레스트의 기본 모델로 사용되는 의사결정 트리는 데이터의 특성을 기반으로 하여 분류 작업을 수행하는 모델입니다. 의사결정 트리는 특정 기준에 따라 데이터를 분할하여 예측을 수행하는 방식으로 동작합니다.
랜덤성: 랜덤 포레스트는 의사결정 트리 학습 시에 랜덤성을 도입합니다. 이는 데이터의 일부 특성을 임의로 선택하거나 데이터를 부트스트랩 샘플링하는 등의 방법으로 랜덤성을 추가함으로써 모델의 다양성을 높입니다. 이는 과적합(Overfitting)을 방지하고 모델의 일반화 성능을 향상시킵니다.
랜덤 포레스트는 다양한 분야에서 활용되며, 데이터 분류, 패턴 인식, 텍스트 분석, 이미지 분류 등 다양한 문제에 적용될 수 있습니다. 또한, 특성 중요도를 제공하여 어떤 특성이 예측에 가장 중요한 역할을 하는지를 확인할 수도 있습니다.

 

//
//  MNIST - RANDOMFORESTCLASSIFIER
//
//  Created by netcanis on 2023/07/20.
//

import cv2
import os
import numpy as np
import pickle
import dataset_loader
import model_tester

from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC


# Load MNIST data
training_images, training_labels, test_images, test_labels = dataset_loader.load_dataset("data/MNIST")

# Reshape the data
# 배열의 차원을 변경하여 크기를 자동으로 변경한다. (-1은 해당 차원의 크기를 자동으로 조정하라는 뜻)
training_images = np.reshape(training_images, (len(training_images), -1))
test_images = np.reshape(test_images, (len(test_images), -1))

# Print the image shapes
# reshape - Training Images shape: (60000, 784)
# reshape - Test Images shape: (10000, 784)
print("reshape - Training Images shape:", training_images.shape)
print("reshape - Test Images shape:", test_images.shape)
 
# Assign images and labels to x_train, y_train, x_test, y_test
x_train, y_train = training_images, training_labels
x_test, y_test = test_images, test_labels




#
# RandomForestClassifier 파일 저장.
#

# Train the model
model = RandomForestClassifier()
model.fit(x_train, y_train)

# Evaluate the model
score = model.score(x_test, y_test)
print('Accuracy:', score)

# Save the pkl model
with open("rfc_mnist_model.pkl", "wb") as file:
    pickle.dump(model, file)

print("Save the rfc_mnist_model.pkl.")




#
# TEST
#

model_file = "rfc_mnist_model.pkl"
model_tester.test_model("data/MNIST", model_file)

# Error rate: 3.09%

 

2023.07.19 - [AI] - MNIST 데이터셋 다운로드

2023.07.19 - [AI] - MNIST 데이터셋을 이미지 파일로 복원

2023.07.19 - [AI] - MNIST 데이터셋 로더

2023.07.19 - [AI] - MNIST 모델 테스터

2023.07.19 - [AI] - MINST - SVC(Support Vector Classifier)

2023.07.19 - [AI] - MNIST - RandomForestClassifier

2023.07.19 - [AI] - MNIST - Keras

2023.07.19 - [AI] - MNIST - TensorFlowLite

 

 

 

 

반응형

'개발 > AI,ML,ALGORITHM' 카테고리의 다른 글

MNIST - TensorFlowLite  (0) 2023.07.19
MNIST - Keras  (0) 2023.07.19
MINST - SVC(Support Vector Classifier)  (0) 2023.07.19
MNIST 모델 테스터  (0) 2023.07.19
MNIST 데이터셋 로더  (0) 2023.07.19
블로그 이미지

SKY STORY

,
반응형

MNIST 이미지 셋 파일들을 이용하여  SVC 모델로 학습을 시켜보았다.

SVC(Support Vector Classifier)는 SVM(Support Vector Machine) 알고리즘을 기반으로 한 분류(Classification) 모델입니다. 
서포트 벡터 머신은 지도 학습(Supervised Learning)에서 주로 사용되며, 데이터를 분류하는 경계를 찾는 데에 특화되어 있습니다.

< SVC 특징 >

최대 마진 분류: SVC는 데이터를 분류하는 결정 경계를 찾을 때, 가능한 한 최대 마진(Margin)을 가지도록 합니다. 
마진은 결정 경계와 가장 가까운 데이터 샘플들 간의 거리를 의미하며, 이를 최대화함으로써 일반화 성능을 향상시킬 수 있습니다.
커널 기법: SVC는 비선형적인 데이터를 처리하기 위해 커널(Kernel) 기법을 사용합니다. 
커널은 데이터를 고차원 특징 공간으로 매핑하여 선형적으로 분리할 수 있도록 합니다. 
대표적인 커널 함수로는 선형 커널, 다항식 커널, 가우시안(RBF) 커널 등이 있습니다.
서포트 벡터: SVC는 분류 결정 경계에 가장 가까이 위치한 데이터 샘플들을 서포트 벡터(Support Vector)라고 부릅니다. 
이들 데이터 샘플들은 분류 결정에 영향을 미치는 주요한 역할을 합니다. 
SVC는 이 서포트 벡터들을 효율적으로 찾아내어 분류를 수행합니다.
SVC는 이진 분류(Binary Classification)와 다중 클래스 분류(Multi-Class Classification) 문제에 모두 사용될 수 있습니다. 
또한, SVM은 분류 뿐만 아니라 회귀(Regression), 이상치 탐지(Outlier Detection), 
차원 축소(Dimensionality Reduction) 등 다양한 문제에도 적용될 수 있습니다.

//
//  MINST - SVC(SUPPORT VECTOR CLASSIFIER)
//
//  Created by netcanis on 2023/07/20.
//

import numpy as np
import pickle
import dataset_loader
import model_tester

from sklearn.model_selection import train_test_split
from sklearn.svm import SVC


# Load MNIST data
training_images, training_labels, test_images, test_labels = dataset_loader.load_dataset("data/MNIST")

# Reshape the data
# 배열의 차원을 변경하여 크기를 자동으로 변경한다. (-1은 해당 차원의 크기를 자동으로 조정하라는 뜻)
training_images = np.reshape(training_images, (len(training_images), -1))
test_images = np.reshape(test_images, (len(test_images), -1))

# Print the image shapes
# reshape - Training Images shape: (60000, 784)
# reshape - Test Images shape: (10000, 784)
print("reshape - Training Images shape:", training_images.shape)
print("reshape - Test Images shape:", test_images.shape)
 
# Assign images and labels to x_train, y_train, x_test, y_test
x_train, y_train = training_images, training_labels
x_test, y_test = test_images, test_labels




#
# SVC(Support Vector Classifier) 파일 저장.
#

print("Training in progress... Please wait.")

# Train the model
# 'verbose=True'를 설정하여 진행상태 로그 출력.
model = SVC(verbose=True)
model.fit(x_train, y_train)

# Evaluate the model
score = model.score(x_test, y_test)
print('Accuracy:', score)

# Save the model (Support Vector Machines)
with open("svm_mnist_model.pkl", "wb") as file:
    pickle.dump(model, file)
    
print("Save the vm_mnist_model.pkl.")




#
# TEST
#

model_file = "svm_mnist_model.pkl"
model_tester.test_model("data/MNIST", model_file)

# Error rate: 2.08%

 


2023.07.19 - [AI] - MNIST 데이터셋 다운로드

2023.07.19 - [AI] - MNIST 데이터셋을 이미지 파일로 복원

2023.07.19 - [AI] - MNIST 데이터셋 로더

2023.07.19 - [AI] - MNIST 모델 테스터

2023.07.19 - [AI] - MINST - SVC(Support Vector Classifier)

2023.07.19 - [AI] - MNIST - RandomForestClassifier

2023.07.19 - [AI] - MNIST - Keras

2023.07.19 - [AI] - MNIST - TensorFlowLite

 

 

반응형

'개발 > AI,ML,ALGORITHM' 카테고리의 다른 글

MNIST - Keras  (0) 2023.07.19
MNIST - RandomForestClassifier  (0) 2023.07.19
MNIST 모델 테스터  (0) 2023.07.19
MNIST 데이터셋 로더  (0) 2023.07.19
MNIST 데이터셋을 이미지 파일로 복원  (0) 2023.07.19
블로그 이미지

SKY STORY

,
반응형

MNIST 모델을 머신러닝을 통해 학습하여 모델 파일을

저장하게 되는데 이 모델파일에 대한 테스트를 진행 하고자

테스터 함수를 만들게 되었다.

이 함수는 test_set 폴더에 있는 이미지들을 이용하여

테스트를 진행하고  error rate를 계산하여 출력해 보았다.

 

//
//  MNIST 모델 테스터
//
//  Created by netcanis on 2023/07/20.
//

import cv2
import os
import numpy as np
import pickle
import tensorflow as tf
from keras.models import load_model


def test_model(path, model_file):
    print("Testing in progress...")
    
    # 데이터셋 경로
    test_set_path = os.path.join(path, 'test_set')
    
    total_samples = 0
    error_count = 0
    
    # Load the model from file
    with open(model_file, "rb") as file:
        if model_file.endswith('.h5'):
            loaded_model = load_model(model_file)
        elif model_file.endswith('.tflite'):
            interpreter = tf.lite.Interpreter(model_path = model_file)
            interpreter.allocate_tensors()
            # Get the input and output details
            input_details = interpreter.get_input_details()
            output_details = interpreter.get_output_details()
        else:
            loaded_model = pickle.load(file)
        
        
    # Load the images from the test set
    for digit_folder in os.listdir(test_set_path):
        if os.path.isdir(os.path.join(test_set_path, digit_folder)):
            label = int(digit_folder)
            for index, image_file in enumerate(os.listdir(os.path.join(test_set_path, digit_folder))):
                if image_file.endswith('.png') or image_file.endswith('.jpg'):

                    image = cv2.imread(os.path.join(test_set_path, digit_folder, image_file))
                    if path.endswith('/credit_card'):
                        image = cv2.resize(image, (32, 32))
                    else: # '/MNIST'
                        image = cv2.resize(image, (28, 28))

                    # Convert color image to grayscale if necessary
                    if image.shape[2] > 1:
                        image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
                            
                    if model_file.endswith('.h5'):
                        image = image.reshape(1, 28, 28, 1) # 배열을 4차원으로 변경.
                        image = image / 255.0
                        # Predict the label for the image
                        predicted_label = loaded_model.predict(image)
                        # Get the predicted class
                        predicted_class = np.argmax(predicted_label)
                    elif model_file.endswith('.tflite'):
                        image = np.expand_dims(image, axis=0)  # Add batch dimension
                        image = np.expand_dims(image, axis=3)  # Add channel dimension
                        image = image.astype(np.float32) / 255.0
                        # Set the input tensor
                        interpreter.set_tensor(input_details[0]['index'], image)
                        # Run the inference
                        interpreter.invoke()
                        # Get the output tensor
                        output_tensor = interpreter.get_tensor(output_details[0]['index'])
                        # Get the predicted class
                        predicted_class = np.argmax(output_tensor)
                    else: # SVM, RandomForestClassifier ('.pkl')                       
                        # Reshape the data
                        image = image.reshape(1, -1)
                        # Predict the label for the image
                        predicted_label = loaded_model.predict(image)
                        # Get the predicted class
                        predicted_class = predicted_label[0]
                    
                    # error 
                    if predicted_class != label:
                        error_count += 1
                        print(f"Prediction for {index} - {label}: {predicted_class}")
                    
                    total_samples += 1


    # Print error rate
    error_rate = (error_count / total_samples) * 100
    print(f"Error rate: {error_rate:.2f}%")

 

사용 방법은 다음과 같다.

import mnist_model_tester

model_file = "svm_mnist_model.pkl"
mnist_model_tester.test_mnist_model("data/MNIST", model_file)

or

from mnist_model_tester import test_mnist_model

model_file = "svm_mnist_model.pkl"
test_mnist_model("data/MNIST", model_file)

 

2023.07.19 - [AI] - MNIST 데이터셋 다운로드

2023.07.19 - [AI] - MNIST 데이터셋을 이미지 파일로 복원

2023.07.19 - [AI] - MNIST 데이터셋 로더

2023.07.19 - [AI] - MNIST 모델 테스터

2023.07.19 - [AI] - MINST - SVC(Support Vector Classifier)

2023.07.19 - [AI] - MNIST - RandomForestClassifier

2023.07.19 - [AI] - MNIST - Keras

2023.07.19 - [AI] - MNIST - TensorFlowLite

 

 

반응형

'개발 > AI,ML,ALGORITHM' 카테고리의 다른 글

MNIST - RandomForestClassifier  (0) 2023.07.19
MINST - SVC(Support Vector Classifier)  (0) 2023.07.19
MNIST 데이터셋 로더  (0) 2023.07.19
MNIST 데이터셋을 이미지 파일로 복원  (0) 2023.07.19
MNIST 데이터셋 다운로드  (0) 2023.07.19
블로그 이미지

SKY STORY

,
반응형

이후 구현할 머신러닝 모델 구현을 위해 데이터 셋 로더 함수를 만들어 보았다.
이 함수는 데이터 셋 이미지들(트레이닝, 테스트)을 로딩하여 이미지, 라벨(해당 이미지 숫자) 리스트를 반환하도록 만들어 보았다.

//
//  MNIST 데이터셋 로더
//
//  Created by netcanis on 2023/07/20.
//


import cv2
import os
import numpy as np

#='data/MNIST'
def load_dataset(path):
    # 데이터셋 경로
    training_set_path = os.path.join(path, 'training_set')
    test_set_path = os.path.join(path, 'test_set')
    
    # Load the images from the training set
    training_images = []
    training_labels = []
    for digit_folder in os.listdir(training_set_path):
        if os.path.isdir(os.path.join(training_set_path, digit_folder)):
            label = int(digit_folder)
            for index, image_file in enumerate(os.listdir(os.path.join(training_set_path, digit_folder))):
                if image_file.endswith('.png') or image_file.endswith('.jpg'):
                    image = cv2.imread(os.path.join(training_set_path, digit_folder, image_file))
                    image = cv2.resize(image, (28, 28))

                    # Convert color image to grayscale if necessary
                    if image.shape[2] > 1:
                        image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

                    # Print image path
                    print(str(index).zfill(5) + " " + os.path.join(training_set_path, digit_folder, image_file))

                    training_images.append(image)
                    training_labels.append(label)

    # Load the images from the test set
    test_images = []
    test_labels = []
    for digit_folder in os.listdir(test_set_path):
        if os.path.isdir(os.path.join(test_set_path, digit_folder)):
            label = int(digit_folder)
            for index, image_file in enumerate(os.listdir(os.path.join(test_set_path, digit_folder))):
                if image_file.endswith('.png') or image_file.endswith('.jpg'):
                    image = cv2.imread(os.path.join(test_set_path, digit_folder, image_file))
                    image = cv2.resize(image, (28, 28))

                    # Convert color image to grayscale if necessary
                    if image.shape[2] > 1:
                        image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

                    # Print image path
                    print(str(index).zfill(5) + " " + os.path.join(test_set_path, digit_folder, image_file))

                    test_images.append(image)
                    test_labels.append(label)

    # Convert lists to numpy arrays
    training_images = np.array(training_images)
    training_labels = np.array(training_labels)
    test_images = np.array(test_images)
    test_labels = np.array(test_labels)

    # Print the image shapes
    # Training Images shape: (60000, 28, 28) or (20000, 32, 32)
    # Test Images shape: (10000, 28, 28) or (4000, 32, 32)
    print("Training Images shape:", training_images.shape)
    print("Test Images shape:", test_images.shape)

    return training_images, training_labels, test_images, test_labels

 
 
2023.07.19 - [AI] - MNIST 데이터셋 다운로드

2023.07.19 - [AI] - MNIST 데이터셋을 이미지 파일로 복원

2023.07.19 - [AI] - MNIST 데이터셋 로더

2023.07.19 - [AI] - MNIST 모델 테스터

2023.07.19 - [AI] - MINST - SVC(Support Vector Classifier)

2023.07.19 - [AI] - MNIST - RandomForestClassifier

2023.07.19 - [AI] - MNIST - Keras

2023.07.19 - [AI] - MNIST - TensorFlowLite

 

 


 
 
 
 

반응형

'개발 > AI,ML,ALGORITHM' 카테고리의 다른 글

MINST - SVC(Support Vector Classifier)  (0) 2023.07.19
MNIST 모델 테스터  (0) 2023.07.19
MNIST 데이터셋을 이미지 파일로 복원  (0) 2023.07.19
MNIST 데이터셋 다운로드  (0) 2023.07.19
Neural Network (XOR)  (0) 2022.11.18
블로그 이미지

SKY STORY

,
반응형

data/MNIST/row/ 폴더에 다운로드된 데이터셋 정보를 이용하여 image파일로 복원하고 해당 숫자(0-9)별로 폴더를 생성하고 각 숫자에 맞게 폴더내에 저장하도록 구현해 보았다.
labels.txt에는 각각의 이미지에대한 숫자 값을 기록하였다.

//
//  MNIST 데이터셋을 이미지 파일로 복원
//
//  Created by netcanis on 2023/07/20.
//

import matplotlib.pyplot as plt
import gzip
import numpy as np
import os



# 파일 압축 해제 함수 
def decompress_file(compressed_file, decompressed_file):
    with gzip.open(compressed_file, 'rb') as f_in, open(decompressed_file, 'wb') as f_out:
        f_out.write(f_in.read())



'''
================= training_set =================
'''

# 이미지 개수 및 크기 추출
training_num_images = 60000  # 예시로는 60,000개의 이미지가 있는 것으로 가정
image_size = 28  # 이미지 크기는 28x28 픽셀

# 압축 해제할 파일 경로 (training_set)
training_image_compressed_file = 'data/MNIST/raw/train-images-idx3-ubyte.gz'
training_label_compressed_file = 'data/MNIST/raw/train-labels-idx1-ubyte.gz'

# 압축 해제된 파일 경로 (training_set)
training_image_decompressed_file = 'data/MNIST/raw/train-images-idx3-ubyte'
training_label_decompressed_file = 'data/MNIST/raw/train-labels-idx1-ubyte'


# 이미지 저장 폴더 경로
training_set_folder = 'data/MNIST/training_set'

# 라벨 저장 파일 경로
training_label_file = 'data/MNIST/training_set/labels.txt'


# 이미지 파일 압축 해제 (training_set)
decompress_file(training_image_compressed_file, training_image_decompressed_file)

# 라벨 파일 압축 해제 (training_set)
decompress_file(training_label_compressed_file, training_label_decompressed_file)


# 이미지 저장 폴더 생성 (training_set)
if not os.path.exists(training_set_folder):
    os.makedirs(training_set_folder)


# 라벨 데이터를 numpy 배열로 변환 (training_set)
with open(training_label_decompressed_file, 'rb') as f:
    # 헤더 부분은 건너뛰기
    f.read(8)
    # 라벨 데이터 읽기
    buf = f.read()

# 라벨 배열 형태로 변환 (training_set)
training_labels = np.frombuffer(buf, dtype=np.uint8)


# 라벨 저장 파일 열기 (training_set)
with open(training_label_file, 'w') as f:
    # 이미지에 대한 라벨 값을 순차적으로 저장
    for i in range(training_num_images):
        image_path = f'{training_set_folder}/{training_labels[i]}/image_{i}.png'  # 이미지 파일 경로  (image_{i:05}.png)
        label_value = training_labels[i]  # 라벨 값

        # 이미지 파일 경로와 라벨 값을 파일에 저장
        f.write(f'{image_path}\t{label_value}\n')


# 이미지 데이터를 numpy 배열로 변환 (training_set)
with open(training_image_decompressed_file, 'rb') as f:
    # 헤더 부분은 건너뛰기
    f.read(16)
    # 이미지 데이터 읽기
    buf = f.read()

# 이미지 배열 형태로 변환
training_images = np.frombuffer(buf, dtype=np.uint8).reshape(training_num_images, image_size, image_size)


# 이미지를 순서대로 저장 (training_set)
for i, image in enumerate(training_images):
    label_value = training_labels[i]  # 라벨 값
    
    # 해당 숫자의 폴더 생성
    digit_folder = os.path.join(training_set_folder, str(label_value))
    if not os.path.exists(digit_folder):
        os.makedirs(digit_folder)

     # 이미지 파일 경로
    image_path = os.path.join(digit_folder, f'image_{i}.png')
    plt.imsave(image_path, image, cmap='gray')

 


'''
================= test_set =================
'''

# 이미지 개수 및 크기 추출
test_num_images = 10000  # 예시로는 60,000개의 이미지가 있는 것으로 가정
test_image_size = 28  # 이미지 크기는 28x28 픽셀

# 이미지 저장 폴더 경로
test_set_folder = 'data/MNIST/test_set'

# 라벨 저장 파일 경로
test_label_file = 'data/MNIST/test_set/labels.txt'

# 압축 해제할 파일 경로 (test_set)
test_image_compressed_file = 'data/MNIST/raw/t10k-images-idx3-ubyte.gz'
test_label_compressed_file = 'data/MNIST/raw/t10k-labels-idx1-ubyte.gz'

# 압축 해제된 파일 경로 (test_set)
test_image_decompressed_file = 'data/MNIST/raw/t10k-images-idx3-ubyte'
test_label_decompressed_file = 'data/MNIST/raw/t10k-labels-idx1-ubyte'


# 이미지 파일 압축 해제 (test_set)
decompress_file(test_image_compressed_file, test_image_decompressed_file)

# 라벨 파일 압축 해제 (test_set)
decompress_file(test_label_compressed_file, test_label_decompressed_file)


# 이미지 저장 폴더 생성 (test_set)
if not os.path.exists(test_set_folder):
    os.makedirs(test_set_folder)


# 라벨 데이터를 numpy 배열로 변환 (test_set)
with open(test_label_decompressed_file, 'rb') as f:
    # 헤더 부분은 건너뛰기
    f.read(8)
    # 라벨 데이터 읽기
    buf = f.read()

# 라벨 배열 형태로 변환 (test_set)
test_labels = np.frombuffer(buf, dtype=np.uint8)


# 라벨 저장 파일 열기 (test_set)
with open(test_label_file, 'w') as f:
    # 이미지에 대한 라벨 값을 순차적으로 저장
    for i in range(test_num_images):
        image_path = f'{test_set_folder}/{test_labels[i]}/image_{i}.png'  # 이미지 파일 경로  (image_{i:05}.png)
        label_value = test_labels[i]  # 라벨 값

        # 이미지 파일 경로와 라벨 값을 파일에 저장
        f.write(f'{image_path}\t{label_value}\n')


# 이미지 데이터를 numpy 배열로 변환 (test_set)
with open(test_image_decompressed_file, 'rb') as f:
    # 헤더 부분은 건너뛰기
    f.read(16)
    # 이미지 데이터 읽기
    buf = f.read()

# 이미지 배열 형태로 변환
test_images = np.frombuffer(buf, dtype=np.uint8).reshape(test_num_images, image_size, image_size)


# 이미지를 순서대로 저장 (test_set)
for i, image in enumerate(test_images):
    label_value = test_labels[i]  # 라벨 값
    
    # 해당 숫자의 폴더 생성
    digit_folder = os.path.join(test_set_folder, str(label_value))
    if not os.path.exists(digit_folder):
        os.makedirs(digit_folder)

     # 이미지 파일 경로
    image_path = os.path.join(digit_folder, f'image_{i}.png')
    plt.imsave(image_path, image, cmap='gray')

 
실행 결과는 다음과 같다.

 
2023.07.19 - [AI] - MNIST 데이터셋 다운로드

2023.07.19 - [AI] - MNIST 데이터셋을 이미지 파일로 복원

2023.07.19 - [AI] - MNIST 데이터셋 로더

2023.07.19 - [AI] - MNIST 모델 테스터

2023.07.19 - [AI] - MINST - SVC(Support Vector Classifier)

2023.07.19 - [AI] - MNIST - RandomForestClassifier

2023.07.19 - [AI] - MNIST - Keras

2023.07.19 - [AI] - MNIST - TensorFlowLite

 

 


 

반응형

'개발 > AI,ML,ALGORITHM' 카테고리의 다른 글

MNIST 모델 테스터  (0) 2023.07.19
MNIST 데이터셋 로더  (0) 2023.07.19
MNIST 데이터셋 다운로드  (0) 2023.07.19
Neural Network (XOR)  (0) 2022.11.18
2D 충돌처리  (0) 2020.12.12
블로그 이미지

SKY STORY

,
반응형

문자인식을 학습을 위해 MNIST 데이터셋을 다운로드 받아 

'data'폴더에 저장하는 코드이다.

//
//  MNIST 데이터셋 다운로드
//
//  Created by netcanis on 2023/07/20.
//

import torch
from torchvision import datasets, transforms

# MNIST 데이터셋 다운로드
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5,), (0.5,))
])
train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
test_dataset = datasets.MNIST(root='./data', train=False, download=True, transform=transform)

 

저장 결과는 다음과 같다.

 

 

2023.07.19 - [AI] - MNIST 데이터셋 다운로드

2023.07.19 - [AI] - MNIST 데이터셋을 이미지 파일로 복원

2023.07.19 - [AI] - MNIST 데이터셋 로더

2023.07.19 - [AI] - MNIST 모델 테스터

2023.07.19 - [AI] - MINST - SVC(Support Vector Classifier)

2023.07.19 - [AI] - MNIST - RandomForestClassifier

2023.07.19 - [AI] - MNIST - Keras

2023.07.19 - [AI] - MNIST - TensorFlowLite

 

 

반응형

'개발 > AI,ML,ALGORITHM' 카테고리의 다른 글

MNIST 데이터셋 로더  (0) 2023.07.19
MNIST 데이터셋을 이미지 파일로 복원  (0) 2023.07.19
Neural Network (XOR)  (0) 2022.11.18
2D 충돌처리  (0) 2020.12.12
Generic algorithm  (0) 2020.05.19
블로그 이미지

SKY STORY

,

Rosetta2

개발/iOS 2023. 4. 26. 14:21
반응형

애플 Silicon CPU사용 컴퓨터의 경우 인텔CPU 전용으로 개발된 앱을 실행하려면 

Rosetta2를 설치해야 한다. 콘솔창을 띄워 다음 명령을 실행해 준다.

 

// 1. 라이센스 동의 필요

softwareupdate —install-rosetta

A Enter

 

// 2. 라이선스 자동 동의

/usr/sbin/softwareupdate —install-rosetta —agree-to-license

반응형

'개발 > iOS' 카테고리의 다른 글

Floating, Dragging Button  (1) 2024.01.04
XOR 연산을 이용한 문자열 비교  (0) 2023.12.07
NFC tag read/write Manager Class (2/2)  (0) 2023.04.26
NFC tag read/write Manager Class (1/2)  (0) 2023.04.26
Carthage 설치 및 제거  (0) 2023.01.11
블로그 이미지

SKY STORY

,
반응형

NFC NDEF tag 및 feliCa, iso7816, iso15693, miFare tag  읽기 / 쓰기 관련 클래스를 만들어 보았다.

//
//  HiNfcManager.swift
//  NFC
//
//  Created by netcanis on 2023/04/18.
//

import UIKit
import CoreNFC

public enum HiNFCError: Error {
    case unavailable
    case notSupported
    case readOnly
    case invalidPayloadSize
    case invalidated(errorDescription: String)
}

open class HiNFCManager: NSObject {
    static let shared = HiNFCManager()
    
    public typealias DidBecomeActive = (HiNFCManager) -> Void
    public typealias DidDetect = (HiNFCManager, Result<[String: Any]?, HiNFCError>) -> Void
    
    private enum HiNFCAction {
        case read
        case write(message: NFCNDEFMessage)
    }
    
    // MARK: - Properties
    private var didBecomeActive: DidBecomeActive?
    private var didDetect: DidDetect?
    private var action: HiNFCAction?
    
    // MARK: - Properties (NDEF, Tag)
    open private(set) var ndefSession: NFCNDEFReaderSession?
    open private(set) var tagSession: Any?
    
    
    
    // MARK: - NFCNDEFTag
    open func read(didBecomeActive: DidBecomeActive? = nil, didDetect: @escaping DidDetect) {
        guard NFCNDEFReaderSession.readingAvailable else {
            self.didDetect?(self, .failure(.unavailable))
            return
        }
        let session = NFCNDEFReaderSession(delegate: self,
                                           queue: nil,
                                           invalidateAfterFirstRead: true)
        action = .read
        startSession(session: session, didBecomeActive: didBecomeActive, didDetect: didDetect)
    }
    
    open func write(message: [String: Any], didBecomeActive: DidBecomeActive? = nil, didDetect: @escaping DidDetect) {
        guard NFCNDEFReaderSession.readingAvailable else {
            self.didDetect?(self, .failure(.unavailable))
            return
        }
        if #available(iOS 13.0, *) {
            let session = NFCNDEFReaderSession(delegate: self,
                                               queue: nil,
                                               invalidateAfterFirstRead: false)
            
            let payload = message["payload"] as? String ?? ""
            let type = message["type"] as? String ?? "T"
            let format: NFCTypeNameFormat = (type == "T") ? (.media) : (.absoluteURI)
            
            let payloadData = payload.data(using: .utf8)!
            let typeData = type.data(using: .utf8)!
            let ndefPayload = NFCNDEFPayload(format: format, type: typeData, identifier: Data(), payload: payloadData)
            let ndefMessage = NFCNDEFMessage(records: [ndefPayload])
            
            action = .write(message: ndefMessage)
            startSession(session: session, didBecomeActive: didBecomeActive, didDetect: didDetect)
        }
    }
    
    open func setMessage(_ alertMessage: String) {
        ndefSession?.alertMessage = alertMessage
    }
    
    
    
    // MARK: - NFCTag
    open func readTag(didBecomeActive: DidBecomeActive? = nil, didDetect: @escaping DidDetect) {
        guard NFCReaderSession.readingAvailable else {
            self.didDetect?(self, .failure(.unavailable))
            return
        }
        if #available(iOS 13.0, *) {
            let session = NFCTagReaderSession(pollingOption: [.iso14443, .iso15693, .iso18092],
                                              delegate: self,
                                              queue: nil)!
            action = .read
            startSession(session: session, didBecomeActive: didBecomeActive, didDetect: didDetect)
        }
    }
    
    open func writeTag(message: [String: Any], didBecomeActive: DidBecomeActive? = nil, didDetect: @escaping DidDetect) {
        guard NFCReaderSession.readingAvailable else {
            self.didDetect?(self, .failure(.unavailable))
            return
        }
        if #available(iOS 13.0, *) {
            let session = NFCTagReaderSession(pollingOption: [.iso14443, .iso15693, .iso18092],
                                              delegate: self,
                                              queue: nil)!
            
            let payload = message["payload"] as? String ?? ""
            let type = message["type"] as? String ?? "T"
            let format: NFCTypeNameFormat = (type == "T") ? (.media) : (.absoluteURI)
            
            let payloadData = payload.data(using: .utf8)!
            let typeData = type.data(using: .utf8)!
            let ndefPayload = NFCNDEFPayload(format: format, type: typeData, identifier: Data(), payload: payloadData)
            let ndefMessage = NFCNDEFMessage(records: [ndefPayload])

            action = .write(message: ndefMessage)
            startSession(session: session, didBecomeActive: didBecomeActive, didDetect: didDetect)
        }
    }
    
    open func setTagMessage(_ alertMessage: String) {
        if #available(iOS 13.0, *) {
            if let session = self.tagSession as? NFCTagReaderSession {
                session.alertMessage = alertMessage
            }
        }
    }
}



// MARK: - NFCNDEFReaderSessionDelegate
extension HiNFCManager : NFCNDEFReaderSessionDelegate {
    
    open func readerSessionDidBecomeActive(_ session: NFCNDEFReaderSession) {
        self.didBecomeActive?(self)
    }
    
    open func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
        // iOS 13미만
        guard let message = messages.first, let record = message.records.first else {
            self.didDetect?(self, .failure(.invalidated(errorDescription: "NDEF message나 message record가 없습니다.")))
            self.invalidate(errorMessage: "NDEF message나 message record가 없습니다.")
            return
        }
        
        
        let language = String(data: record.payload.advanced(by: 1), encoding: .utf8)
        let encoding = record.payload[0] & NFCTypeNameFormat.nfcWellKnown.rawValue
        let textData = record.payload.advanced(by: 3)
        let text = String(data: textData, encoding: .utf8)
        
        let result: [String: Any] = [
            "Type":record.type.string,
            "Format": self.formattedTNF(from: record.typeNameFormat),
            "Value": [
                "Encoding": "\(encoding)",
                "Language": "\(language ?? "")",
                "Text": "\(text ?? "")"
            ],
            "Raw value": record.payload.string,
            "Payload": "\(record.payload.hexStringFormatted)",
            "Size": "\(record.payload.count)",
        ]
        print("\(String(describing: result.toJsonString()))")
        
        self.didDetect?(self, .success(result))
        self.invalidate(errorMessage: "NDEF message read successfully.")
    }
    
    @available(iOS 13.0, *)
    open func readerSession(_ session: NFCNDEFReaderSession, didDetect tags: [NFCNDEFTag]) {
        guard tags.count == 1, let tag = tags.first else {
            DispatchQueue.global().asyncAfter(deadline: .now() + .microseconds(500)) {
                session.restartPolling()
            }
            return
        }
        
        session.connect(to: tag) { [weak self] error in
            guard let self = self else { return }
            if error != nil {
                self.didDetect?(self, .failure(.invalidated(errorDescription: error!.localizedDescription)))
                self.invalidate(errorMessage: error?.localizedDescription)
                return
            }
            
            tag.queryNDEFStatus { status, capacity, error in
                switch (status, self.action) {
                case (.notSupported, _):
                    self.didDetect?(self, .failure(.notSupported))
                    self.invalidate(errorMessage: error?.localizedDescription)

                case (.readOnly, _):
                    self.didDetect?(self, .failure(.readOnly))

                case (.readWrite, .read):
                    tag.readNDEF { message, error in
                        if error != nil {
                            self.didDetect?(self, .failure(.invalidated(errorDescription: error!.localizedDescription)))
                            self.invalidate(errorMessage: error?.localizedDescription)
                            return
                        }
                        
                        let record = message?.records.first
                        let result: [String: Any] = [
                            "Type":record?.type.string ?? "",
                            "Format": self.formattedTNF(from: record!.typeNameFormat),
                            "Raw value": record?.payload.string ?? "",
                            "Payload": "\(record?.payload.hexStringFormatted ?? "")",
                            "Size": "\(record?.payload.count ?? 0)",
                        ]
                        self.didDetect?(self, .success(result))
                        self.invalidate(errorMessage: error?.localizedDescription)
                    }

                case (.readWrite, .write(let message)):
                    guard message.length <= capacity else {
                        self.didDetect?(self, .failure(.invalidPayloadSize))
                        self.invalidate(errorMessage: "Invalid payload size")
                        return
                    }

                    tag.writeNDEF(message) { error in
                        if error != nil {
                            self.didDetect?(self, .failure(.invalidated(errorDescription: error!.localizedDescription)))
                            self.invalidate(errorMessage: error!.localizedDescription)
                            return
                        }
                        let result: [String: Any] = [
                            "result":message.records.first?.payload.string ?? ""
                        ]
                        self.didDetect?(self, .success(result))
                        self.invalidate(errorMessage: error?.localizedDescription)
                    }
                default:
                    return
                }
            }
        }
    }
    
    open func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
        if let error = error as? NFCReaderError,
           error.code != .readerSessionInvalidationErrorFirstNDEFTagRead &&
            error.code != .readerSessionInvalidationErrorUserCanceled {
            self.didDetect?(self, .failure(.invalidated(errorDescription: error.localizedDescription)))
        }
        self.ndefSession = nil
        self.tagSession = nil
        self.didBecomeActive = nil
        self.didDetect = nil
    }
}


// MARK: - Private helper functions
extension HiNFCManager {
    private func invalidate(errorMessage: String?) {
        if errorMessage != nil {
            if #available(iOS 13.0, *) {
                if let ns = ndefSession {
                    ns.invalidate(errorMessage: errorMessage!)
                }
                if let ts = tagSession as? NFCTagReaderSession {
                    ts.invalidate(errorMessage: errorMessage!)
                }
            } else {
                if let ns = ndefSession {
                    ns.invalidate()
                }
                if #available(iOS 13.0, *) {
                    if let ts = tagSession as? NFCTagReaderSession {
                        ts.invalidate()
                    }
                }
            }
        } else {
            if let ns = ndefSession {
                ns.invalidate()
            }
            if #available(iOS 13.0, *) {
                if let ts = tagSession as? NFCTagReaderSession {
                    ts.invalidate()
                }
            }
        }
        ndefSession = nil
        tagSession = nil
        didBecomeActive = nil
        didDetect = nil
    }
    
    private func startSession(session: NFCNDEFReaderSession,
                              didBecomeActive: DidBecomeActive?,
                              didDetect: @escaping DidDetect) {
        self.ndefSession = session
        self.didBecomeActive = didBecomeActive
        self.didDetect = didDetect
        session.begin()
    }
    
    @available(iOS 13.0, *)
    private func startSession(session: NFCTagReaderSession,
                              didBecomeActive: DidBecomeActive?,
                              didDetect: @escaping DidDetect) {
        self.tagSession = session
        self.didBecomeActive = didBecomeActive
        self.didDetect = didDetect
        session.begin()
    }
    
    private func formattedTNF(from tnf: NFCTypeNameFormat) -> String {
        switch tnf {
        case .empty:        // 0: 이름 없는 레코드 (빈 문자열로 표시됨)
            return "Empty (0x00)"
        case .nfcWellKnown: // 1: NFC Forum에서 정의한 레코드 형식
            return "NFC Well Known (0x01)"
        case .media:        // 2: 미디어 타입의 레코드 (ex. 'audio/mp3')
            return "Media (0x02)"
        case .absoluteURI:  // 3: URI 형식의 레코드
            return "Absolute URI (0x03)"
        case .nfcExternal:  // 4: NFC 포럼에서 정의한 외부 레코드
            return "NFC External (0x04)"
        case .unchanged:    // 6: 이름 형식 변경 없음. (현재의 이름 형식을 유지)
            return "Unchanged (0x06)"
        default:            // 5: 알 수 없는 레코드
            return "Unknown (0x05)"
        }
    }
}





// MARK: - NFCTagReaderSessionDelegate
@available(iOS 13.0, *)
extension HiNFCManager : NFCTagReaderSessionDelegate {
    
    public func tagReaderSessionDidBecomeActive(_ session: NFCTagReaderSession) {
        self.didBecomeActive?(self)
    }
    
    public func tagReaderSession(_ session: NFCTagReaderSession, didDetect tags: [NFCTag]) {
        guard tags.count == 1, let tag = tags.first else {
            DispatchQueue.global().asyncAfter(deadline: .now() + .microseconds(500)) {
                session.restartPolling()
            }
            return
        }
        
        var ndefTag: NFCNDEFTag
        switch tag {
        case let .feliCa(tag):  /// FeliCa tag. (NFCFeliCaTag)
            ndefTag = tag
            self.parseFeliCaTag(tag)
        case let .iso7816(tag): /// ISO14443-4 type A / B tag with ISO7816 communication. (NFCISO7816Tag)
            ndefTag = tag
            self.parseISO7816Tag(tag)
        case let .iso15693(tag):/// ISO15693 tag.
            ndefTag = tag
            self.parseISO15693Tag(tag)
        case let .miFare(tag):  /// MiFare technology tag (MIFARE Plus, UltraLight, DESFire) base on ISO14443. (NFCMiFareTag)
            ndefTag = tag
            self.parseMIFARETag(tag)
        @unknown default:
            self.didDetect?(self, .failure(.invalidated(errorDescription: "Tag not valid.")))
            self.invalidate(errorMessage: "Tag not valid.")
            return
        }
        
        
        session.connect(to: tag) { [weak self] error in
            guard let self = self else { return }
            if error != nil {
                self.didDetect?(self, .failure(.invalidated(errorDescription: error!.localizedDescription)))
                self.invalidate(errorMessage: error?.localizedDescription)
                return
            }
            
            ndefTag.queryNDEFStatus { status, capacity, error in
                switch (status, self.action) {
                case (.notSupported, _):
                    self.didDetect?(self, .failure(.notSupported))
                    self.invalidate(errorMessage: error?.localizedDescription)

                case (.readOnly, _):
                    self.didDetect?(self, .failure(.readOnly))

                case (.readWrite, .read):
                    ndefTag.readNDEF { (message, error) in
                        if error != nil || message == nil {
                            self.didDetect?(self, .failure(.invalidated(errorDescription: error!.localizedDescription)))
                            self.invalidate(errorMessage: error?.localizedDescription)
                            return
                        }

                        let record = message?.records.first
                        let result: [String: Any] = [
                            "Type":record?.type.string ?? "",
                            "Format": self.formattedTNF(from: record!.typeNameFormat),
                            "Raw value": record?.payload.string ?? "",
                            "Payload": "\(record?.payload.hexStringFormatted ?? "")",
                            "Size": "\(record?.payload.count ?? 0)",
                        ]
                        self.didDetect?(self, .success(result))
                        self.invalidate(errorMessage: error?.localizedDescription)
                    }
                case (.readWrite, .write(let message)):
                    guard message.length <= capacity else {
                        self.didDetect?(self, .failure(.invalidPayloadSize))
                        self.invalidate(errorMessage: "Invalid payload size")
                        return
                    }

                    ndefTag.writeNDEF(message) { error in
                        if error != nil {
                            self.didDetect?(self, .failure(.invalidated(errorDescription: error!.localizedDescription)))
                            self.invalidate(errorMessage: error!.localizedDescription)
                            return
                        }
                        let result: [String: Any] = [
                            "result":message.records.first?.payload.string ?? ""
                        ]
                        self.didDetect?(self, .success(result))
                        self.invalidate(errorMessage: error?.localizedDescription)
                    }
                default:
                    return
                }
            }
        }
    }
    
    public func tagReaderSession(_ session: NFCTagReaderSession, didInvalidateWithError error: Error) {
        // NFC 태그 읽기가 중단되었을 때, 호출된다.
        print("NFCTag 읽기 중단: \(error.localizedDescription)")
    }
    
}


// MARK: - Private helper functions (for NFCTag)
@available(iOS 13.0, *)
extension HiNFCManager {
    /// FeliCa tag. (NFCFeliCaTag)
    private func parseFeliCaTag(_ tag: NFCFeliCaTag) {
        let log = """
                    ------------------------------------
                    :::::::::: [ FeliCa tag ] ::::::::::
                    "- Type : FeliCa tag."
                    "- currentIDm : \(String(describing: tag.currentIDm))"
                    "- currentSystemCode : \(String(describing: tag.currentSystemCode))"
                    "- description : \(String(describing: tag.description))"
                    ------------------------------------
                    """
        print(log)
    }
    
    /// ISO14443-4 type A / B tag with ISO7816 communication. (NFCISO7816Tag)
    private func parseISO7816Tag(_ tag: NFCISO7816Tag) {
        let log = """
                    ------------------------------------
                    :::::::::: [ ISO7816 tag ] ::::::::::
                    "- Type : ISO14443-4 type A / B tag with ISO7816 communication."
                    "- Identifier : \(String(describing: tag.identifier))"
                    "- historicalBytes : \(String(describing: tag.historicalBytes?.hexStringFormatted))"
                    "- applicationData : \(String(describing: tag.applicationData?.hexStringFormatted))"
                    "- initialSelectedAID : \(tag.initialSelectedAID)"
                    "- proprietaryApplicationDataCoding : \(tag.proprietaryApplicationDataCoding)"
                    "- description : \(String(describing: tag.description))"
                    ------------------------------------
                    """
        print(log)
    }

    /// ISO15693 tag.
    private func parseISO15693Tag(_ tag: NFCISO15693Tag) {
        let log = """
                    ------------------------------------
                    :::::::::: [ ISO15693 tag ] ::::::::::
                    "- Type : ISO15693 tag."
                    "- Identifier : \(tag.identifier.hexString)"
                    "- icSerialNumber : \(String(describing: tag.icSerialNumber.hexStringFormatted))" // IC 시리얼 번호(IC serial number)
                    "- icManufacturerCode : \(String(describing: tag.icManufacturerCode))" // C 제조사 코드(IC manufacturer code)
                    "- description : \(String(describing: tag.description))"
                    ------------------------------------
                    """
        print(log)
    }
    
    /// MiFare technology tag (MIFARE Plus, UltraLight, DESFire) base on ISO14443. (NFCMiFareTag)
    private func parseMIFARETag(_ tag: NFCMiFareTag) {
        let log = """
                    ------------------------------------
                    :::::::::: [ MiFare tag ] ::::::::::
                    "- Type : MiFare technology tag (MIFARE Plus, UltraLight, DESFire) base on ISO14443."
                    "- Identifier : \(tag.identifier.hexString)"
                    "- Historical bytes : \(tag.historicalBytes?.hexString ?? "None")"
                    "- mifareFamily : \(self.formattedMiFareFamily(tag))"
                    "- description : \(String(describing: tag.description))"
                    "- Block count : \(tag.mifareFamily == .plus ? 256 : 16)"
                    ------------------------------------
                    """
        print(log)
    }
    
    private func formattedMiFareFamily (_ tag: NFCMiFareTag) -> String {
        switch (tag.mifareFamily) {
        case .unknown:
            return "MiFare compatible ISO14443 Type A tag." // ISO14443 Type A 호환제품
        case .ultralight:
            return "MiFare Ultralight series."
        case .plus:
            return "MiFare Plus series."
        case .desfire:
            return "MiFare DESFire series."
        default:
            return "Unknown"
        }
    }
}



extension Data {
    // Data([0x12, 0x34, 0x56]) -> 123456
    var hexString: String {
        return map { String(format: "%02hhx", $0) }.joined() // big-endian byte order : reversed().map
    }
    
    // Data([0x12, 0x34, 0x56]) -> 0x12 0x34 0x56
    var hexStringFormatted: String {
        let hexArray = map { String(format: "0x%02hhx", $0) }
        return hexArray.joined(separator: " ")
    }
    
    // Data([0x12, 0x34, 0x56]) -> 313233343536
    public func hexEncodedString() -> String {
        let hexDigits = Array("0123456789abcdef".utf16)
        var hexChars = [UTF16.CodeUnit]()
        hexChars.reserveCapacity(count * 2)
        for byte in self {
            let (index1, index2) = Int(byte).quotientAndRemainder(dividingBy: 16)
            hexChars.append(hexDigits[index1])
            hexChars.append(hexDigits[index2])
        }
        return String(utf16CodeUnits: hexChars, count: hexChars.count)
    }
    
    var string: String {
        return String(data: self, encoding: .utf8)!
    }
    
    var jsonString: String {
        do {
            let json = try JSONSerialization.jsonObject(with: self, options: [])
            let data = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)
            return String(data: data, encoding: .utf8) ?? ""
        } catch let error {
            print("JSON serialization error: \(error.localizedDescription)")
            return ""
        }
    }
    
    func isJsonString() -> Bool {
        do {
            let _ = try JSONSerialization.jsonObject(with: self, options: [])
            return true
        } catch {
            return false
        }
    }
}

extension Dictionary {
    func toJsonString() -> String? {
        do {
            let jsonData = try JSONSerialization.data(withJSONObject: self, options: [.prettyPrinted, .sortedKeys])
            return String(data: jsonData, encoding: .utf8)
        } catch {
            print(error.localizedDescription)
            return nil
        }
    }
}

extension Array where Element == Data {
    func toStrings() -> [String] {
        return self.map { String(data: $0, encoding: .utf8) ?? "" }
    }
    
    func toHexStrings() -> [String] {
        return self.map { $0.reduce("") { $0 + String(format: "%02x", $1) } }
    }
}

extension NSObject {
    func rootWindow() -> UIWindow? {
        var window: UIWindow?
        if #available(iOS 15.0, *) {
            window = UIApplication.shared.connectedScenes
                .compactMap { $0 as? UIWindowScene }
                .flatMap { $0.windows }
                .first(where: { $0.isKeyWindow })
        } else {
            window = UIApplication.shared.windows.first(where: { $0.isKeyWindow })
        }
        return window
    }
    
    func showAlert(title: String?, message: String?, actions: [UIAlertAction] = [UIAlertAction(title: "OK", style: .default, handler: nil)], preferredStyle: UIAlertController.Style = .alert) {
        let alertController = UIAlertController(title: title, message: message, preferredStyle: preferredStyle)
        for action in actions {
            alertController.addAction(action)
        }
        guard let rootViewController = self.rootWindow()?.rootViewController else { return }
        rootViewController.present(alertController, animated: true)
    }
}

 

사용법 :

// NFC NDEF 테그 쓰기
let str = "테스트 메시지 입니다."
let type = str.hasPrefix("http") == true ? "U" : "T"

let message: [String: Any] = [
    "payload": str,
    "type": type
]
HiNFCManager.shared.write(message: message) { manager in
    manager.setMessage("Place iPhone near the tag to be written on")
} didDetect: { manager, result in
    switch result {
    case .failure(let error):
        manager.setMessage("Failed to write tag")
        print("\(error.localizedDescription)")
    case .success(let payload):
        manager.setMessage("Tag successfully written")
        print("\(payload?.toJsonString() ?? "")")
        DispatchQueue.main.async {
            self.infoText.text = "\(payload?.toJsonString() ?? "")" + "\n" + self.infoText.text
        }
   }
}


// NFC NDEF 테그 읽기
HiNFCManager.shared.read { manager, result in
    switch result {
    case .failure(let error):
        manager.setMessage("Failed to read tag")
        print("\(error.localizedDescription)")
    case .success(let payload):
        manager.setMessage("Tag read successfully")
        print("\(payload?.toJsonString() ?? "")")
        DispatchQueue.main.async {
            self.infoText.text = "\(payload?.toJsonString() ?? "")" + "\n" + self.infoText.text
        }
   }
}




// NFC Tag 쓰기
let str = "https://www.apple.com"
let type = str.hasPrefix("http") == true ? "U" : "T"

let message: [String: Any] = [
    "payload": str,
    "type": type
]
HiNFCManager.shared.writeTag(message: message) { manager in
    manager.setMessage("Place iPhone near the tag to be written on")
} didDetect: { manager, result in
    switch result {
    case .failure(let error):
        manager.setMessage("Failed to write tag")
        print("\(error.localizedDescription)")
    case .success(let payload):
        manager.setMessage("Tag successfully written")
        print("\(payload?.toJsonString() ?? "")")
        DispatchQueue.main.async {
            self.infoText.text = "\(payload?.toJsonString() ?? "")" + "\n" + self.infoText.text
        }
   }
}


// NFC Tag 읽기
HiNFCManager.shared.readTag { manager, result in
    switch result {
    case .failure(let error):
        manager.setMessage("Failed to read tag")
        print("\(error.localizedDescription)")
    case .success(let payload):
        manager.setMessage("Tag read successfully")
        print("\(payload?.toJsonString() ?? "")")
        DispatchQueue.main.async {
            self.infoText.text = "\(payload?.toJsonString() ?? "")" + "\n" + self.infoText.text
        }
   }
}

 

결과값 로그 :

// NFC NDEF tag 읽기결과 로그
{
  "Format" : "Media (0x02)",
  "Payload" : "0x74 0x65 0x73 0x74 0x20 0x6d 0x65 0x73 0x73 0x61 0x67 0x65",
  "Raw value" : "test message",
  "Size" : "12",
  "Type" : "T"
}

// NFC NDEF tag 쓰기결과 로그
{
  "result" : "test message"
}


// NFC Tag 읽기결과 로그
{
  "Format" : "Absolute URI (0x03)",
  "Payload" : "0x68 0x74 0x74 0x70 0x73 ... 0x65 0x2e 0x63 0x6f 0x6d",
  "Raw value" : "https://www.apple.com",
  "Size" : "21",
  "Type" : "U"
}

// NFC Tag 쓰기결과 로그
{
  "result" : "https:\/\/www.apple.com"
}

 

 

2023.04.26 - [iOS] - NFC tag read/write Manager Class (1/2)

2023.04.26 - [iOS] - NFC tag read/write Manager Class (2/2)

 

반응형

'개발 > iOS' 카테고리의 다른 글

XOR 연산을 이용한 문자열 비교  (0) 2023.12.07
Rosetta2  (0) 2023.04.26
NFC tag read/write Manager Class (1/2)  (0) 2023.04.26
Carthage 설치 및 제거  (0) 2023.01.11
NSURLSessionTask 캐싱 비활성화  (0) 2022.10.24
블로그 이미지

SKY STORY

,
반응형

NFC NDEF tag 읽기 / 쓰기 및 feliCa, iso7816, iso15693, miFare tag 읽기 관련 클래스를

만들기 위해 우선 다음과 같이 환경 설정부터 진행해 보자.

 

우선 개발자 사이트에서 App ID 설정에서 NFC 읽기 활성화 한다.

Xcode에서는 다음과 같이 적용된다.

entitlements 파일에 NDEF, TAG 등록.

Info.plist 권한 설정은 다음과 같다.

NFC 읽기 권한  설정을 한다.

<key>NFCReaderUsageDescription</key>
<string>결제를 위해 NFC 사용이 승인되어야 합니다.</string>

 

소니의 Felica tag를 읽기 위해 시스템 코드를 등록한다.

<key>com.apple.developer.nfc.readersession.felica.systemcodes</key>
<array>
	<string>88B4</string>
	<string>88A8</string>
	<string>8031</string>
	<string>88CA</string>
	<string>8FC7</string>
	<string>8E5E</string>
	<string>8E6F</string>
	<string>8F5B</string>
	<string>869F</string>
	<string>12FC</string>
	<string>0003</string>
	<string>FE00</string>
</array>
</plist>

 

iso7816 tag의 경우 다음과 같이 읽을 id를 등록한다.

<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
	<string>D2760000850101h</string>
	<string>315041592E5359532E4444463031</string>
	<string>D4100000030001</string>
	<string>325041592E5359532E4444463031</string>
	<string>44464D46412E44466172653234313031</string>
	<string>A00000000101</string>
	<string>A000000003000000</string>
	<string>A00000000300037561</string>
	<string>A00000000305076010</string>
	<string>A0000000031010</string>
	<string>A000000003101001</string>
	<string>A000000003101002</string>
	<string>A0000000032010</string>
	<string>A0000000032020</string>
	<string>A0000000033010</string>
	<string>A0000000034010</string>
	<string>A0000000035010</string>
	<string>A000000003534441</string>
	<string>A0000000035350</string>
	<string>A000000003535041</string>
	<string>A0000000036010</string>
	<string>A0000000036020</string>
	<string>A0000000038002</string>
	<string>A0000000038010</string>
	<string>A0000000039010</string>
	<string>A000000003999910</string>
	<string>A0000000040000</string>
	<string>A00000000401</string>
	<string>A0000000041010</string>
	<string>A00000000410101213</string>
	<string>A00000000410101215</string>
	<string>A0000000041010BB5449435301</string>
	<string>A0000000042010</string>
	<string>A0000000042203</string>
	<string>A0000000043010</string>
	<string>A0000000043060</string>
	<string>A000000004306001</string>
	<string>A0000000044010</string>
	<string>A0000000045010</string>
	<string>A0000000045555</string>
	<string>A0000000046000</string>
	<string>A0000000048002</string>
	<string>A0000000049999</string>
	<string>A0000000050001</string>
	<string>A0000000050002</string>
	<string>A0000000090001FF44FF1289</string>
	<string>A0000000101030</string>
	<string>A00000001800</string>
	<string>A0000000181001</string>
	<string>A000000018434D</string>
	<string>A000000018434D00</string>
	<string>A00000002401</string>
	<string>A000000025</string>
	<string>A0000000250000</string>
	<string>A00000002501</string>
	<string>A000000025010104</string>
	<string>A000000025010402</string>
	<string>A000000025010701</string>
	<string>A000000025010801</string>
	<string>A0000000291010</string>
	<string>A00000002945087510100000</string>
	<string>A00000002949034010100001</string>
	<string>A00000002949282010100000</string>
	<string>A000000029564182</string>
	<string>A00000003029057000AD13100101FF</string>
	<string>A0000000308000000000280101</string>
	<string>A0000000421010</string>
	<string>A0000000422010</string>
	<string>A0000000423010</string>
	<string>A0000000424010</string>
	<string>A0000000425010</string>
	<string>A0000000426010</string>
	<string>A00000005945430100</string>
	<string>A000000063504B43532D3135</string>
	<string>A0000000635741502D57494D</string>
	<string>A00000006510</string>
	<string>A0000000651010</string>
	<string>A00000006900</string>
	<string>A000000077010000021000000000003B</string>
	<string>A0000000790100</string>
	<string>A0000000790101</string>
	<string>A0000000790102</string>
	<string>A00000007901F0</string>
	<string>A00000007901F1</string>
	<string>A00000007901F2</string>
	<string>A0000000790200</string>
	<string>A0000000790201</string>
	<string>A00000007902FB</string>
	<string>A00000007902FD</string>
	<string>A00000007902FE</string>
	<string>A0000000790300</string>
	<string>A0000000791201</string>
	<string>A0000000791202</string>
	<string>A0000000871002FF49FF0589</string>
	<string>A00000008810200105C100</string>
	<string>A000000088102201034221</string>
	<string>A000000088102201034321</string>
	<string>A0000000960200</string>
	<string>A000000098</string>
	<string>A0000000980840</string>
	<string>A0000000980848</string>
	<string>A0000001110101</string>
	<string>A0000001110201</string>
	<string>A0000001160300</string>
	<string>A0000001166010</string>
	<string>A0000001166030</string>
	<string>A0000001169000</string>
	<string>A000000116A001</string>
	<string>A000000116DB00</string>
	<string>A000000118010000</string>
	<string>A000000118020000</string>
	<string>A000000118030000</string>
	<string>A000000118040000</string>
	<string>A0000001184543</string>
	<string>A000000118454E</string>
	<string>A0000001211010</string>
	<string>A0000001320001</string>
	<string>A0000001408001</string>
	<string>A0000001410001</string>
	<string>A0000001510000</string>
	<string>A00000015153504341534400</string>
	<string>A0000001523010</string>
	<string>A0000001524010</string>
	<string>A0000001544442</string>
	<string>A0000001570010</string>
	<string>A0000001570020</string>
	<string>A0000001570021</string>
	<string>A0000001570022</string>
	<string>A0000001570023</string>
	<string>A0000001570030</string>
	<string>A0000001570031</string>
	<string>A0000001570040</string>
	<string>A0000001570050</string>
	<string>A0000001570051</string>
	<string>A0000001570100</string>
	<string>A0000001570104</string>
	<string>A0000001570109</string>
	<string>A000000157010A</string>
	<string>A000000157010B</string>
	<string>A000000157010C</string>
	<string>A000000157010D</string>
	<string>A0000001574443</string>
	<string>A0000001574444</string>
	<string>A000000167413000FF</string>
	<string>A000000167413001</string>
	<string>A000000172950001</string>
	<string>A000000177504B43532D3135</string>
	<string>A0000001850002</string>
	<string>A0000001884443</string>
	<string>A0000002040000</string>
	<string>A0000002281010</string>
	<string>A0000002282010</string>
	<string>A00000022820101010</string>
	<string>A0000002471001</string>
	<string>A0000002472001</string>
	<string>A0000002771010</string>
	<string>A00000030600000000000000</string>
	<string>A000000308000010000100</string>
	<string>A00000031510100528</string>
	<string>A0000003156020</string>
	<string>A00000032301</string>
	<string>A0000003230101</string>
	<string>A0000003241010</string>
	<string>A000000333010101</string>
	<string>A000000333010102</string>
	<string>A000000333010103</string>
	<string>A000000333010106</string>
	<string>A000000333010108</string>
	<string>A000000337301000</string>
	<string>A000000337101000</string>
	<string>A000000337102000</string>
	<string>A000000337101001</string>
	<string>A000000337102001</string>
	<string>A000000337601001</string>
	<string>A0000003591010</string>
	<string>A0000003591010028001</string>
	<string>A00000035910100380</string>
	<string>A0000003660001</string>
	<string>A0000003660002</string>
	<string>A0000003710001</string>
	<string>A00000038410</string>
	<string>A00000038420</string>
	<string>A0000003964D66344D0002</string>
	<string>A00000039742544659</string>
	<string>A0000003974349445F0100</string>
	<string>A0000004271010</string>
	<string>A0000004320001</string>
	<string>A0000004360100</string>
	<string>A0000004391010</string>
	<string>A0000004540010</string>
	<string>A0000004540011</string>
	<string>A0000004762010</string>
	<string>A0000004763030</string>
	<string>A0000004766C</string>
	<string>A000000476A010</string>
	<string>A000000476A110</string>
	<string>A000000485</string>
	<string>A0000005241010</string>
	<string>A0000005271002</string>
	<string>A000000527200101</string>
	<string>A000000527210101</string>
	<string>A0000005591010FFFFFFFF8900000100</string>
	<string>A0000005591010FFFFFFFF8900000200</string>
	<string>A0000005591010FFFFFFFF8900000D00</string>
	<string>A0000005591010FFFFFFFF8900000E00</string>
	<string>A0000005591010FFFFFFFF8900000F00</string>
	<string>A0000005591010FFFFFFFF8900001000</string>
	<string>A00000061700</string>
	<string>A0000006200620</string>
	<string>A0000006260101010001</string>
	<string>A0000006351010</string>
	<string>A0000006581010</string>
	<string>A0000006581011</string>
	<string>A0000006582010</string>
	<string>A0000006723010</string>
	<string>A0000006723020</string>
	<string>A0000007705850</string>
	<string>A0000007790000</string>
	<string>B012345678</string>
	<string>D040000001000002</string>
	<string>D040000002000002</string>
	<string>D040000003000002</string>
	<string>D040000004000002</string>
	<string>D04000000B000002</string>
	<string>D04000000C000002</string>
	<string>D04000000D000002</string>
	<string>D040000013000001</string>
	<string>D040000013000001</string>
	<string>D040000013000002</string>
	<string>D040000013000002</string>
	<string>D040000014000001</string>
	<string>D040000015000001</string>
	<string>D040000015000001</string>
	<string>D0400000190001</string>
	<string>D0400000190002</string>
	<string>D0400000190003</string>
	<string>D0400000190004</string>
	<string>D0400000190010</string>
	<string>D268000001</string>
	<string>D276000005</string>
	<string>D276000005AA040360010410</string>
	<string>D276000005AA0503E00401</string>
	<string>D276000005AA0503E00501</string>
	<string>D276000005AA0503E0050101</string>
	<string>D276000005AB0503E0040101</string>
	<string>D27600002200000001</string>
	<string>D27600002200000002</string>
	<string>D27600002200000060</string>
	<string>D276000025</string>
	<string>D27600002545410100</string>
	<string>D27600002545500100</string>
	<string>D27600002547410100</string>
	<string>D276000060</string>
	<string>D2760000850100</string>
	<string>D2760000850101</string>
	<string>D276000118</string>
	<string>D2760001180101</string>
	<string>D27600012401</string>
	<string>D276000124010101FFFF000000010000</string>
	<string>D2760001240102000000000000010000</string>
	<string>D27600012402</string>
	<string>D2760001240200010000000000000000</string>
	<string>D4100000011010</string>
	<string>D5280050218002</string>
	<string>D5780000021010</string>
	<string>D7560000010101</string>
	<string>D7560000300101</string>
	<string>D8040000013010</string>
	<string>E80704007F00070302</string>
	<string>E82881C11702</string>
	<string>E828BD080F</string>
	<string>F0000000030001</string>
</array>
</plist>

 

 

2023.04.26 - [개발노트] - NFC tag read/write Manager Class (1/2)

2023.04.26 - [분류 전체보기] - NFC tag read/write Manager Class (2/2)

 

반응형

'개발 > iOS' 카테고리의 다른 글

Rosetta2  (0) 2023.04.26
NFC tag read/write Manager Class (2/2)  (0) 2023.04.26
Carthage 설치 및 제거  (0) 2023.01.11
NSURLSessionTask 캐싱 비활성화  (0) 2022.10.24
UIScrollView 스크린샷 만들기  (0) 2022.10.24
블로그 이미지

SKY STORY

,
반응형

아파치 웹 서버 실행

 

아파치, php 버전 확인

$ apachectl -v

$ php -v

 

아파치, php 설치 (설치되어 있지 않을 경우)

$ brew install httpd

$ brew install php

 

아파치 실행

$ sudo launchctl load -w /System/Library/LaunchDaemons/org.apache.httpd.plist

 

아파치 종료

$ sudo launchctl unload -w /System/Library/LaunchDaemons/org.apache.httpd.plist

 

기본 웹서버 패스

/Library/WebServer/Documents/index.html

 

브라우저로 확인 

http://127.0.0.1

http://localhost

 

 

 

[ 요약 ]

sudo vi /private/etc/hosts

# mysite

127.0.0.1       ww1.mysite.net

127.0.0.1       ww2.mysite.net

 

sudo vi /private/etc/apache2/httpd.conf

 

sudo vi /private/etc/apache2/extra/httpd-vhosts.conf

<VirtualHost *:80>
    ServerName     ww1.mysite.net
    ServerAdmin    netcanis@mysite.net
    DocumentRoot   "/private/etc/apache2/htdocs-ww1"
    CustomLog      "/private/var/log/apache2/ww1.access.log" common
    ErrorLog       "/private/var/log/apache2/ww1.error.log"
    <Directory "/private/etc/apache2/htdocs-ww1">
        Options Indexes MultiViews
        AllowOverride None
        Require all granted
    </Directory>
</VirtualHost>



<VirtualHost *:80>
    ServerName     ww2.mysite.net
    ServerAdmin    netcanis@mysite.net
    DocumentRoot   "/private/etc/apache2/htdocs-ww2"
    CustomLog      "/private/var/log/apache2/ww2.access.log" common
    ErrorLog       "/private/var/log/apache2/ww2.error.log"
    <Directory "/private/etc/apache2/htdocs-ww2">
        Options Indexes MultiViews
        AllowOverride None
        Require all granted
    </Directory>
</VirtualHost>

 

 

테스트

sudo apachectl configtest

 

재시작

sudo apachectl graceful

 

브라우저 테스트

http://ww1.mysite.net

http://ww2.mysite.net

 

 

 

웹서버 패스

/private/etc/apache2/htdocs-ww1/index.html 

/private/etc/apache2/htdocs-ww2/index.html 

 

로그파일 패스

/private/var/log/apache2/ww1.access.log

/private/var/log/apache2/ww1.error.log

/private/var/log/apache2/ww2.access.log

/private/var/log/apache2/ww2.error.log

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형

'개발 > Note' 카테고리의 다른 글

Packet Format for the LE Uncoded PHYs  (0) 2024.01.09
Push Notification  (1) 2023.12.27
Unwind Segue 사용방법  (0) 2023.01.11
랜덤 seed 초기화  (0) 2022.11.18
matplotlib 사용  (0) 2022.11.18
블로그 이미지

SKY STORY

,
반응형

https://github.com/Carthage/Carthage

 

 

Homebrew 설치 (설치되어 있지 않을 경우)

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" < /dev/null 2> /dev/null

 

Carthage 설치

brew install Carthage

 

Carthage 업그레이드

brew upgrade Carthage

 

Carthage 제거

방법 1 (Carthage.pkg로 설치했을 경우) :

    rm -rf /usr/local/bin/carthage

    sudo rm -rf /Library/Frameworks/CarthageKit.framework

방법 2 (Homebrew로 설치했을 경우) :

    brew uninstall --force carthage

 

반응형

'개발 > iOS' 카테고리의 다른 글

NFC tag read/write Manager Class (2/2)  (0) 2023.04.26
NFC tag read/write Manager Class (1/2)  (0) 2023.04.26
NSURLSessionTask 캐싱 비활성화  (0) 2022.10.24
UIScrollView 스크린샷 만들기  (0) 2022.10.24
문자열 숫자에 콤마 넣기  (0) 2022.10.14
블로그 이미지

SKY STORY

,
반응형

다음은 ViewController가 종료될 때 이전 ViewController에

Unwind Segue 이벤트가 수신되도록 설정하는 방법이다.

 

현재 QRcodeDetailViewController 가 popViewController될때 

이전QRcodeViewController의 특정함수(unwindToQRcodeViewController)를 호출하기 위한 설정

 

다음과 같이 ViewController가 push되어 있을 경우

QRcodeDetailViewController가 pop되어 종료 되었을 때 

Unwind Segue를 이용하여 QRcodeViewController의 

특정 함수 호출방법을 알아본다.

 

 

+ UINavigationViewController

   - QRcodeViewController

   - QRcodeDetailViewController

 

 

 

::::  QRcodeViewController  ::::

 

:::: storyboard ::::

QRcodeDetailViewController 를 Ctrl 버튼을 누른상태에서 Exit버튼 클릭

 

QRcodeDetailViewController 로 연결

 

‘manual’ 선택

 

QRcodeDetailViewController Presenting Segues 설정 확인

 

Unwind segue id 설정 (아래 예에서는 호출되는 함수 이름으로 할당함)

 

QRcodeDetailViewController 클래스 내에서 종료함수 호출

 

QRcodeDetailViewController 가 pop되고나서 종료되면 QRcodeViewController에 선언된 

unwindToQRcodeViewController()함수 호출

 

svc = QRcodeDetailViewController

dvc = QRcodeViewController

segueId = unwindToQRcodeViewController

반응형

'개발 > Note' 카테고리의 다른 글

Push Notification  (1) 2023.12.27
[Mac OS X] Apache Virtual Hosts 설정 설정  (0) 2023.02.21
랜덤 seed 초기화  (0) 2022.11.18
matplotlib 사용  (0) 2022.11.18
git 소스코드 비교  (0) 2022.02.23
블로그 이미지

SKY STORY

,

랜덤 seed 초기화

개발/Note 2022. 11. 18. 13:48
반응형

최초 seed값을 초기화 하지 않으면 동일한 결과 값이 반환되므로

반드시 초기화 처리 하도록 한다.

import random
from datetime import datetime
random.seed(datetime.now())

 

 

 

반응형

'개발 > Note' 카테고리의 다른 글

[Mac OS X] Apache Virtual Hosts 설정 설정  (0) 2023.02.21
Unwind Segue 사용방법  (0) 2023.01.11
matplotlib 사용  (0) 2022.11.18
git 소스코드 비교  (0) 2022.02.23
20진수 변환  (0) 2021.09.16
블로그 이미지

SKY STORY

,