How to pixelate image using OpenCV in Python?
Asked Answered
S

2

13

Top answer in this link How to pixelate a square image to 256 big pixels with python? uses PIL to pixelate image. Converting image from PIL to cv2.Mat is possible but I'm not allowed to use other library, and I couldn't find any good method using opencv.

Is there any way to pixelate image using OpenCV library only in Python? Any sample image is fine. Solution with pixel size parameter that I can control for later adjustment would be very appreciated.

Sarilda answered 4/4, 2019 at 5:39 Comment(1)
See #63768745Salsala
U
28

EDIT^2

With the help of himself, I moved Mark Setchell's answer, which is the above mentioned top answer, to plain OpenCV Python code. (Have a look at the revision history of my answer to see the old version using a loop.)


import cv2

# Input image
input = cv2.imread('images/paddington.png')

# Get input size
height, width = input.shape[:2]

# Desired "pixelated" size
w, h = (16, 16)

# Resize input to "pixelated" size
temp = cv2.resize(input, (w, h), interpolation=cv2.INTER_LINEAR)

# Initialize output image
output = cv2.resize(temp, (width, height), interpolation=cv2.INTER_NEAREST)

cv2.imshow('Input', input)
cv2.imshow('Output', output)

cv2.waitKey(0)

Input (from linked question):

Input

Output:

Output

Disclaimer: I'm new to Python in general, and specially to the Python API of OpenCV (C++ for the win). Comments, improvements, highlighting Python no-gos are highly welcome!

Unnecessary answered 4/4, 2019 at 6:28 Comment(16)
Why not cv2.resize(...INTER_NEAREST...)?Brasher
@MarkSetchell Do you refer to the "first" cv2.resize(...) or the "second" (in your code from the linked answer)? For the latter: I tried that, but I got some interpolated image, and not that "pixelated" kind of. There seem to be differences in PIL and OpenCV regarding this issue - which I assume is the reason, the questioner posted this question in first place.Unnecessary
I just had a go and it seems to work fine for me if I replace all your code after the word "Initialize" with this big = cv2.resize(temp, (w, h), 0, 0, cv2.INTER_NEAREST) Don't worry, you already have my vote anyway ;-)Brasher
@MarkSetchell Then, I get a 16 x 16 image, not a "pixelated" 400 x 400 image. And, if I replace (w, h) with (width, height), I get the afore-mentioned interpolated image. If you have working code, I highly encourage you to post an own answer. I'd be interested, if/why my code/setup fails.Unnecessary
D'oh, you are right - I am mistaken. I had left w and h at 16 and my viewer was zoomed! I get the same as you if I put in 400. Sorry! I better go get a coffee to wake up properly! I'll delete this bit of conversation in a minute so I don't confuse future readers.Brasher
More experimentation, but still no coffee, yields this which does seem to work big = cv2.resize(temp, (400, 400), 0, 0, interpolation=cv2.INTER_NEAREST)Brasher
Aaaahhh, Python! I missed the dst parameter in cv2.resize(...). If you put in something there, the cv2.INTER_NEAREST is correctly mapped to the interpolation parameter, without explicitly mentioning it. As I said: Python noob... I will update my answer then. Thanks for your tenacity (if the use of this word is correct here)!Unnecessary
Cool :-) Teamwork! Ein bisschen Zusammenarbeit ist immer gut :-)Brasher
Very nice approach. I also made same mistake as you with that Python syntax. Well done and thanks to both of you @MarkSetchell! This answer needs more attention and voteSarilda
Great stuff, but how would you pixelate a certain area of the image only (eg. just the sandwich), firstly as a square area and then even better an ellipse or circle. I guess one way would be to cut this image with a transparent background and overlay it?Rasp
@Rasp That's what I'd do, yes. Mask the area of interest, and then copy that sub-image from the pixelated to the original one. If you get stuck on that task or have any further problems/tasks related to that, feel free to ask a new question and link this one.Unnecessary
@MarkSetchell why is this answer split up? #47143832Talmud
@Talmud I'm not sure I remember why. Sometimes I answer questions more than once because OP asks for something in Python and OpenCV but I can do it much more simply with an ImageMagick one-liner, then later I realise how I could do it with Python and, as it's based on totally distinct technology, I make a new answer to keep things clear. Other times I put 4 different techniques in one answer https://mcmap.net/q/908112/-how-to-overlay-segmented-image-on-top-of-main-image-in-python because there is no response from OP so I work on different solutions that may appeal more to them. It's quite fluid.Brasher
@Talmud because the other post asks for Python solution while I asked for specific opencv solutionSarilda
Likely revenge downvoted following a dispute from here.Unnecessary
A minor no-go: you should never name your variables built-in names like input.Crean
O
0

For anyone looking for alternate solution,

import cv2
import numpy as np

img = cv2.imread('input.png')
height, width, channels = img.shape

pixel_size = 50

# Pad image
pad_x = (pixel_size - width % pixel_size) % pixel_size
pad_y = (pixel_size - height % pixel_size) % pixel_size
img = np.pad(img, ((0, pad_y), (0, pad_x), (0, 0)), mode='reflect')

# Reshape image into blocks and compute average color of each block
h, w, c = img.shape
blocks = np.mean(img.reshape(h//pixel_size, pixel_size, -1, pixel_size, c), axis=(1, 3))

# Repeat average color of each block to fill corresponding region in the image
output = np.repeat(np.repeat(blocks, pixel_size, axis=1), pixel_size, axis=0)

# Remove padding
output = output[:height, :width].astype("uint8")

cv2.imshow('Output', output)
cv2.waitKey(0)
Osteogenesis answered 5/5, 2023 at 7:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.