preface

In Douyin, we often see the funhouse mirror effect used by various douyin players. So what is a funhouse mirror?

In real life, a funhouse mirror is a mirror with an uneven surface that reflects the distorted features of people and objects. Simply put, it is the effect of stretching a face (object), or squeezing a face (object).

The realization principle of funhouse mirror is as follows:

Suppose the width and height of the input image are W and H, and the coordinate of the center point of the image is (cx, cy). So the distance from any point in the graph (x, y) to the center point is (x-cx), (y-cy).

Then pull up and enlarge, and the radius of image transformation is R (r is the range size of the funhouse mirror). The formula is as follows:

x=(tx/2)(sqrt(txtx+tyty)/r)+cx y=(ty/2)(sqrt(txtx+tyty)/r)+cy

Similarly, the formula for compression reduction is as follows (compress is compression strength) : x=cos(atan2(ty,tx))compresssqrt(sqrt(txtx+tyty))+cx y=cos(atan2(ty,tx))compresssqrt(sqrt(txtx+tyty))+cy

Funhouse mirror magnification effect

Now that we understand the mathematical formula and the principle of its implementation, let’s directly achieve the funhouse mirror amplification effect. The specific code is as follows:

# Funhouse mirror magnification effect achieved
def enlarge_effect(img) :
    h, w, n = img.shape
    cx = w / 2
    cy = h / 2
    radius = 100This value can be defined by yourself. It determines the size of the funhouse mirror. If the image is large, it should be increased accordingly
    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()
Copy the code

After running, the effect is as follows:

Note that the above calculation may result in floating point numbers, whereas the pixel value must be an integer. Therefore, in order to ensure the validity of the pixel value, the cast int() must be performed after the calculation process is complete. In addition, when calculating the values of x and y, the range of image coordinates may be exceeded, so x<w and y<h must be used to judge and prevent crossing the boundary.

Funhouse mirror shrinkage effect

Now let’s directly achieve the funhouse mirror zoom effect. The specific code is as follows:

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("4.jpg")
    enlarge_img = enlarge_effect(img)
    frame = reduce_effect(img)
    cv2.imshow("1", img)
    cv2.imshow("2", enlarge_img)
    cv2.imshow("3", frame)
    cv2.waitKey()
    cv2.destroyAllWindows()
Copy the code

After running, the effect is as follows:

Direct video to achieve funhouse mirror effect

The funhouse mirror on Tiktok is dynamic, not a single picture that changes. In fact, as long as we assemble the video recording function of the front camera, we can complete the dynamic effect of the video funhouse mirror. The specific code is as follows:

if __name__ == "__main__":
	cap = cv2.VideoCapture(0)
    while (cap.isOpened()):
        ret, frame = cap.read()
        frame=enlarge_effect(frame)
        cv2.imshow('video', frame)
        c = cv2.waitKey(1)
        if c == 27:
            break
    cap.release()
    cv2.destroyAllWindows()
Copy the code