Fun Python — Make a "hand-drawn style" video in less than 20 lines of code

Hello everyone, I’m Xiao Zhang, long time no see~

This tweet is related to computer vision. It takes less than 20 lines of Python code to convert a picture from natural wind to hand-drawn style . During this period, no pre-processing or post-processing is performed on the picture; only two common libraries are used in the code. Numpy is responsible for the core calculations, and Pillow is responsible for image reading and writing

Before starting the text, let’s take a look at the initial results. The following is a comparison of before and after conversion of a single image.

Figure one

image

Figure II

image

Figure three

image

In order to increase the fun, apply this code to a video later, add a background music, and a fresh "hand-drawn style video" is released.

Python hand-painted video production!

"Hand-painted style" realization steps

Before explaining, you need to understand the three main characteristics of hand-drawn images:

  • The picture must be a grayscale image, single-channel;
  • The edges of the lines are heavily painted black, and the same or similar pixel values ​​tend to be white after conversion;
  • With the support of the light source effect, the gray scale change can simulate the distance effect of human vision

Read the picture and convert it to an array

Because the pixel calculation will be used later, for convenience, the read image is converted into an array in advance

a = np.asarray(Image.open("Annie1.jpg").convert('L')).astype('float')

Calculate the x, y, z axis gradient values ​​and normalize them

A hand-drawn picture characteristics just mentioned, is painted more focused picture region edge , the edge portion of the positioning image, the most effective way is to calculate the gradient, a gray scale image to simulate the effect of distance, depth denotes a preset depth, z-axis gradient default Is 1

depth = 10.  # (0-100)
grad = np.gradient(a)  # 取图像灰度的梯度值
grad_x, grad_y = grad  # 分别取横纵图像梯度值
grad_x = grad_x * depth / 100.
grad_y = grad_y * depth / 100.

Complete the normalization operation on the gradient value

A = np.sqrt(grad_x ** 2 + grad_y ** 2 + 1.)
uni_x = grad_x / A
uni_y = grad_y / A
uni_z = 1. / A

Add light source effect

In addition to calculating the gradient value of the hand-painted wind picture, the influence of the light source needs to be considered; according to the angle of the light source, the gradient value on the x, y, and z axes will be affected to different degrees. Add a simulated light source and place it in the oblique Above, it forms two angles with x and y respectively

image

And these two angles are known through experiments, and then the final new pixel value is calculated according to the sine and cosine function

vec_el = np.pi / 2.2  # 光源的俯视角度,弧度值
vec_az = np.pi / 4.  # 光源的方位角度,弧度值
dx = np.cos(vec_el) * np.cos(vec_az)  # 光源对 x轴的影响
dy = np.cos(vec_el) * np.sin(vec_az)  # 光源对 y轴的影响
dz = np.sin(vec_el)  # 光源对z 轴的影响

b = 255 * (dx * uni_x + dy * uni_y + dz * uni_z)  # 光源归一化,8 255
b = b.clip(0, 255)# 对像素值低于0,高于255部分做截断处理

Export the picture and save it

im.save("Annie_shouhui.jpg")

The following is all the code involved in this step

from PIL import Image
import numpy as np


a = np.asarray(Image.open("Annie1.jpg").convert('L')).astype('float')

depth = 10.  # (0-100)
grad = np.gradient(a)  # 取图像灰度的梯度值
grad_x, grad_y = grad  # 分别取横纵图像梯度值
grad_x = grad_x * depth / 100.
grad_y = grad_y * depth / 100.
A = np.sqrt(grad_x ** 2 + grad_y ** 2 + 1.)
uni_x = grad_x / A
uni_y = grad_y / A
uni_z = 1. / A

vec_el = np.pi / 2.2  # 光源的俯视角度,弧度值
vec_az = np.pi / 4.  # 光源的方位角度,弧度值
dx = np.cos(vec_el) * np.cos(vec_az)  # 光源对 x轴的影响
dy = np.cos(vec_el) * np.sin(vec_az)  # 光源对 y轴的影响
dz = np.sin(vec_el)  # 光源对z 轴的影响

b = 255 * (dx * uni_x + dy * uni_y + dz * uni_z)  # 光源归一化
b = b.clip(0, 255)

im = Image.fromarray(b.astype('uint8'))  # 重构图像
im.save("Annie_shouhui.jpg")

Make a hand-painted video

Although the effect of the image conversion is good, the image is static after all. As a visual animal, it would be great if it can be made dynamic. After knowing the above method, you only need to add another frame splitting to the video. Operation, you can make a hand-painted video effect

you-get download video

Here I used the you-get command to find a video on station B and downloaded it.

you-get --format=dash-flv -o ./ https://www.bilibili.com/video/BV1tT4y1j7a9?from=search&8014393453748720686

image

After downloading, use OpenCV2 to cut the frame of the video, cut the frame and convert the picture at the same time, and write it out to the local video file

 vc = cv2.VideoCapture(video_path)
    c = 0
    if vc.isOpened():
        rval,frame = vc.read()
        height,width = frame.shape[0],frame.shape[1]
        print(height, width)
    else:
        rval = False
        height,width = 960,1200

    # jpg_list = [os.path.join('Pic_Directory/',i) for i in os.listdir('Pic_Directory') if i.endswith('.jpg')]

    fps = 24 # 视频帧率
    video_path1 = './text.mp4'
    video_writer = cv2.VideoWriter(video_path1,cv2.VideoWriter_fourcc(*'mp4v'),fps,(width,height))

    while rval:
        rval,frame = vc.read()# 读取视频帧
        img = coonvert_jpg(Image.fromarray(frame))
        frame_converted = np.array(img)

        # 转化为三通道
        image = np.expand_dims(frame_converted,axis = 2)
        result_arr = np.concatenate((image,image,image),axis = -1)

        video_writer.write(result_arr)
        print('Sucessfully Conveted---------{}'.format(c))
        c = c + 1
        if c >= 3000:
            break
    video_writer.release()

When extracting a picture sequence, you need to pay attention, because the converted picture is a single-channel, it is impossible to directly generate a video sequence with OpenCV. It is necessary to add a step to convert a single-channel to a three-channel!

 # 转化为三通道
 image = np.expand_dims(frame_converted,axis = 2)
 result_arr = np.concatenate((image,image,image),axis = -1)

If you want to make the generated video more feel, you can add a back view music, with the help of editing software, Python can be used, it is recommended to use editing software here, because Python customization to increase audio effects is not ideal, and real-time feedback is required when adding music , And Python cannot meet this requirement temporarily

Data source code acquisition

For the source code involved in the article to obtain the data, follow the WeChat official account : Xiao Zhang Python , and the back-end reply keyword: 210322 !

summary

This article mainly introduces how to use Python to convert a picture into a hand-painted style. The amount of code is small but the knowledge domain is related to mathematics and physics, so it is not easy to understand. The purpose of this article is only to introduce you to the hand-painted style conversion of pictures. One way, of course, if you have an interested partner, you can delve into it

Alright, the above is the entire content of this article, and finally thank you all for reading, see you in the next issue~

Guess you like

Origin blog.csdn.net/weixin_42512684/article/details/115339810