에러화면

 

 

AI 학습데이터에 활용할 데이터셋을 불러오는 중에 아래와 같은 에러가 발생했습니다.

 

Error opening file

Unexpected UTF-8 BOM (decode using utf-8-sig): line 1 column 1 (char 0)

Make sure /Users/kaflix_wanya/Desktop/current/카스캐닝 데이터셋/parts_labeling/0831_1996개_jpg+json/100210979526_576120.json is a valid label file.'

 

문제는 annotating, labeling 데이터를 저장하고 있는 json 파일을 불러오면서 발생한 에러인데요.

이 많은 json파일의 인코딩을 어떻게 바꿀까 고민하다가 파이썬 코드로 해결하였습니다.

 

데이터셋 경로를 불러와서 -> 데이터셋 경로 내의 모든 json 파일을 읽어온 후 -> UTF-8로 다시 인코딩 -> 변경된 인코딩형식으로 json파일 저장

 

위 순서대로 작업한 후 다시 labelme에서 'open dir'을 통해 학습데이터 경로를 불러오니 오류없이 잘 작동합니다.

아래는 코드입니다.

import os
import json

def convert_json_encoding(folder_path):
    # 주어진 폴더 내의 모든 파일에 대해 반복
    for filename in os.listdir(folder_path):
        filepath = os.path.join(folder_path, filename)
        
        # 파일이 JSON 파일인지 확인
        if filename.endswith('.json'):
            # JSON 파일을 UTF-8 인코딩으로 읽어들임
            with open(filepath, 'r', encoding='utf-8-sig') as file:
                data = json.load(file)
            
            # 같은 파일을 UTF-8 인코딩으로 다시 저장
            with open(filepath, 'w', encoding='utf-8') as file:
                json.dump(data, file, ensure_ascii=False, indent=4)

# 변경할 폴더 경로를 지정
folder_path = '/User/Dataset'

# 함수 호출
convert_json_encoding(folder_path)

 

이미지 전처리를 위해서 이미지 파일 내에 불필요한 부분을 제거하기 위해서 특정 객체가 포함된 부분(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로 도 확인해보니 데이터가 잘 삽입되었네요 :)

oracle, bigquery만 주로 활용하다가 postgres를 사용할 일이 생겼습니다.

매우 반갑죠.

설치부터 시작하고 postgres에 대해 알아가야겠습니다.


설치

macOS환경에서 Docker로 설치합니다.

(Docker가 설치되어있다는 가정하에) 매우 간단해요.

docker pull postgress
docker run --name my-postgres-container -e POSTGRES_PASSWORD=1234 -p 5432:5432 -d postgres
docker exec -it my-postgres-container psql -U postgres

 

* POSTGRES_PASSWORD와 포트는 사용환경에 맞게 설정하시면 됩니다.

저는 테스트용이기에 1234로 작성했습니다

 

DB 접속

DBeaver를 실행하여 설치한 postgress DB에 접속이 잘 되는지 확인해보겠습니다.

 

 

 

접속이 잘 됩니다.

 

 

macOS로 작업하다보니 어느 순간부터 텍스트나 이미지를 복사하여도 붙여넣기가 안 되거나, 이전에 복사한 데이터를 붙여넣는 현상이 발생하였습니다.

 재부팅도 해보고 키보드 문제인가 의심도 해봤지만 해결이 안되었는데 말끔하게 해결할 수 있는 방법이 있네요.

바로 'pboard' 프로세스를 재시작해주면 됩니다

 

pboard란?

macOS에서 pboard는 Pasteboard, 즉 클립보드와 관련된 명령줄 유틸리티입니다. pboard는 텍스트와 이미지와 같은 데이터를 클립보드에 복사하거나 클립보드에서 데이터를 가져오는 데 사용됩니다.

일반적으로, pboard는 텍스트나 이미지와 같은 데이터를 파일이나 명령줄을 통해 전달하고자 할 때 유용하게 사용될 수 있습니다. macOS에서는 pbcopy 명령어로 클립보드에 복사하고, pbpaste 명령어로 클립보드에서 데이터를 가져올 수 있습니다. 이러한 명령어들은 주로 스크립트나 명령줄 작업에서 활용됩니다


1. '활성 상태 보기' app으로 진입하여 'pboard' 프로세스를 종료시켜 준 후,

2. 터미널을 실행하여 'killall pboard' 명령어를 입력하면 정상적으로 복사 붙여넣기가 작동합니다

dataframe의 한 컬럼에 대해 unique한 값을 얻기위해 'datafame['컬럼명'].uique()' 메소드를 사용할 수 있고 결과값이 list가 아닌 ndarray형태로 생성돼죠.

 

unique() 메소드를 이용하여 생성된 list를 다른 컬럼으로 생성하거나 비교를 위한 list로 활용하려하던 중 ndarray 형태로는 원하는 작업에 지장이 생겨서 list형태로 변환하는 과정입니다.

 

numpy 형태의 배열의 경우 list와 다르게 ','로 구분이 안되어있습니다.

list형태로 간단하게 변경할 수 있습니다.

import numpy as np
unique_values = df_proceed['시간대별'].unique()
print(unique_values)
print(type(unique_values))

# ndarray to list
python_list = unique_values.tolist()
# 결과 확인
print(python_list)

ndarray

Bigquery Table → CSV file download-> Python Dataframe Read 과정에서 발생한 문제

파이썬으로 데이터 분석을 위해 빅쿼리 테이블을 csv로 받았습니다. (google.bigquery packages는 사용하지 않음)

연, 월, 일만을 갖고있는 Datetime을 나타내는 컬럼의 포맷을 변경하면서 발생한 문제입니다.


1. bigquery 테이블을 가져온 csv 파일 Read

Datetime 컬럼 예시

위 컬럼을 yyyy-mm-dd 형태로 포맷을 변경할거죠

 

2. dt.strtime() 메소드 사용 → 에러 발생

dataframe_of_csv = pd.read_csv('bigquery_table.csv')
dataframe_of_csv["SEARCH_DATE"] =  dataframe_of_csv['SEARCH_DATE'].dt.strftime('%Y-%m-%d')

"AttributeError: Can only use .dt accessor with datetimelike values" 에러가 발생합니다

Can only use .dt accessor with datetimelike values는 .dt 접근자는 datetimelike 값에만 사용할 수 있다는 의미입니다. 즉, 해당 컬럼이 datetimelike 데이터 형식이 아니라서 발생한 오류입니다.

 

3. pd.to_datetime(dataframe['컬럼명']) 메소드 사용 → 해결

코드를 아래와 같이 수정하여 원하는 형태로 formmating을 할 수 있습니다.

dataframe_of_csv = pd.read_csv('bigquery_table.csv')
dataframe_of_csv['SEARCH_DATE'] = pd.to_datetime(dataframe_of_csv['SEARCH_DATE'])

Datetime 컬럼 변환 예시

 


결론

bigqeury, csv(file), python pandas(dataframe) 등 테이블 형태를 여러 방법으로 다루다보면 column의 데이터 형으로 인한 문제는 흔하게 발생합니다.

특히 Datetime의 경우 ETL과정에서 형식의 일치가 무엇보다 중요하죠

'ORA-01722: 수치가 부적합합니다'

Oracle 쿼리 실행하다보면 자주 발생하는 에러중 하나입니다.

 

집계함수를 사용하던 중에 발생한 오류를 해결한 과정입니다.

'date_column'에 'yyyy-mm-dd'형태의 일자가 있고, 'a_columnm'에 날짜별 데이터가 있다고 가정했을때,

날짜별로 'a_columnm'이 갖는 값들에 대해 count를 하려합니다.

SELECT TO_CHAR(date_column, 'YYYY-MM') AS month,
       a_column,
       COUNT(*) AS count_per_value
FROM your_table_name
GROUP BY TO_CHAR(date_column, 'YYYY-MM'), a_column
ORDER BY month, a_column;

 

위 쿼리만 봤을 때 문제없이 실행되어야 하지만 'ORA-01722: 수치가 부적합합니다' 에러가 발생합니다.

TO_CHAR() 함수가 실행될 때 데이터 형식에 관련된 문제라고 판단되어 쿼리를 아래와 같이 실행하였습니다.

SELECT TO_CHAR(TO_DATE(date_column), 'YYYY-MM') AS month,
       a_column,
       COUNT(*) AS count_per_value
FROM your_table_name
GROUP BY TO_CHAR(TO_DATE(date_column), 'YYYY-MM'), a_column
ORDER BY month, a_column;

 

'date_column'의 형 변환을 TO_DATE(date_column) 처럼 해준 뒤에 실행하니 정상적으로 실행되었습니다.

다시 확인해보니 'date_column'의 형식이 character형식이었네요.

 

이처럼 oracle은 데이터 형시에 더 민감한 모습을 보여줍니다.

허무하게 해결되었네요 :)

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

하지만 일괄적으로 입력할 데이터 양도 많고 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()'로 호출한다는 점! 

 flask로 간단한 웹 페이지를 만들어서 Local 환경에서 테스트한 후, 서버에 docker container로 서비스를 가동하니 접속이 안되는 문제가 발생하였습니다.

if __name__ == '__main__':
    cache.clear()
    app.run(debug=True, port=8888)

1. port binding도 잘 해주었고,

2. host도 오타없이 잘 작성하였고,

3. python 파일도 정상적으로 실행되고 있었습니다.

 


host='0.0.0.0'

해결책은 간단했습니다.

 

Flask 애플리케이션을 실행할 때, Flask 서버는 기본적으로 127.0.0.1 또는 localhost에서만 연결을 허용합니다.

따라서 "Running on http://127.0.0.1:8888"과 같이 로컬 주소에서만 서버가 실행되는 것이 기본 동작입니다.

하지만 "Running on all addresses (0.0.0.0)" 또는 "Running on http://172.17.0.2:8100"와 같이 모든 주소에서 서버를 실행하려면 Flask 애플리케이션을 아래와와 같은 코드로 실행해야합니다.

if __name__ == '__main__':
    cache.clear()
    app.run(debug=True, host='0.0.0.0', port=8888)

바로 run()함수에 'host = '0.0.0.0'으로 지정해두니 서버 호스트로도 접속이 정상적으로 이루어졌습니다.

 

아래는 chat-gpt가 답해준 내용입니다. 훌륭한 설명이네요.

이렇게 하면 Flask 서버가 모든 주소 (0.0.0.0)에서 클라이언트 연결을 허용하며, 외부 클라이언트도 액세스할 수 있게 됩니다. 
단, 이렇게 서버를 열면 보안상 주의가 필요하며, 특히 개발 환경에서만 사용해야 합니다. 
production(운영) 환경에서는 보안 검토와 관련된 설정을 고려해야 합니다.
따라서 Flask 애플리케이션을 모든 주소에서 실행하고 싶다면 위의 코드를 Flask 애플리케이션 코드 안에서 사용하십시오.

 

python flask로 구현한 웹 페이지를 가상화공간(docker container)에서 서비스하려할 때 위와 같은 문제가 발생하시는 분들께 도움이 되었으면 합니다.

 

+ Recent posts