Image processing Python OpenCV image generation, overlay and response to mouse events

The title is a little long

In fact, the title is: using OpenCV Python to implement 2048 games

But who read that? Ha ha ha ha

But of course, it involves some problems mentioned in the title.

Source location: https://github.com/RainkLH/2048GameByOpencv_Python/blob/master/opcv2048.py

The code of this article is Python language, which needs the following modules

import cv2
import numpy as np
import random

Next, to implement the 2048 game, you need some global variables

# It is used to randomly generate new elements. To facilitate the adjustment of probability, it is created in the form of list
new_nums = [1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3]
new_vals = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4]
# 4x4 game data
data = np.array([[0, 0, 0, 0],
                 [0, 0, 0, 0],
                 [0, 0, 0, 0],
                 [0, 0, 0, 0]])
# Data used to record mouse movement: start (press) and end (lift)
m_start = [0, 0]
m_end = [0, 0]

 

Then the function codes

1. Generate pictures in opencv Python

1.1 game background

In python, opencv does not have the "Mat" data type, but directly uses the array in Numpy. In the original, all the operations of Mat became the operations of np array.

For example, create a white image (as the basic background of 2048 game):

# Create background map
def create_back():
    color = (255, 255, 255)
    img = np.array([[color for i in range(450)]for j in range(450)], dtype=np.uint8)
    return img

It's a 450x450 white image. It's not shown

1.2 figure of digital block

# Create color blocks
def create_block(num):
    # Get number of digits
    index = len(str(num))
    # The maximum number of 16 cells will not exceed the 16th power of 2, that is, 65536, that is, 5 digits,
    # Determine the color, text size, thickness and position of the block according to the length of the number
    greens = [180, 162, 144, 126, 108]
    font_sizes = [2.75, 1.75, 1.25, 1, 0.875]
    thickness_s = [6, 5, 4, 3, 2]
    color = (54, greens[index - 1], 60)
    font_size = font_sizes[index-1]
    thickness = thickness_s[index - 1]
    pos = (int(24 - (3.3 * index)), int(85 - (6.5 * index)))
    # Create a digital block
    img = np.array([[color for i in range(100)] for j in range(100)], dtype=np.uint8)
    if num > 0:
        #  Draw numbers greater than 0
        cv2.putText(img, str(num), pos, cv2.FONT_HERSHEY_SIMPLEX, font_size, (255, 255, 255), thickness)
    return img

Put 2, 4, 8, 16 65536 a total of 16 numbers of blocks are show n. That's it. As the number increases, the color of the block deepens.

2. Opencv Python responds to mouse events (game sliding operation)

These are the mouse events supported in opencv python.

Event number Notes
CV_EVENT_MOUSEMOVE 0 Slide
EVENT_LBUTTONDOWN 1 Left click
EVENT_RBUTTONDOWN 2 Right click
EVENT_MBUTTONDOWN 3 Middle click
EVENT_LBUTTONUP 4 Release of left key
EVENT_RBUTTONUP 5 Right click release
EVENT_MBUTTONUP 6 Intermediate release
EVENT_LBUTTONDBLCLK 7 Left click double click
EVENT_RBUTTONDBLCLK 8 Right click double click
EVENT_MBUTTONDBLCLK 9 Middle double click

 

Here is mainly used to press and lift the left mouse button

First, register the mouse event function:

During the initialization and startup of the game, define the window name, bind the mouse event function, and then generate the first number block and display the screen

# Initialize game interface
def game_run():
    cv2.namedWindow("2048", cv2.WINDOW_AUTOSIZE)
    # Bind mouse response function
    cv2.setMouseCallback("2048", mouse_event)
    new_elements()
    img = game_image()
    cv2.imshow("2048", img)
    cv2.waitKey(0)

The second step is to implement the mouse response function:

Press the left key to record the coordinates, lift the left key to record the coordinates, then calculate the front and back coordinates to determine which direction to slide, execute the corresponding function (that is, move the digital block, adjacent and the same sum), and then regenerate into a new digital block, with a new interface.

There is a very difficult problem to deal with here. In opencv, once the mouse event function is bound, as long as the mouse is in the window, it will trigger, no matter you are moving, double clicking, clicking Function writing is not good easy to lead to "as soon as the mouse enters the window, it will be crazy to perform some operations.".

# Mouse event
def mouse_event(event, x, y, flags, param):
    global m_start, m_end
    if event == cv2.EVENT_LBUTTONDOWN:
        m_start = [x, y]
    if event == cv2.EVENT_LBUTTONUP:
        m_end = [x, y]
        dx = m_end[0] - m_start[0]
        dy = m_end[1] - m_start[1]
        if abs(dx) > abs(dy):
            if dx > 0:
                to_right()
            else:
                to_left()
        else:
            if dy > 0:
                to_bottom()
            else:
                to_top()
        img = game_image()
        cv2.imshow("2048", img)
        cv2.waitKey(100)  # Update the screen after sliding, pause and insert new numbers to make it feel animated
        new_elements()
        img = game_image()
        cv2.imshow("2048", img)
        m_start = [0, 0]
        m_end = [0, 0]


3 Python array random elements (each slide randomly generates a new number block)

Here we mainly use the random module

Random. Choice() --- > randomly take an element from the list

Random. Sample() ------ > randomly take a specified number of elements from the list

# Generate new elements
def new_elements():
    pos_list_t = np.where(data == 0)
    pos_list = [(pos_list_t[0][i], pos_list_t[1][i]) for i in range(len(pos_list_t[0]))]
    # Number of random new elements
    nums = random.choice(new_nums)
    if nums >= len(pos_list):
        nums = 1
    # Random position of a new element
    new_poss = random.sample(pos_list, nums)
    for pos in new_poss:
        # The value of a random new element
        data[pos[0]][pos[1]] = random.choice(new_vals)

4 (up, down, left and right sliding treatment)

The first three are the technical core of the design, the next is the algorithm core, sliding in one direction, that is, in 4x4 matrix, non-zero numbers move to the specified direction, and then sum the adjacent and the same numbers into a number.

For example, slide the first line to the left once,

[1, 0, 1, 2]-->[1, 1, 2, 0]-->[2, 0, 2, 0]-->[2, 2, 0, 0]

The code is as follows (if you have a simpler and faster way, please leave a message to teach me)

# Sliding to the left
def to_left():
    for i in range(4):
        index = 0
        t = [0, 0, 0, 0]
        for x in range(3):
            if data[i][x] > 0:
                t[index] = data[i][x]
                data[i][x] = 0
                n = x + 1
                while data[i][n] == 0 and n < 3:
                    n = n + 1
                if t[index] == data[i][n]:
                    t[index], data[i][n] = t[index] + data[i][n], 0
                    x += 1
                index += 1
        t[index] = data[i][3]
        data[i] = t


# Towards the right
def to_right():
    for i in range(4):
        index = 3
        t = [0, 0, 0, 0]
        for x in range(3, 0, -1):
            if data[i][x] > 0:
                t[index] = data[i][x]
                data[i][x] = 0
                n = x - 1
                while data[i][n] == 0 and n >= 0:
                    n = n - 1
                if t[index] == data[i][n]:
                    t[index], data[i][n] = t[index] + data[i][n], 0
                    x -= 1
                index -= 1
        t[index] = data[i][0]
        data[i] = t


# Upward
def to_top():
    for i in range(4):
        index = 0
        t = [0, 0, 0, 0]
        for x in range(3):
            if data[x][i] > 0:
                t[index] = data[x][i]
                data[x][i] = 0
                n = x + 1
                while data[n][i] == 0 and n < 3:
                    n = n + 1
                if t[index] == data[n][i]:
                    t[index], data[n][i] = t[index] + data[n][i], 0
                    x += 1
                index += 1
        t[index] = data[3][i]
        data[:, i] = t


# down
def to_bottom():
    for i in range(4):
        index = 3
        t = [0, 0, 0, 0]
        for x in range(3, 0, -1):
            if data[x][i] > 0:
                t[index] = data[x][i]
                data[x][i] = 0
                n = x - 1
                while data[n][i] == 0 and n >= 0:
                    n = n - 1
                if t[index] == data[n][i]:
                    t[index], data[n][i] = t[index] + data[n][i], 0
                    x -= 1
                index -= 1
        t[index] = data[0][i]
        data[:, i] = t

5 (other functions and calls)

You also need a function to generate the game interface according to the current number of games data[4x4]

# Generate current screen
def game_image():
    img = create_back()
    for i in range(4):
        for j in range(4):
            x_start = 10 + 110 * i
            y_start = 10 + 110 * j
            x_end = x_start + 100
            y_end = y_start + 100
            block = create_block(data[i][j])
            img[x_start:x_end, y_start:y_end] = block
    return img

Finally, make a call to start the game

if __name__ == "__main__":
    game_run()

The picture is as follows

Tail: of course, there are many bugs. Here are just some techniques. It's good to play. If you want to write a game, you can't do it with Opencv.

Published 17 original articles, won praise 3, visited 1703
Private letter follow

Tags: OpenCV Python github

Posted on Thu, 16 Jan 2020 05:27:58 -0500 by LAMP