[PYTHON] Block and draw images in Minecraft Pi Edition

I bought a Raspberry Pi at previous article and still only use Bluetooth, but I noticed. Yes, that Minecraft is installed on Raspbian!

I wanted to try Minecraft, so I'll play a little.

Minecraft Pi Edition I found a summary site. https://matome.naver.jp/m/odai/2142545327953860701

The features of Pi Edition are as follows.

There seems to be an unofficial patch that enables survival mode. The size of the plane is only 256 x 256, so if you want to program and play and do it in earnest, you might buy the PC version.

Official Documentもプログラミングすること前提で書かれているみたい。

What to make

The map tile that comes to mind when you hear 256x256! (Occupational disease) https://en.m.wikipedia.org/wiki/Tiled_web_map

Let's try drawing a map tile.

What I used

I didn't know if Python3 could be used, so I chose Python2.

Implementation

Read the image with OpenCV, check the RGB value of each pixel, and place blocks close to that color.

OpenCV installation

I was able to install it by following the steps in the following article. http://qiita.com/suppy193/items/91609e75789e9f458c39

$ sudo apt-get update
$ sudo apt-get install libopencv-dev
$ sudo apt-get install python-opencv

Load image & get RGB value of pixel

It's easy with OpenCV.

import cv2

img = cv2.imread('image.png')

column = 10
row = 20
pixel = img[column, row]
print(pixel)

When you execute the above, the output will be as follows. The order is BGR, not RGB.

>>>
[ 57  70 255]

Place blocks of similar color

We use wool (wool) that can be painted in 16 colors. The RGB values for each color were listed below. http://minecraft.gamepedia.com/Wool

The code to put the block looks like this: When executed, the block at the player's feet will turn orange.

from mcpi import minecraft
mc = minecraft.Minecraft.create()
x, y, z = mc.player.getPos()

wool = 35
selectedColor = 1 # Orange
mc.setBlock(x, y-1, z, wool, selectedColor)

As for which color to use for each pixel, find the difference between RGB values with numpy.linalg.norm and use the color with the smallest difference.

dist = numpy.linalg.norm(woolColor - pixel)

Draw an image in a loop

It loops the process of placing blocks of colors close to RGB values and converts each pixel of the image into blocks.

The final code is below.

minecraft.py


from mcpi import minecraft
import cv2
import numpy

mc = minecraft.Minecraft.create()

wool = 35

# wool color BGR
woolColors = [
    [221,221,221],  # 0: White
    [62,125,219], # 1: Orange
    [188,80,179], # 2: Magenta
    [201,138,107], # 3: Light blue
    [39,166,177], # 4: Yellow
    [56,174,65], # 5: Lime
    [153,132,208], # 6: Pink
    [64,64,64], # 7: Gray
    [161,161,154], # 8: Light gray
    [137,110,46], # 9: Cyan
    [181,61,126], # 10: Purple
    [141,56,46], # 11: Blue
    [31,50,79], # 12: Brown
    [27,70,53], # 13: Green
    [48,52,150], # 14: Red
    [22,22,25] # 15:Black
]

img = cv2.imread('image.png')
img = cv2.resize(img, (256, 256))

height, width = img.shape[:2]
x, y, z = mc.player.getPos()

for column in range(width):
    for row in range(height):
        pixel = img[column, row]

        selectedColor = 0
        minDist = None
        for i, woolColor in enumerate(woolColors):
            dist = numpy.linalg.norm(woolColor - pixel)
            if  minDist == None or minDist > dist:
                minDist = dist
                selectedColor = i

        mc.setBlock(-153+row, y-1, -116+column, wool, selectedColor)

mc.postToChat("Create Image")

The x and z coordinates are based on the world edge, but for some reason it was [-153, -116]. The y coordinate is -1 from the player's position, and the blocks are arranged on the plane on which the player is standing.

Execution result

With Minecraft Pi Edition running, run the Python script. You can do this by pressing the "F5" key or by pressing "Run"> "Run Module".

It takes a lot of time because we are comparing 65,536 pixels with 16 colors. Please wait patiently for a few minutes.

For the original image, I used the OpenStreetMap map tile (around Shibuya station).

IMG_1436.jpg

IMG_1438.jpg

© OpenStreetMap contributors http://www.openstreetmap.org/copyright

Due to camera work, it is not possible to see the whole thing at once. The shape of the icon is clearly visible, but the letters in the note are broken to the extent that they can be read. Also, the light gray and light blue became white blocks, and the boundaries between the buildings and the site disappeared, so the result was a little subtle as a map.

Summary

I can draw more than I expected with simple logic, but I have the impression that the number of colors is not enough. If you use blocks other than wool and assign the missing colors, the image quality will improve, but it will increase the processing time, so it is a thought.

Bonus (main story)

I tried a map tile with a size of 256 x 256, but if the image has the same number of pixels in the vertical and horizontal directions, it should be resized with OpenCV and then processed. (I also put it in the code example)

So, the images distributed for the Twitter icon are square and have a small resolution, aren't they?

Let's read it appropriately.

IMG_1441.jpg

IMG_1442.jpg

IMG_1443.jpg

This one can draw better at all!

There is no change in the tendency for light-colored areas such as hair and skin to become white blocks. It might be a good idea to add blocks that are close to flesh tones or brown (Sandstone or Staind Clay).

Recommended Posts

Block and draw images in Minecraft Pi Edition
Hello World with Raspberry Pi + Minecraft Pi Edition
Operate Minecraft Pi Edition from your PC
How to draw OpenCV images in Pygame
Draw a weakness graph in Python and save it in various formats (Raspberry Pi, macOS)
Calculate and draw bounded (closed) Voronoi diagrams in Python
Carefully understand the exponential distribution and draw in Python
Carefully understand the Poisson distribution and draw in Python