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.

gameon67
  • 3,981
  • 5
  • 35
  • 61
  • See https://stackoverflow.com/questions/63768245/how-i-can-pixelate-a-image-from-opencv-video-capture-in-real-time/63768858#63768858 – fmw42 May 05 '23 at 15:28

2 Answers2

26

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!

Community
  • 1
  • 1
HansHirse
  • 18,010
  • 10
  • 38
  • 67
  • Why not `cv2.resize(...INTER_NEAREST...)`? – Mark Setchell Apr 04 '19 at 07:17
  • 1
    @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. – HansHirse Apr 04 '19 at 07:22
  • 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 ;-) – Mark Setchell Apr 04 '19 at 07:57
  • @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. – HansHirse Apr 04 '19 at 08:10
  • 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. – Mark Setchell Apr 04 '19 at 08:16
  • 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)` – Mark Setchell Apr 04 '19 at 08:26
  • 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)! – HansHirse Apr 04 '19 at 08:32
  • 1
    Cool :-) Teamwork! Ein bisschen Zusammenarbeit ist immer gut :-) – Mark Setchell Apr 04 '19 at 08:34
  • 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 vote – gameon67 Apr 04 '19 at 08:59
  • 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? – blissweb Jun 13 '19 at 10:07
  • @blissweb 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. – HansHirse Jun 13 '19 at 10:15
  • @MarkSetchell why is this answer split up? https://stackoverflow.com/questions/47143332/how-to-pixelate-a-square-image-to-256-big-pixels-with-python/51547855#51547855 – mLstudent33 Aug 28 '19 at 07:20
  • 2
    @mLstudent33 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://stackoverflow.com/a/57579290/2836621 because there is no response from OP so I work on different solutions that may appeal more to them. It's quite fluid. – Mark Setchell Aug 28 '19 at 07:42
  • @mLstudent33 because the other post asks for Python solution while I asked for specific opencv solution – gameon67 Aug 28 '19 at 08:42
  • Likely revenge downvoted following a dispute from [here](https://stackoverflow.com/questions/59963173/). – HansHirse Jan 29 '20 at 10:37
  • A minor no-go: you should never name your variables built-in names like `input`. – LMCuber Oct 20 '21 at 16:05
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)
Harisreedhar
  • 173
  • 1
  • 5