😊AIλ₯Ό μ΄μš©ν•΄μ„œ 이미지 고해상도 생성, λ””λ…Έμ΄μ¦ˆ, λͺ¨μ…˜λΈ”λŸ¬μ œκ±°, 컬러 λ³€ν™˜μ„ ν•΄λ³΄μž!

,

μ‹œμž‘ν•˜κΈ° μ•žμ„œ

μ–΄μ œ 처음으둜 용기λ₯Ό λ‚΄μ„œ Dot4 meetup에 저쑰도 κ°œμ„  λͺ¨λΈ λˆ„κ²Ÿ κ°œλ°œν•œ 것을 λ°œν‘œν–ˆμ—ˆλŠ”λ°μš”.
지루해 ν•˜μ‹€ 것 κ°™μ•„μ„œ λ”₯λŸ¬λ‹ λ‚΄μš©μ„ λŒ€λΆ€λΆ„ λΊμ—ˆλŠ”λ° λ“€μœΌμ‹œλŠ” λΆ„λ“€ 쀑에 ν•΄μ™ΈλŒ€ν•™ κ΅μˆ˜λ‹˜λ„ κ³„μ‹œλ”κ΅°μš”?
(μ–΄λ–»κ²Œ μ•Œκ³  μ˜€μ‹ κ±΄μ§€β€¦λ§Žμ€ μ§ˆλ¬Έμ„ λ°›μ•˜μŠ΅λ‹ˆλ‹€ γ…  )
λ˜β€¦ λͺ‡ 뢄은 개인적으둜 Linkedin, μΉ΄μΉ΄μ˜€ν†‘ λ©”μ‹ μ €λ‘œ λͺ‡κ°€μ§€ ν•„μš”λ‘œ ν•˜λŠ” λ”₯λŸ¬λ‹ λˆ„κ²Ÿμ„ λ§μ”€ν•΄μ£Όμ…”μ„œ λ‹€μ‹œ ν•œλ²ˆ μ•„λž˜μ™€ 같은 4κ°€μ§€ Task λͺ¨λΈμ„ λ§Œλ“€κ³  4개의 λˆ„κ²Ÿ λΌμ΄λΈŒλŸ¬λ¦¬μ™€ 데λͺ¨μ•±μ„ λ§Œλ“€μ–΄ 보렀고 ν•©λ‹ˆλ‹€.

Super Resolution


Motion Blur Reduction


Noise Reduction


Gray To Color

이전 λˆ„κ²Ÿκ³Ό λ‹€λ₯΄κ²Œ μ μš©ν•  λΆ€λΆ„

이전에 λˆ„κ²Ÿ 배포할 μ μ—λŠ”.. C++, C++/CLIλ₯Ό μ‚¬μš©ν–ˆμ—ˆλŠ”λ°μš”
μ΄λ²ˆμ—λŠ” C#만 κ°€μ§€κ³  ν•΄λ³Ό μƒκ°μž…λ‹ˆλ‹€. λ‹€λ₯Έ OSμ—μ„œλ„ λŒμ•„κ°€λ €λ©΄ 그게 λ§žλŠ” 것 κ°™μ•„μ„œμš”.
속도가 걱정이 λ˜μ§€λ§Œ C#에도 μ œκ³΅ν•΄μ£ΌλŠ” λ©”λͺ¨λ¦¬ν’€μ΄ 있고 아직 덜 μ°Ύμ•„λ΄€μ§€λ§Œ SIMD μ˜€νΌλ ˆμ΄μ…˜λ„ λ˜λŠ” 것 κ°™μ•„μ„œ μ‹œλ„ν•΄λ³΄λ €κ³ ν•©λ‹ˆλ‹€.

13개의 μ’‹μ•„μš”

SIMD 처리 ν• λ•Œ System.Numerics.Tensors μ‚¬μš©ν•˜λ‹ˆ νŽΈν•˜λ”λΌκ΅¬μš”

TensorPrimitives 클래슀 μ•ˆμ— ν•¨μˆ˜ ν™œμš©ν•˜μ‹œλ©΄ 쒋을 λ“― ν•©λ‹ˆλ‹€.

5개의 μ’‹μ•„μš”

였 κ°μ‚¬ν•©λ‹ˆλ‹€ λ©”λͺ¨ 해놓고 μš”κΈ΄ν•˜κ²Œ μ“°κ² μŠ΅λ‹ˆλ‹€!

3개의 μ’‹μ•„μš”

ν˜Ήμ‹œ μ˜¬λ €μ£Όμ‹  λΌμ΄λΈŒλŸ¬λ¦¬μ™€ μ½”λ“œκ°€ OCR μ „μ²˜λ¦¬μ—λ„ 쓰일 수 μžˆλŠ”κ²ƒμΌμ§€ κΆκΈˆν•©λ‹ˆλ‹€.!

2개의 μ’‹μ•„μš”

닡변이 λŠ¦μ—ˆμŠ΅λ‹ˆλ‹€. μ§€κΈˆ 올린 4κ°€μ§€ Task외에 ocr을 μš”μ²­ν•˜μ‹œλŠ”κ±΄κ°€μš”? μ•„λ‹ˆλ©΄ γ…  ocr전에 μ‚¬μš©ν•΄μ„œ ν™•λ₯ μ„ μ˜¬λ¦¬μ‹€ λͺ©μ μœΌλ‘œ μš”μ²­ν•˜μ‹ κ±΄κ°€μš”? Ocr도 ν•„μš”ν•˜μ‹ κ°€μš”?

2개의 μ’‹μ•„μš”

μ•„λ‹ˆμš”. γ…Žγ…Ž 뭘 λ§Œλ“€μ–΄ 달라고 λ§μ”€λ“œλ¦¬λŠ” 것은 μ ˆλŒ€λ‘œ μ•„λ‹™λ‹ˆλ‹€. λ§Œλ“€μ–΄μ£Όμ‹  μœ ν˜•μ˜ graphic enhancementλ₯Ό OCR에도 μ μš©ν•΄μ„œ μ„±λŠ₯ ν–₯상을 μ΄λŒμ–΄λ‚Ό 수 μžˆλŠ” 것인지 κΆκΈˆν•΄μ„œ μ—¬μ­€λ΄€μŠ΅λ‹ˆλ‹€. λ§Œλ“€μ–΄μ£Όμ‹  κ·Έλž˜ν”½ 처리 기법은 λ…μžμ μœΌλ‘œ ν›Œλ₯­ν•œ κΈ°μˆ λ“€μ΄κ³ μš”! :clap:

2개의 μ’‹μ•„μš”

νšŒμ‚¬μ—μ„œ λŒ€λΉ„κ°€ μ’‹μ§€λͺ»ν•œ 이미지 (저쑰도 κ°œμ„ )κ³Ό μˆ˜νΌλ ˆμ‘Έλ£¨μ…˜μ„ μˆ˜ν–‰ν–ˆμ„λ•Œ ν™•μ‹€νžˆ λ””ν…μ…˜ μ„±λŠ₯μ΄λ‚˜ OCRμ—μ„œ μ„±λŠ₯이 μ’‹μ•˜μŠ΅λ‹ˆλ‹€. 근데 막 10νΌμ„ΌνŠΈκ°œμ„  μ΄λŸ°λŠλ‚Œμ€ μ•„λ‹ˆμ—ˆκ³ . 1νΌμ„ΌνŠΈ? 2νΌμ„ΌνŠΈ κ°œμ„ μ΄λ˜λ”κ΅°μš”. 이건 λŒ€μƒμœΌλ‘œ ν•˜λŠ” 이미지에 λ”°λΌμ„œ 달라 질 κ²ƒκ°™μ•„μš”. (μœ ν˜•μ— 따라), 사내에 κ°€μ§€κ³  μžˆλŠ” 이미지듀은 곡개λ₯Όλͺ»ν•΄μ„œ λͺ»λ³΄μ—¬λ“œλ¦¬κ³  γ…  λͺ¨λΈλ“€ λ§Œλ“€μ–΄μ§€λ©΄ ν•œλ²ˆ ν…ŒμŠ€νŠΈν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€!

2개의 μ’‹μ•„μš”

Super Resolution λ…Όλ¬Έ μ„ μ •

https://arxiv.org/pdf/2311.12770

μš” 녀석이 μ λ‹Ήν•œ μ„±λŠ₯에 쉽고 λΉ¨λΌλ³΄μ—¬μ„œ μ„ μ •ν–ˆμŠ΅λ‹ˆλ‹€. 읽어보고 ν•œλ²ˆ λ²„λ¬΄λ €λ³΄κ² μŠ΅λ‹ˆλ‹€..

2개의 μ’‹μ•„μš”

ν•™μŠ΅ μ‹œμž‘

였늘 μ˜€ν›„μ— μž‘μ—…μ„ ν•΄μ„œ ν•™μŠ΅μ„ κ±Έμ–΄ λ†¨μŠ΅λ‹ˆλ‹€.
잘됬으면 μ’‹κ² κ΅°μš”. ν•™μŠ΅ 슀크립트 μ½”λ“œλŠ” git에 μ˜¬λ €λ‘μ—ˆμŠ΅λ‹ˆλ‹€.

링크

ν•™μŠ΅ 슀크립트

import os, glob, time
import cv2
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import Dataset, DataLoader
from datasets.srdataset import SRDataset
from model.span import SPAN30

# Hyper parameter
train_dir = r"C:\github\dataset\DIV2K_train_HR\DIV2K_train_HR"
scale = 4
hr_patch = 256
batch_size = 16
lr = 1e-3    
epochs = 1000       
save_dir = r"C:\github\SRSharp\python\results"



os.makedirs(save_dir, exist_ok=True)

device = "cuda" if torch.cuda.is_available() else "cpu"
torch.backends.cudnn.benchmark = True

ds = SRDataset(train_dir, hr_size=hr_patch, scale=scale)
dl = DataLoader(ds, batch_size=batch_size, shuffle=True, pin_memory=True, drop_last=True)

model = SPAN30(num_in_ch=3, num_out_ch=3, feature_channels=48, upscale=scale, bias=True).to(device)
criterion = nn.L1Loss()
optim = torch.optim.Adam(model.parameters(), lr=lr)

cv2.namedWindow("input", cv2.WINDOW_NORMAL)
cv2.namedWindow("output", cv2.WINDOW_NORMAL)

global_step = 0
for epoch in range(1, epochs + 1):
    model.train()
    t0 = time.time()
    loss_sum = 0.0
    n = 0

    last_lr = None
    last_sr = None

    for lr_img, hr_img in dl:
        lr_img = lr_img.to(device, non_blocking=True)   # (B,3,lr,lr), float32 0~255
        hr_img = hr_img.to(device, non_blocking=True)   # (B,3,hr,hr), float32 0~255

        sr = model(lr_img)

        loss = criterion(sr, hr_img)

        optim.zero_grad(set_to_none=True)
        loss.backward()
        torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
        optim.step()

        loss_sum += float(loss.item())
        n += 1
        global_step += 1

        last_lr = lr_img[0].detach().clamp(0, 255).to("cpu")
        last_sr = sr[0].detach().clamp(0, 255).to("cpu")


    model.eval()
    with torch.no_grad():
        lr_np = last_lr.permute(1, 2, 0).numpy().astype(np.uint8)       # RGB
        sr_np = last_sr.permute(1, 2, 0).numpy().astype(np.uint8)       # RGB

        lr_up = cv2.resize(lr_np, (hr_patch, hr_patch), interpolation=cv2.INTER_NEAREST)

        cv2.imshow("input", cv2.cvtColor(lr_up, cv2.COLOR_RGB2BGR))
        cv2.imshow("output", cv2.cvtColor(sr_np, cv2.COLOR_RGB2BGR))

        key = cv2.waitKey(1) & 0xFF
        if key == 27 or key == ord('q'):  # ESC λ˜λŠ” q둜 μ’…λ£Œ
            break

    avg_loss = loss_sum / max(1, n)
    dt = time.time() - t0
    print(f"[Epoch {epoch:03d}/{epochs}] loss={avg_loss:.4f} time={dt:.1f}s step={global_step}")

    # 체크포인트 μ €μž₯
    torch.save(model.state_dict(), os.path.join(save_dir, f"span_stage1_e{epoch:03d}.pth"))

cv2.destroyAllWindows()

ν•™μŠ΅ 과정쀑 κ²°κ³Ό 이미지 ( 크둭 256->1024 4λ°° λ»₯νŠ€κΈ°)


μ§€κΈˆμ€ μƒ€ν”„ν•˜κ²Œ λ‚˜μ˜€μ§€ μ•Šμ§€λ§Œ. ν•™μŠ΅μ΄ 였래 μ§„ν–‰λ˜λ©΄ 될수둝 μƒ€ν”„ν•˜κ²Œ λ°”λ€” 것 κ°™μŠ΅λ‹ˆλ‹€.

2개의 μ’‹μ•„μš”