該演算法利用區域性的平移、縮放以及旋轉的方式來不失真的進行影像纖瘦化處理。

freedragon發表於2024-06-26

import cv2
import imageio.v2 as iio
from PIL import Image
import copy
import operator
import math
import numpy as np

class LocalWarpEffect(object):
    '''Interactive Image Warping Effect:互動式扭曲影像效果(可以理解為平滑的拉伸\扭曲影像)
    @note 參考文獻: Interactive Image Warping by Andreas Gustafsson
    說明:該演算法利用區域性的平移、縮放以及旋轉的方式來不失真的進行影像纖瘦化處理。
    '''

    def __init__(self, center, mouse, radius, antialias=2):
        '''
        @param center 區域性變形效果的圓心,可以認為是滑鼠按下起點
        @param mouse 滑鼠釋放的位置
        '''
        self.center = center
        self.mouse = mouse
        self.radius = radius
        self.antialias = antialias

    def warp(self, x, y, r, center, mouse):
        cx, cy = center
        mx, my = mouse
        dis_x_c = math.sqrt((x - cx) ** 2 + (y - cy) ** 2)
        dis_m_c = math.sqrt((x - mx) ** 2 + (y - my) ** 2)
        div = float(r ** 2 - dis_x_c ** 2 + dis_m_c ** 2)
        if div == 0:
            div = 0.0000000001
        factor = ((r ** 2 - dis_x_c ** 2) / div) ** 2

        u = x - factor * (mx - cx)
        v = y - factor * (my - cy)

        return u, v

    def __call__(self, img):
        width, height = img.size
        new_img = img.copy()
        r = self.radius
        cx, cy = self.center
        mx, my = self.mouse

        nband = len(img.getpixel((0, 0)))
        antialias = self.antialias
        for x in range(width):
            for y in range(height):
                if math.sqrt((x - cx) ** 2 + (y - cy) ** 2) > r:
                    continue

                found = 0
                psum = (0,) * nband

                # anti-alias
                for ai in range(antialias):
                    _x = x + ai / float(antialias)
                    for aj in range(antialias):
                        _y = y + aj / float(antialias)

                        u, v = self.warp(_x, _y, r, (cx, cy), (mx, my))
                        u = int(round(u))
                        v = int(round(v))
                        if not (0 <= u < width and 0 <= v < height):
                            continue
                        pt = img.getpixel((u, v))
                        psum = list(map(operator.add, psum, pt))
                        found += 1

                if found > 0:
                    psum = map(operator.floordiv, psum, (found,) * len(psum))
                    new_img.putpixel((x, y), tuple(psum))

        return new_img

這個類 `LocalWarpEffect` 是一個用於影像處理的Python類,它實現了一種互動式的影像扭曲效果。這種效果通常用於影像編輯軟體中,允許使用者透過滑鼠操作來對影像的特定區域進行平移、縮放和旋轉,而不會失真。下面是對這個類及其方法的逐行解釋:

1-2. 匯入所需的庫,包括OpenCV、imageio、PIL庫中的Image模組、copy模組、operator模組、math模組和numpy。

```python
class LocalWarpEffect(object):
```
3. 定義了一個名為 `LocalWarpEffect` 的類,它繼承自 `object`(在Python 3中,所有類都隱式地繼承自 `object`)。

```python
def __init__(self, center, mouse, radius, antialias=2):
```
4-8. 類的建構函式 `__init__`,用於初始化類的例項。它接受四個引數:
- `center`:區域性變形效果的圓心,通常是滑鼠按下的起點。
- `mouse`:滑鼠釋放的位置。
- `radius`:變形效果作用的半徑。
- `antialias`:抗鋸齒級別,預設為2,用於改善影像質量。

```python
self.center = center
self.mouse = mouse
self.radius = radius
self.antialias = antialias
```
9-12. 將傳入的引數賦值給例項變數。

```python
def warp(self, x, y, r, center, mouse):
```
13. 定義了一個名為 `warp` 的方法,用於計算給定點 `(x, y)` 在經過扭曲後的新位置。它接受四個引數:當前點的座標 `(x, y)` 和之前定義的 `r`、`center` 和 `mouse`。

```python
cx, cy = center
mx, my = mouse
```
14-15. 從 `center` 和 `mouse` 元組中解構出中心點和滑鼠點的座標。

```python
dis_x_c = math.sqrt((x - cx) ** 2 + (y - cy) ** 2)
dis_m_c = math.sqrt((x - mx) ** 2 + (y - my) ** 2)
```
16-17. 分別計算點 `(x, y)` 到中心點和滑鼠點的距離。

```python
div = float(r ** 2 - dis_x_c ** 2 + dis_m_c ** 2)
```
18. 計算一個用於後續計算的除數,如果除數為0,則設定一個非常小的值以避免除以零的錯誤。

```python
factor = ((r ** 2 - dis_x_c ** 2) / div) ** 2
```
19. 計算一個因子,這個因子將用於計算扭曲後的新座標。

```python
u = x - factor * (mx - cx)
v = y - factor * (my - cy)
```
20-21. 根據扭曲演算法計算新座標 `u` 和 `v`。

```python
return u, v
```
22. 返回計算出的新座標。

```python
def __call__(self, img):
```
23. `__call__` 方法允許類的例項像函式一樣被呼叫。它接受一個引數 `img`,即要處理的影像。

```python
width, height = img.size
```
24. 獲取影像的寬度和高度。

```python
new_img = img.copy()
```
25. 建立一個影像的副本,以避免直接修改原始影像。

```python
r = self.radius
cx, cy = self.center
mx, my = self.mouse
```
26-29. 從例項變數中獲取半徑、中心點和滑鼠點的座標。

```python
nband = len(img.getpixel((0, 0)))
```
30. 獲取影像的通道數,例如RGB影像的通道數為3。

```python
antialias = self.antialias
```
31. 獲取抗鋸齒級別。

```python
for x in range(width):
for y in range(height):
```
32-33. 遍歷影像的每個畫素點。

```python
if math.sqrt((x - cx) ** 2 + (y - cy) ** 2) > r:
continue
```
34-35. 如果當前畫素點距離中心點的距離大於半徑,則跳過該點。

```python
found = 0
psum = (0,) * nband
```
36-37. 初始化用於累加畫素值的變數。

```python
for ai in range(antialias):
_x = x + ai / float(antialias)
for aj in range(antialias):
_y = y + aj / float(antialias)
```
38-41. 進行抗鋸齒處理,透過在當前畫素點周圍進行取樣。

```python
u, v = self.warp(_x, _y, r, (cx, cy), (mx, my))
```
42. 對取樣點進行扭曲計算。

```python
u = int(round(u))
v = int(round(v))
```
43-44. 將扭曲後的座標四捨五入到最近的整數。

```python
if not (0 <= u < width and 0 <= v < height):
continue
```
45. 如果扭曲後的座標超出影像邊界,則跳過。

```python
pt = img.getpixel((u, v))
psum = list(map(operator.add, psum, pt))
found += 1
```
46-48. 累加扭曲後的畫素值,並更新取樣點計數。

```python
if found > 0:
psum = map(operator.floordiv, psum, (found,) * len(psum))
new_img.putpixel((x, y), tuple(psum))
```
49-51. 如果有有效的取樣點,則計算平均畫素值,並將其設定為當前畫素點的新值。

```python
return new_img
```
52. 返回處理後的影像。

這個類的主要作用是對影像進行區域性的扭曲處理,透過定義一箇中心點和滑鼠釋放點,以及一個作用半徑,來實現平滑的影像拉伸和扭曲效果。這種效果可以用於影像編輯,例如在不改變影像整體結構的情況下,對人物的體型進行調整。

相關文章