2024년 9월 4일 수요일

bookscan image enhance(python으로 스캔한 책 전처리 )

 book scan(카메라로 찍은 사진) 한 파일의 품질을 올리는 방법을 고민을 하게 되었습니다.

ChatGPT를 이용해서 굉장히 많은 기법들을 소개(?) 받았고, 이것 저것 조합하면서 가장 무난한 방법을 찾아봤습니다.


sharpen -> bilateralFilter -> 배경 부분만 밝기 높이기 순으로 되어 있습니다.


아래 코드를 참고하시고, 모르는건 ChatGPT에 문의해보면 자세히 알려줍니다.

세상이 편해졌네요. 그래도 이것저것 해보고 제일 좋은지 어떤지는 인간이 좀 판단을 해야합니다. filename만 적절히 변경 하시면 됩니다.

filename=r'D:\temp\1_20240830\0066.jpg'

import cv2
import numpy as np
import matplotlib.pyplot as plt

# 이미지 불러오기
image = cv2.imread(filename)
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

# 1. 샤프닝 적용
kernel_sharpen = np.array([[-1, -1, -1],
[-1, 9, -1],
[-1, -1, -1]])
sharpened_image = cv2.filter2D(image_rgb, -1, kernel_sharpen)

# 2. bilateralFilter 적용
bilateral_filtered_image = cv2.bilateralFilter(sharpened_image, d=9, sigmaColor=75, sigmaSpace=75)

# 3. Grayscale 변환
gray = cv2.cvtColor(bilateral_filtered_image, cv2.COLOR_RGB2GRAY)

# Otsu's Thresholding을 사용하여 글씨와 배경 분리
# Otsu's Method는 최적의 threshold 값을 자동으로 선택함
_, mask = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

# 선택된 Otsu Threshold 값 출력
otsu_threshold_value = _ # Otsu가 선택한 최적의 Threshold
print(f"Otsu's Threshold Value: {otsu_threshold_value}")

# 배경 마스크를 생성 (글씨가 있는 부분은 제외)
background_mask = cv2.bitwise_not(mask)

# 배경을 밝게 조정 (50 정도 밝기 증가)
upcolor = 50
background_brightened = cv2.add(bilateral_filtered_image, np.array([upcolor, upcolor, upcolor], dtype=np.float32), mask=background_mask)

# 글씨 부분은 원본에서 그대로 가져옵니다.
final_image = cv2.add(background_brightened, cv2.bitwise_and(bilateral_filtered_image, bilateral_filtered_image, mask=mask))

# 결과 출력
plt.figure(figsize=(18, 6))
plt.subplot(131), plt.imshow(image_rgb), plt.title('Original')
plt.subplot(132), plt.imshow(bilateral_filtered_image), plt.title('Sharpened + Bilateral Filtered')
plt.subplot(133), plt.imshow(final_image), plt.title('Background Brightened with Otsu Thresholding')
plt.axis('off')
plt.show()


소스는 아래 링크 참고

Create jpg_to_enhance3.py · donarts/sourcecode@54ee6af · GitHub


추가 개선 


threshold -> adaptiveThreshold 로 변경함

filename=r'D:\temp\0005.jpg'

import cv2
import numpy as np
import matplotlib.pyplot as plt

# 이미지 불러오기
image = cv2.imread(filename)
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

# 1. 샤프닝 적용
kernel_sharpen = np.array([[-1, -1, -1],
[-1, 9, -1],
[-1, -1, -1]])
sharpened_image = cv2.filter2D(image_rgb, -1, kernel_sharpen)

# 2. bilateralFilter 적용
bilateral_filtered_image = cv2.bilateralFilter(sharpened_image, d=9, sigmaColor=75, sigmaSpace=75)

# 3. Grayscale 변환
gray = cv2.cvtColor(bilateral_filtered_image, cv2.COLOR_RGB2GRAY)

# 4. GaussianBlur 적용 (Thresholding 전에)
gray = cv2.GaussianBlur(gray, (5, 5), 0)

# Otsu's Thresholding을 사용하여 글씨와 배경 분리
# Otsu's Method는 최적의 threshold 값을 자동으로 선택함
#_, mask = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

# Adaptive Thresholding을 사용하여 글씨와 배경 분리
mask = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY_INV, 41, 8)

# 선택된 Otsu Threshold 값 출력
#otsu_threshold_value = _ # Otsu가 선택한 최적의 Threshold
#print(f"Otsu's Threshold Value: {otsu_threshold_value}")

# 배경 마스크를 생성 (글씨가 있는 부분은 제외)
background_mask = cv2.bitwise_not(mask)

# 배경을 밝게 조정 (50 정도 밝기 증가)
upcolor = 50
background_brightened = cv2.add(bilateral_filtered_image, np.array([upcolor, upcolor, upcolor], dtype=np.float32), mask=background_mask)

# 글씨 부분은 원본에서 그대로 가져옵니다.
final_image = cv2.add(background_brightened, cv2.bitwise_and(bilateral_filtered_image, bilateral_filtered_image, mask=mask))

# 결과 출력
plt.figure(figsize=(18, 6))
plt.subplot(131), plt.imshow(image_rgb), plt.title('Original')
#plt.subplot(132), plt.imshow(bilateral_filtered_image), plt.title('Sharpened + Bilateral Filtered')
plt.subplot(132), plt.imshow(mask, cmap='gray'), plt.title('mask')
#plt.subplot(132), plt.imshow(gray, cmap='gray'), plt.title('gray')
plt.subplot(133), plt.imshow(final_image), plt.title('Background Brightened with Otsu Thresholding')
plt.axis('off')
plt.show()


sourcecode/python/example/_64_jpeg_to_pdf/jpg_to_enhance3.py at main · donarts/sourcecode · GitHub



2024년 9월 1일 일요일

remove background(배경 제거) in python, rembg 사용

AI로 이미지를 만들면 배경을 제거해야 하는 경우가 있는데 파일이 여러개면 대량으로 제거하기가 불편합니다. 그래서 python 스크립트로 만들어 봤습니다.

GPT로 생성해봤는데 생각보다 잘되지 않아서 작업한 코드를 공유합니다.

이미지 파일들에서 배경을 제거하고 투명 배경을 적용하여 다른 폴더에 저장하는 파이썬 코드입니다. 먼저 이 작업을 수행하기 위해서는 Pillow와 rembg 라이브러리를 사용 해야 합니다.

rembg는 이미지에서 배경을 제거하는데 사용되며, Pillow는 이미지를 처리하고 저장하는 데 사용됩니다.


아래 패키지들을 설치해줍니다.

pip install pillow rembg


rembg 설치하는데 시간이 오래 걸리니 참고 해주세요

전체 코드

import os
from PIL import Image
from rembg import remove, new_session

def remove_background_and_save(source_folder, destination_folder, model_name):
# 세션 객체 생성, 특정 모델 지정
session = new_session(model_name=model_name)

# 소스 폴더에서 모든 파일을 검색
for filename in os.listdir(source_folder):
if filename.endswith('.webp') or filename.endswith('.jpg') \
or filename.endswith('.png') or filename.endswith('.jpeg'):
file_path = os.path.join(source_folder, filename)
output_filename = os.path.splitext(filename)[0] + '.png' # 원래 파일 이름에서 확장자를 png로 변경
output_path = os.path.join(destination_folder, output_filename)
print(file_path)
if os.path.exists(output_path):
print(f"File {output_path} already exists, skipping...")
continue

# 이미지 파일을 열고 배경 제거
with Image.open(file_path) as img:
input_data = img.convert("RGBA") # rembg RGBA 포맷의 바이트 데이터가 필요

# alpha_matting_foreground_threshold: 알파 매팅에서 전경 임계값을 설정합니다. 기본값은 240입니다.
# alpha_matting_background_threshold: 알파 매팅에서 배경 임계값을 설정합니다. 기본값은 10입니다.
result_data = remove(input_data, session=session, alpha_matting=True,
alpha_matting_background_threshold=128) # 배경 제거

# 결과 이미지 생성 및 저장
result_data.save(output_path, format='PNG') # PNG 형식으로 저장


if __name__ == "__main__":
# 폴더 경로 설정 (사용자가 수정 가능)
source_folder = r'D:\dev\game_art\dalle\monster'
destination_folder = r'D:\dev\game_art\dalle\monster\rbg'

model_name = 'birefnet-general' # 'u2net', 'u2net_human_seg' 등 다른 모델도 사용 가능

'''
The available models are:

u2net (download, source): A pre-trained model for general use cases.
u2netp (download, source): A lightweight version of u2net model.
u2net_human_seg (download, source): A pre-trained model for human segmentation.
u2net_cloth_seg (download, source): A pre-trained model for Cloths Parsing from human portrait.
Here clothes are parsed into 3 category: Upper body, Lower body and Full body.
silueta (download, source): Same as u2net but the size is reduced to 43Mb.
isnet-general-use (download, source): A new pre-trained model for general use cases.
isnet-anime (download, source): A high-accuracy segmentation for anime character.
sam (download encoder, download decoder, source): A pre-trained model for any use cases.
birefnet-general (download, source): A pre-trained model for general use cases.
birefnet-general-lite (download, source): A light pre-trained model for general use cases.
birefnet-portrait (download, source): A pre-trained model for human portraits.
birefnet-dis (download, source): A pre-trained model for dichotomous image segmentation (DIS).
birefnet-hrsod (download, source): A pre-trained model for high-resolution salient object detection (HRSOD).
birefnet-cod (download, source): A pre-trained model for concealed object detection (COD).
birefnet-massive (download, source): A pre-trained model with massive dataset.
'''

# 경로가 없다면 생성
if not os.path.exists(destination_folder):
os.makedirs(destination_folder)

remove_background_and_save(source_folder, destination_folder, model_name)


여러가지 모델을 사용해봤는데 제일 잘 나오는 형태가 birefnet-general 모델 이었습니다.
이것은 이미지에 따라 다르므로 여러가지 시도해보시기 바랍니다.

전체 코드는 아래링크에서 다운로드 하세요


2024년 8월 19일 월요일

Google Play Console 개발자 인증, 주소지 인증

Google Play Console 개발자 인증시 주소지 인증 부분에서 막혀서 정리해 보았습니다.

주소지 인증은 google payment 인증하는 부분으로 넘어가게 되는데 허용 되는 서류는

아래에 표기된 서류들입니다.

요즘 온라인으로 명세서가 오다 보니 급하게 준비할 서류가 없었습니다.


검색으로는 주민등록 등본으로 된다는 말이 있어서 업로드 해봤는데 다음으로 넘어가지 않았습니다. (이때는 필요하지 않은 기타 정보를 수정해야 한다는 내용을 숙지 못한 상태에서 제출했던 시점)

주소지가 정확하게 나와야 하는데 email로 발급받은 청구서에는 주소지가 나타나지 않았습니다.
그래서 부랴부랴 신용 카드사에서 모바일 청구서를 신청했습니다. (결재일이 다가오자 미리 발송 되었습니다.)
모바일 링크를 타고 들어가면 종이로 받았을때와 동일한 pdf를 받는 카드사가 있는데 저의 경우 신한 카드가 동일하게 나왔습니다.
해당 내용을 카메라로 찍어도 여전히 다음으로 넘어가지 않고 문제가 있다는 설명만 나왔습니다.

이시점에 필요하지 않은 기타 정보를 수정해야 한다는 내용이 있었고 링크를 타고 들어가면 개인 정보는 검은색으로 지우라는 설명이 있어서, 그림판에서 이름과 주소지만 남기고 아래와 같이 모두 지웠습니다.


평일이 되자 금방 등록 완료 되었습니다.




2024년 8월 11일 일요일

하나의 GameObject에 DOTweenAnimation 여러개 사용 방법

 Unity에서 DOTweenAnimation 을 사용하여 두 개의 애니메이션이 있을 때, 그 중 하나만 동작시키려면 각 애니메이션을 개별적으로 제어해야 합니다.


GPT에 문의해보니 엉뚱한 답을 가르쳐 줘서 구글링해서 찾아냈습니다.


ID를 이용 하면 됩니다. 하나는 ID:1 로 하고 다른 하나는 ID:2를 사용하였습니다.



ById 라는 함수가 준비되어 있습니다. 아래와 같이 string으로 ID값을 넘겨주면 됩니다.
아래는 작업된 코드 입니다.

    public void Attack()

    {        

        DOTweenAnimation ani;

        ani = GetComponent<DOTweenAnimation>();

        if (ani != null)

        {

            ani.DORestartById("1");

        }

    }

    public void FadeOut() 

    {

        DOTweenAnimation ani;

        ani = GetComponent<DOTweenAnimation>();

        if (ani != null)

        {

            ani.DORestartById("2");

        }

    }





2024년 7월 20일 토요일

unity UI Particle 소개

 최근 개발중인 작품에서 UI에서 파티클 출력하는 부분이 필요해서 조사해 보았습니다.

대부분의 파티클들이 UI 모드에서는 나오지 않는 편이 많았는데 그중에 아래 링크의 오픈소스로된 부분을 참고해서 작업을 하였습니다. 

그럼 소개입니다.

https://github.com/mob-sakai/ParticleEffectForUGUI

그중에서도 가장 필요한 기능이었던 특정 target으로 파티클이 이동하는 기능이 필요했는데 해당 기능도 여기에서 UIParticleAttractor 라는 스크립트로 제공을 하였습니다.

Component: UIParticleAttractor

UIParticleAttractor attracts particles generated by the specified ParticleSystem.

 

설치

설치하는 방법은 해당 게시물에 아래부분 참고 부탁드립니다.

OpenUPM을 설치해야 하는데 npm 설치가 필요한데 npm을 설치하려면 node.js 설치가 필요합니다.

  • This package is available on OpenUPM package registry.
  • This is the preferred method of installation, as you can easily receive updates as they're released.
  • If you have openupm-cli installed, then run the following command in your project's directory:
    openupm add com.coffee.ui-particle

    정리하자면, Node.js (npm이 포함됨) -> npm install -g openupm-cli -> cd 작업 폴더 -> openupm add com.coffee.ui-particle 이런 순서로 설치합니다.

    중간에 작업 폴더로 이동해서 설치가 필요합니다.

    아래 링크 다른 패키지 설치하는 예제인데 참고 하시면 됩니다.

    https://openupm.com/#get-started-with-cli-optional


    설치가 완료되면 예제가 없어서, 다시 github demo https://github.com/mob-sakai/ParticleEffectForUGUI/tree/main/Samples~ 이 파일만 받아서 ~를 제거하고 Asset 폴더에 넣어서 실행을 했습니다.


    UIParticleAttractor 

    타겟이 되는 object에 UIParticleAttractor을 추가해 줍니다. 자세한건 예제를 보시면 됩니다.









    2024년 7월 8일 월요일

    Unity Hold on 에서 (hang) 멈추는 현상

     유니티를 실행하다 보면 가끔 아래와 같이 Hold on 에서 무한 대기하는 경우가 있습니다.



    원인이 다양하겠지만, 가장 유력한 범인은 ... 바로 당신입니다.

    가장 최근에 추가한 모듈이나 소스를 의심하시기 바랍니다.

    저 상태에서 다음 상태로 안 넘어 가는 원인 중 하나는 Update() 루틴이 수행되지 못하고 있기 때문입니다.

    어제밤 이 문제로 구글링 하면서 Unity 여러 버전 삭제도 해보고 antivirus 프로그램 중지도 해보았으나 전혀 효과가 없었습니다.

    Start() 함수에 최근에 특별히 추가된 코드가 없는지 확인해보기 바랍니다. 어딘가에서 무한 루프에 빠져 있거나 무한 메모리 증식중일 수 있습니다.

    저의 경우는 Start() 함수에 의해 발생한 현상이었습니다.


    2024년 6월 15일 토요일

    Termux 설치 및 사용

    Termux 설치 및 사용

    Android Linux terminal emulator 입니다.

    ( 안드로이드에서 리눅스를 사용할 수 있게 해줍니다. 컴파일 도구도 있습니다. )

    F-Droid 스토어에서 검색 해서 설치 하거나

    F-Droid : https://f-droid.org/ko/

    github 에서 직접 설치

    https://github.com/termux/termux-app/releases


    기본 설정

    pkg update

    pkg upgrade


    storage 접근을 편하게 하기 위해서

    https://wiki.termux.com/wiki/Termux-setup-storage // 상세 내용, storage를 만들고 심볼릭 링크를 생성한다

    termux-setup-storage // 실행전 환경설정>어플리케이션>termux의 모든 권한을 허용해주고 실행 시킨다.

    정상적으로 권한이 획득 되었으면

    ls ~/storage/ 실행시 권한이 정상적으로 획득되었다면 아래와 같은 폴더가 보입니다.

    PC의 파일을 옮길때 아래와 같이 사용하면 편합니다.

    adb push [윈도우에 있는 파일명] /storage/emulated/0/download

    mv ~/storage/downloads/[파일명] ~/llama.cpp/models


    PC 와 연결(SSH) 설정

    (이것을 진행하지 않는다면 매번 전화기에 입력해야 해서 불편 합니다.)

    설치 후 패스워드 관련 설정이 필요하므로 아래 내용 설치해줍니다.

    pkg install openssh

    sshd // 구동

    "sshd 실행시 no hostkeys available -- exiting" 에러가 발생한다면 pkg uninstall openssh 를 실행한 뒤 다시 설치하도록 합니다.

    서버 설정을 바꾸게 되는 경우 재시작이 필요할 수 있는데 pkill sshd 로 서버를 중지 후 다시 시작하면 됩니다.

    whoami // 접속할 id가 뭔지 확인, 이 값은 장치마다 다릅니다.

    passwd // 접속 비밀번호 설정합니다.

    ifconfig // 실행후 wlan0 항목을 확인 후 ip주소를 알아냅니다. 물론 WIFI설정에 할당된 주소를 확인해도 됩니다.



    Windows 에서 SSH로 Android 연결

    Windows에는 SSH 실행 파일이 기본으로 존재합니다.


    ssh <ServerIP> -p <port> -l <id> 와 같이 입력해서 접속 했습니다.
    <ServerIP>는 ipconfig의 wlan0 값의 ip를 입력합니다.
    <port> 는 8022로 접속 합니다. (기본 포트는 22 라서 에러가 발생합니다.)
    <id>는 앞에서 whoami 를 입력한 출력한 값을 입력합니다.


    adb로 연결하기


    Termux 설치한 단말기가 network을 가지고 있지 않거나 같은 network 환경에 있지 않다면 ip 로 접속을 할 수 없습니다.
    이때는 adb port forward로 사용이 가능합니다.

    adb forward tcp:8022 tcp:8022


    위와 같이 한번 해주고 ip는 localhost ssh 로 접속하면 됩니다. 이때 id(u0_a412) 는 앞에서 whoami 로 확인한 id 입니다.

    ssh localhost -p 8022 -l u0_a412


    간호 yes/no/[fingerprint]가 나오는 경우 yes를 선택하고 비밀번호를 입력해 주면 됩니다.