前言
在抖音中,我們經常看到各種抖音玩家都喜歡使用哈哈鏡效果。那麼什麼是哈哈鏡呢?
在現實生活中,哈哈鏡是指一種表面凹凸不平的鏡子,可以反應出人像及物體的扭曲面貌。簡單點來說就是拉伸人臉(物件),或者壓縮人臉(物體)的效果。
哈哈鏡實現原理
假設輸入影象的寬高為w
和h
,影象中心點的座標為(cx,cy)
。那麼影象中任意一點(x,y)
到中心點的距離為(x-cx)
,(y-cy)
。
那麼拉昇放大,影象變換的半徑為r
(r是哈哈鏡的範圍大小),得到公式如下:
x=(tx/2)(sqrt(txtx+tyty)/r)+cx
y=(ty/2)(sqrt(txtx+tyty)/r)+cy
同理,壓縮縮小的公式如下(compress為壓縮強度):
x=cos(atan2(ty,tx))compresssqrt(sqrt(txtx+tyty))+cx
x=cos(atan2(ty,tx))compresssqrt(sqrt(txtx+tyty))+cy
原始影象:
基於OpenCV的哈哈鏡放大實現
既然我們瞭解了其數學公式以及其實現的原理,下面我們來直接實現哈哈鏡的放大效果。具體程式碼如下所示:
import cv2
import math
#哈哈鏡放大效果實現
def enlarge_effect(img):
h, w, n = img.shape
cx = w / 2
cy = h / 2
radius = 100#該值可以自行定義,它決定了哈哈鏡的大小,當影象很大時,應該相應的調大
r = int(radius / 2.0)
new_img = img.copy()
for i in range(w):
for j in range(h):
tx = i - cx
ty = j - cy
distance = tx * tx + ty * ty
if distance < radius * radius:
x = int(int(tx / 2.0) * (math.sqrt(distance) / r) + cx)
y = int(int(ty / 2.0) * (math.sqrt(distance) / r) + cy)
if x < w and y < h:
new_img[j, i, 0] = img[y, x, 0]
new_img[j, i, 1] = img[y, x, 1]
new_img[j, i, 2] = img[y, x, 2]
return new_img
if __name__ == "__main__":
img = cv2.imread("4.jpg")
enlarge_img = enlarge_effect(img)
cv2.imshow("4", enlarge_img)
cv2.waitKey()
cv2.destroyAllWindows()
執行後的效果:
需要注意的是,上面的計算過程可能導致有浮點數的出現,而畫素值必須為整數。所以,為了保證畫素值的有效性,必須在計算過程完整之後,進行強制型別轉換int()。另外,計算x,y值時,可能會導致超過影象座標的範圍,所以必須用x<w和y<h來判斷防止越界。
基於OpenCV的哈哈鏡縮小實現
具體程式碼如下所示:
import cv2
import math
#哈哈鏡縮小效果實現
def reduce_effect(img):
h, w, n = img.shape
cx = w / 2
cy = h / 2
radius = 100
r = int(radius / 2.0)
compress = 8
new_img = img.copy()
for i in range(w):
for j in range(h):
tx = i - cx
ty = j - cy
x = int(cx + (math.sqrt(math.sqrt(tx * tx + ty * ty)) * compress * math.cos(math.atan2(ty, tx))))
y = int(cy + (math.sqrt(math.sqrt(tx * tx + ty * ty)) * compress * math.sin(math.atan2(ty, tx))))
if x < 0 and x > w:
x = 0
if y < 0 and y > h:
y = 0
if x < w and y < h:
new_img[j, i, 0] = img[y, x, 0]
new_img[j, i, 1] = img[y, x, 1]
new_img[j, i, 2] = img[y, x, 2]
return new_img
if __name__ == "__main__":
img = cv2.imread("lena.jpg")
frame = reduce_effect(img)
cv2.imshow("lena1", img)
cv2.imshow("lena2", frame)
cv2.waitKey()
cv2.destroyAllWindows()
執行後的效果如下:
視訊實現哈哈鏡效果
抖音上面的哈哈鏡都是動態的,並不是單一的圖片這麼變來變去。其實,只要我們集合攝像頭視訊錄製功能,就可以完成視訊哈哈鏡的動態效果。具體程式碼如下:
import cv2
import math
#哈哈鏡放大效果實現
def enlarge_effect(img):
h, w, n = img.shape
cx = w / 2
cy = h / 2
radius = 100#該值可以自行定義,它決定了哈哈鏡的大小,當影象很大時,應該相應的調大
r = int(radius / 2.0)
new_img = img.copy()
for i in range(w):
for j in range(h):
tx = i - cx
ty = j - cy
distance = tx * tx + ty * ty
if distance < radius * radius:
x = int(int(tx / 2.0) * (math.sqrt(distance) / r) + cx)
y = int(int(ty / 2.0) * (math.sqrt(distance) / r) + cy)
if x < w and y < h:
new_img[j, i, 0] = img[y, x, 0]
new_img[j, i, 1] = img[y, x, 1]
new_img[j, i, 2] = img[y, x, 2]
return new_img
#哈哈鏡縮小效果實現
def reduce_effect(img):
h, w, n = img.shape
cx = w / 2
cy = h / 2
radius = 100
r = int(radius / 2.0)
compress = 8
new_img = img.copy()
for i in range(w):
for j in range(h):
tx = i - cx
ty = j - cy
x = int(cx + (math.sqrt(math.sqrt(tx * tx + ty * ty)) * compress * math.cos(math.atan2(ty, tx))))
y = int(cy + (math.sqrt(math.sqrt(tx * tx + ty * ty)) * compress * math.sin(math.atan2(ty, tx))))
if x < 0 and x > w:
x = 0
if y < 0 and y > h:
y = 0
if x < w and y < h:
new_img[j, i, 0] = img[y, x, 0]
new_img[j, i, 1] = img[y, x, 1]
new_img[j, i, 2] = img[y, x, 2]
return new_img
if __name__ == "__main__":
cap = cv2.VideoCapture("video.mp4")
while (cap.isOpened()):
ret, frame = cap.read()
enlarge_img = enlarge_effect(frame)
frame = reduce_effect(frame)
cv2.imshow('video1', enlarge_img)
cv2.imshow('video2', frame)
c = cv2.waitKey(1)
if c == 27:
break
cap.release()
cv2.destroyAllWindows()