tensorflow 패키지를 활용할 작업이 있어서 m1 실리콘 맥에 설치한 과정입니다.

불과 m1 맥이 출시했던 2~3년 전만 하더라도, m1 프로세서의 맥으로 작업하는 동료들 중 tensorflow 설치로 애먹은 동료들이 떠올랐습니다.

하지만 지금은 매우 쉽게 설치가 되네요.

 

python 3.8버전의 anaconda가 설치된 환경에서 진행하였으며 아래 pip 명령어만 입력해주면 어렵지 않게 설치가 가능합니다.

python3 -m pip install tensorflow
python3 -m pip install tensorflow-metal

 

설치 후 터미널 상에서 테스트한 결과입니다.

% python3
Python 3.8.19 (default, Mar 20 2024, 15:27:52) 
[Clang 14.0.6 ] :: Anaconda, Inc. on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import tensorflow as tf

>>> 
>>> tf.__version__
'2.13.0'
>>> tf.config.list_physical_devices('GPU')
[PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')]
>>>

 

앞으로 더 많은 작업을 쉽게 할 수 있겠네요 .

 

DONE!

Ubuntu 환경에서 pytorch 프로젝트 실행을 위해 설정하던 중 발생한 에러입니다.

 

'ModuleNotFoundError: No module named 'torch_geometric'

단순히 아래처럼 실행하면 되지 않을까 했는데, 여전히 같은 에러가 계속 발생했습니다.

pip install torch-geometric

설치전에..

우선 점검해볼것은 Nvidia 드라이버와 CuDA 설치 유무입니다.

아래 명령어로 각각의 요소들이 설치되었는지 확인을 하고, 설치가 안되었다면 반드시 설치를 해주어야합니다.

nvidia-smi
nvcc --version

Anaconda 환경에서 설치

저는 Anaconda 환경에서 진행하였습니다.

conda install pytorch torchvision torchaudio cudatoolkit=11.1 -c pytorch
pip install torch-scatter -f https://pytorch-geometric.com/whl/torch-1.9.0+cu111.html
pip install torch-sparse -f https://pytorch-geometric.com/whl/torch-1.9.0+cu111.html
pip install torch-cluster -f https://pytorch-geometric.com/whl/torch-1.9.0+cu111.html
pip install torch-spline-conv -f https://pytorch-geometric.com/whl/torch-1.9.0+cu111.html
pip install torch-geometric

 

위 명령어들을 순차적으로 진행하면 정상적으로 설치가 되면서 에러가 해결됩니다.

(tf) admin@gpu-server:/usr/local/cuda$ python
Python 3.9.16 (main, Mar  8 2023, 14:00:05) 
[GCC 11.2.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import torch_geometric

 

GOOD!

윈도우 PC에서 서비스할 프로젝트가 시작되었습니다.

윈도우에 Docker를 설치해서 하려다 번거로워서, ASP.NET 개발 할 때 IIS를 사용한 경험을 되살려서 IIS에 Flask서버를 가동하였습니다.

 

아래와 같은 환경에서 설정하였습니다.

  • Anaconda3
  • Windows 10
  • python <= 3.8

아래는 도움받은 블로그 글입니다

https://blog.gapmoe.net/entry/IIS%EC%97%90-Flask-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0
https://urame.tistory.com/entry/IIS%EC%99%80-Flask-Python-%EC%97%B0%EB%8F%99

 


 

1. IIS & CGI 설정

윈도우 OS에 설치되어 있는 IIS를 활성화해줘야합니다.

아래 처럼 '인터넷 정보 서비스(IIS)' 내 항목들과 'CGI'를 체크하여 확인을 누른 후 설치과정을 진행합니다

 


2. FLASK 실행을 위한 설정

다른 서버 어플리케이션과 달리 설정할 변수들이 많아 번거로울 수도 있지만 한 번 세팅해두면 의외로 IIS도 서버 관리에 장점이 많은 어플리케이션입니다.

 

1) python 실행 경로

먼저 Python은 Ananconda3에서 실행되는 환경입니다.

저 같은 경우 가상환경 이름은 'flask'로 지정했으며 python이 실행되는 인터프리터 경로는 아래와 같습니다

C:\anaconda3\envs\flask\python.exe

 

2) 필요한 파이썬 패키지

wfastcgi, flask 두 가지 패키지가 필요합니다. 

아래 처럼 anaconda prompt에서 설치를 진행해줍니다.

(flask) C:\WINDOWS\system32> pip install wfastcgi
(flask) C:\WINDOWS\system32> pip install flask

 

3) flask 웹 서버 경로 설정

iis 가 정상적으로 설치되었다면 

"C:\inetpub\" 디렉토리가 생성됩니다. 

참고로 웹 브라우저상에서 'http://localhost' 경로를 호출하면 아래와 같은 페이지를 불러오게 되며, 기본적인 iis 설치가 완료된 상태임을 확인할 수 있습니다.

 

flask 프로젝트 경로는 아래의 경로에서 실행할 예정입니다.

C:\inetpub\flask

 

'wfastcgi.py'파일을 flask 프로젝트 폴더에 복사해줍니다.

저 같은 경우는 flask라는 anaconda3 가상공간에 wfastcgi 패키지를 설치하였으므로 해당 경로에서 파일을 찾을 수 있었습니다.

그리고 위 파일을 복사하여 flask 프로젝트에 복사해주고 'app.py'파일을 생성해줍니다

 

 


3. IIS와 Python Flask Mapping 설정

여기서 부터가 실제로 설정하는 단계입니다.

IIS 어플리케이션을 실행한 후 사이트 -> 웹 사이트 추가를 해줍니다.

'flask_test'라는 웹 사이트를 추가하였으며 포트는 '5000'으로 지정하였습니다

 

flask_test 웹 사이트의 처리기 매핑 설정 -> 처리기 매핑 클릭

 

우측 상단에 '모듈 매핑 추가',

아래 화면과 같이 설정값을 입력해줘야 합니다.

'파이썬 인터프리터 경로|프로젝트 내 wfastcgi.py 경로'

'|'기호로 연결된다는 점을 유의해야합니다.

C:\anaconda3\envs\flask\python.exe|C:\inetpub\flask\wfastcgi.py

 

마지막으로 FastCGI를 설정해줘야합니다.

IIS내 최상단 장치명 -> FastCGI 설정 클릭 -> 환경변수 설정하여 아래 두 개의 환경 변수를 설정합니다

NAME : PYTHONPATH
VALUE : C:\inetpub\flask  (flask 프로젝트 폴더 경로)
---------------------
NAME : WSGI_HANDLER
VALUE : app.app

 

 


4. Flask Test 페이지 코드 입력

flask 프로젝트 폴더 내 'app.py'파일에 아래와 같은 코드를 입력합니다.

from flask import Flask

app = Flask(__name__)

@app.route('/')
def home():
    return "Hello, World!"

if __name__ == '__main__':
    app.run()

 

그리고 웹 사이트 생성하면서 설정한 포트(5000)로 접속하여 결과값을 확인합니다.

 

이미지 전처리를 위해서 이미지 파일 내에 불필요한 부분을 제거하기 위해서 특정 객체가 포함된 부분(ROI와 비슷)만 남기는 작업을 해야했습니다.

cv2로 bitmap 처리를 해보니 시간도 많이 소요되고 효율성이 좋지 않아 ai model을 사용하기로 하였고 결과는 성공적이었습니다.


 정지 영상(image)에 어떤 객체들이 있는지 python으로 yolo 모델을 활용하여 이미지 내의 객체들을 찾는(detection) 코드를 구현하였습니다.

추가로 이미지 내에서 인식(Detection)된 객체의 좌표를 불러와서 특정 객체만 남기고 이미지를 crop하는 기능까지 구현한 내용입니다.


설치

1. pythorch

pip install torch torchvision torchaudio

 

2. yolo v5 repository

pip install yolov5

git clone https://github.com/ultralytics/yolov5.git
cd yolov5
pip install -U -r requirements.txt

 * clone할 공간은 작업중인 폴더 안에서 실행해도 무관합니다


Image Detection 사용하기 (1) 터미널 명령어 (단 한 줄)

terminal 명령어로 이미지 경로를 지정해주기만해도 간단하게 사용할 수 있습니다.

yolov5 폴더로 이동한 후 아래 명령어를 입력하면 웹캠 영상, 정지된 영상을 바로 실습할 수 있습니다.

python detect.py --source /path/image/free-video-854671.jpg

테스트에 사용한 이미지 :  https://www.pexels.com/video/cars-on-highway-854671/

 

 

명령어 실행 결과 ...

user@mypc % python detect.py --source /path/image/free-video-854671.jpg
detect: weights=yolov5s.pt, source=/path/project/free-video-854671.jpg, data=data/coco128.yaml, imgsz=[640, 640], conf_thres=0.25, iou_thres=0.45, max_det=1000, device=, view_img=False, save_txt=False, save_csv=False, save_conf=False, save_crop=False, nosave=False, classes=None, agnostic_nms=False, augment=False, visualize=False, update=False, project=runs/detect, name=exp, exist_ok=False, line_thickness=3, hide_labels=False, hide_conf=False, half=False, dnn=False, vid_stride=1
YOLOv5 🚀 v7.0-267-gc42aba5 Python-3.8.18 torch-2.1.2 CPU

Fusing layers... 
YOLOv5s summary: 213 layers, 7225885 parameters, 0 gradients, 16.4 GFLOPs
image 1/1 /path/project/free-video-854671.jpg: 352x640 9 persons, 7 cars, 1 bus, 2 trucks, 80.7ms
Speed: 0.7ms pre-process, 80.7ms inference, 7.3ms NMS per image at shape (1, 3, 640, 640)
Results saved to runs/detect/exp5

 

맨 아래줄에 detection이 끝난 결과에 대한 경로가 있습니다.

Results saved to runs/detect/exp5

결과이미지 경로 이동

 

결과 이미지

분석이 끝난 이미지를 간단하게 확인할 수 있습니다.

truck, bus같은 경우 인식 오류가 발생한듯 싶으나 대체로 잘 인식한 걸 확인할 수 있습니다.


 

Image Detection 사용하기 (2) 터미널 명령어를 파이썬 코드로 실행

import subprocess

# 이미지 파일의 경로
image_path = 'path/to/your/image.jpg'

# detect.py 스크립트를 파이썬으로 호출
result = subprocess.run(['python', 'yolov5/detect.py', '--source', image_path])

terminal 에서 실행한 명령어를 파이썬의 subprocess 패키지를 이용해 코드로 실행한 코드입니다.


 

Image Detection 사용하기 (3) 파이썬 코드로 실행

모델을 불러오고, 분석할 이미지 파일을 모델에 넣어준 뒤에 결과를 받는 코드입니다.

파이썬 코드로 실행하게 되면 결과에 대한 다양한 값들을 확인하고 응용할 수 있습니다.

import torch
from pathlib import Path
from yolov5 import detect
import os
from PIL import Image, ImageDraw

def vehicle_detection_yolov5(image_path):
    model = torch.hub.load('ultralytics/yolov5:master', 'yolov5s')
    # Inference
    results = model(image_path)
    # Results, change the flowing to: results.show()
    results.show()
    # results.save()
    print(results.s)
    
    return results
    

data_path = 'car_image'
image_files = [f for f in os.listdir(data_path) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.gif'))]

for image_file in image_files:
    print(image_file)
    results = vehicle_detection_yolov5(f'{data_path}/{image_file}')
    print(results.s)

 

'car_image'라는 폴더 안에 있는 이미지파일들을 일괄적으로 분석할 수 있게 코드를 구현하였습니다.

 

python으로 postgres를 다룰 수 있습니다.

 

 

1. 파이썬 패키지 설치

postgres를 다루기 위한 파이썬 패키지(psycopg2)을 설치해줍니다.

pip install psycopg2-binary

 

2. postgres DB 연결

# PostgreSQL 연결 정보 설정
db_params = {
    "dbname": "postgres",
    "user": "postgres",
    "password": "1234",
    "host": "localhost",
    "port": "5432"
}

# PostgreSQL 연결
conn = psycopg2.connect(**db_params)

위 코드로 간단하게 연결할 수 있습니다.

db접근을 위한 연결정보인 db_params 변수에 오타나 잘못된 값이 입력되면 psycopg2.connect() 함수가 실행될 때 에러가 발생합니다.

오류가 발생했다면 DB가 제대로 설치되었는지, 오타가 없는지 확인이 필요합니다.

 

3. 간단한 테이블을 생성하고 INSERT 쿼리를 실행해보기

# 테이블 생성 쿼리
create_table_query = """
CREATE TABLE IF NOT EXISTS logging (
    log_id SERIAL PRIMARY KEY,
    log_datetime TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    car_number VARCHAR(20),
    image_file_name VARCHAR(255),
    json_file_name VARCHAR(255),
    saved_location VARCHAR(255)
);
"""

cursor = conn.cursor()
cursor.execute(create_table_query)



# 로그 정보
log_data = {
    "car_number": "ABC123",
    "image_file_name": "example_image.jpg",
    "json_file_name": "example_json.json",
    "saved_location": "/path/to/save/location"
}

# 로그 정보 삽입
insert_query = """
INSERT INTO logging (car_number, image_file_name, json_file_name, saved_location)
VALUES (%s, %s, %s, %s);
"""
cursor.execute(insert_query, (
    log_data["car_number"],
    log_data["image_file_name"],
    log_data["json_file_name"],
    log_data["saved_location"]
))

# 변경사항 커밋
conn.commit()

# 연결 및 커서 닫기
cursor.close()
conn.close()

 

위 예제는 주차장이나 건물 차량 입구의 입출차에 대한 cctv 기록을 저장하는 테이블입니다.

'logging'이라는 테이블을 만들고  자동차 번호, 차량 촬영 이미지, 차량 정보 json 파일, cctv 위치 등의 데이터를 insert 해봤습니다.

 

 

DBeaver로 도 확인해보니 데이터가 잘 삽입되었네요 :)

외부서비스에서 제공하는 데이터를 관리용 웹사이트를 이용중인 상황.

하지만 일괄적으로 입력할 데이터 양도 많고 input tag에 값을 일일이 넣을 경우 사이트가 느려지거나 여러 값들을 일일이 넣어줘야하는 번거로운 상황이 있었다.


1. Python requests와 BeautifulSoup4 package 이용

 데이터를 입력하고 submit할 때 호출하는 api와 payload(parameters)값들을 추출하였고 웹브라우저 상에서 테스트 시 정상적으로 작동하였지만 requests 패키지를 활용하여 api를 다루다보니 '세션'관련 문제가 발생하였다.

 form-data에 id와 pw를 입력하여 post로 전송하였으나 이 역시 마찬가지 였으며, headers의 세팅 또한 웹브라우저 환경과 동일하게 하였으나 마찬가지 였다.


2. python selenium 활용

입력할 값들을 미리 변수에 넣어서 selenium으로 자동화를 하려하였으나, 관리하는 데이터의 값들과 사이트 폼이 달라서 사실상 의미없는 작업이었다.


3. 세션유지를 위한 문제해결 방법!

결론적으로 requests를 활용하여 api를 호출하는 방법이 가장 효과적이었기에, 세션 유지를 하고자 1 + 2 방법을 혼용하였다.

최초엔 selenium으로 해당사이트에 로그인을 한 뒤, session 값을 가져와서 requests로 제어하니 원하던 작업을 진행할 수 있었다.

 

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
import time

driver = webdriver.Chrome(service= Service(ChromeDriverManager().install()))

url = "https://test.com"
driver.get(url)
time.sleep(5)


import requests
# 여기에서 로그인 작업을 수행
# 사용자명과 비밀번호 입력
username_element = driver.find_element(By.XPATH, '//*[@id="UserId"]')  # 사용자명 입력 필드
password_element = driver.find_element(By.XPATH, '//*[@id="UserPassword"]')  # 비밀번호 입력 필드

username_element.send_keys("id")
password_element.send_keys("password")

# 로그인 버튼 클릭
login_button = driver.find_element(By.XPATH, '//*[@id="myForm"]/button')  # 로그인 버튼 선택
login_button.click()



# Selenium을 사용하여 페이지 이동 및 추가 작업 수행

# 이제 requests를 사용하여 세션을 유지하면서 추가 요청을 보낼 수 있음
session = requests.Session()
# 필요한 추가 요청 설정, 세션에 쿠키 등을 추가
# 예를 들어, session.cookies.update(driver.get_cookies())를 사용하여 브라우저 쿠키를 세션에 추가

# requests를 사용하여 추가 요청을 보내거나 데이터를 추출
response = session.get("https://test.com/example/list")
response.text



# Selenium에서 세션 쿠키 가져오기
selenium_cookies = driver.get_cookies()

# requests 세션 초기화
session = requests.Session()

# Selenium에서 가져온 세션 쿠키를 requests 세션에 추가
for cookie in selenium_cookies:
    session.cookies.set(cookie['name'], cookie['value'])

# requests를 사용하여 추가 요청을 보냄
response = session.get("https://test.com/example/list")




# 요청을 보낼 URL
url = "https://test.com/api/sample"

# POST 요청에 사용할 데이터
data = {
    .........
}

response = session.post(url, data=data)

 

핵심은 마지막 줄 처럼 'requests.post()'가 아닌 'session.post()'로 호출한다는 점! 

* 참고 : pytutbe 모듈은 python 3.6 버전 이상에서 작동합니다!

 

 유튜브 영상을 다운로드 받는 방법은 여러가지지만 써드파티 유틸리티를 다운받거나 사용하려니 번거로움이 있어서 파이썬으로 간단하게 구현해봤습니다.

 유튜브 플레이리스트에 있는 영상도 한 번에 다운로드 할 수 있게 구현한 코드이니 주석 참고해주시고 궁금하신 점은 무엇이든 남겨주세요 ㅎ


프로그램 순서는 대략적으로 아래와 같습니다.

1. 다운로드 받을 유튜브 플레이리스트 URL을 리스트로 생성해줌

2. 유튜브 플레이리스트 제목을 이름으로 갖는 폴더를 생성함

3. 생성한 폴더 안에 유튜브 플레이리스트 안에 있는 모든 영상을 다운로드 해줌

from pytube import YouTube
from pytube import Playlist
import os

# 참고 1) 폴더 생성 : https://data-make.tistory.com/170
# '폴더명'을 매개변수로 받는 함수, 파라미터로 받은 폴더명과 같은 폴더가 없으면 폴더를 새로 생성해줌
def createFolder(directory):
    try:
        if not os.path.exists(directory):
            os.makedirs(directory)
    except OSError:
        print ('Error: Creating directory. ' +  directory)


# 참고 2) pytube 기본 사용법 :  https://pytube.io/en/latest/user/playlist.html
# '플레이리스트 URL'을 매개변수로 받는 함수
def playlist_download(playlist_url):
    # 플레이리스트 제목을 폴더명으로 사용하여 폴더를 생성할 것 -> 플레이리스트 이름 가져오기
    p = Playlist(playlist_url)
    
    ############################################################################
    # 참고) 아래 방법으로 간다하게 구현할 수 있으나 고해상도 영상을 다운받을 수 없음!!    
    # for video in p.videos:
    #     video.streams.first().download(DOWNLOAD_FOLDER)
    ############################################################################
    
    print(p.title)
    DOWNLOAD_FOLDER = p.title
    
    # 플레이리스트 제목으로 폴더 생성
    createFolder(DOWNLOAD_FOLDER)

    # 플레이리스트 안의 각 영상마다 URL을 가져온 후 지정한한 경로에 다운로드를 해줌
    for url in p.video_urls:
        print(url)
        yt = YouTube(url)
        stream = yt.streams.get_highest_resolution()            
        stream.download(output_path=DOWNLOAD_FOLDER)
        
    print(p.title + " 다운로드 완료\n")

if __name__ == "__main__":
    
    # 다운로드 받을 플레이리스트 url을 리스트에 넣어줌
    playlist_url =  ['https://www.youtube.com/watch?v=~~~~~~', 
                 'https://youtube.com/playlist?list=~~~~~~~~~~~',
                 'https://youtube.com/playlist?list=~~~~~~~',
                 'https://youtube.com/playlist?list=~~~~~~~~~',
                 'https://youtube.com/playlist?list=~~~~~~~~-',
                 'https://youtube.com/playlist?list=~~~~~~-5mWbx0zdG0betdeoL']
    
    # 플레이리스트 url을 담고 있는 리스트를 반복문 안 에서 다운로드 함수 매개변수에 할당해주면서 실행
    for i in playlist_url:
        playlist_download(i)

추가로 단일 유튜브 영상을 받고 싶으시면 아래 처럼 간단하게 구현할 수 있습니다.

from pytube import YouTube
url = '유튜브 동영상 url (플레이리스트 url 아님!)'
DOWNLOAD_FOLDER = '다운로드 받을 폴더 경로' #빈값('')으로 두면 파이썬 소스파일이 있는 폴더에 다운로드 받습니다
yt = YouTube(url)
stream = yt.streams.get_highest_resolution()            
stream.download(output_path=DOWNLOAD_FOLDER)

+ Recent posts