Originally posted on : https://medium.com/analytics-vidhya/the-ultimate-handbook-for-opencv-pillow-72b7eff77cd7
Introduction
OpenCV and Pillow are the commonly used libraries in Python for image processing.
In this article, I will list out all the codes that I think are useful regarding OpenCV and Pillow side by side, so that you can compare them easily.
Updates
- 15-Jun-22: Added [13. RGBA to RGB].
0. Install & Import
To install Pillow and OpenCV, please type the following in your terminal (replace pip with pip3 if your python3 is using pip3):
pip install numpy
pip install opencv-python
pip install Pillow
Before using them, you have to import them into your python file with the following codes:
from PIL import Image
import cv2import numpy as np
Note: Since OpenCV images are actually NumPy arrays, we would also like to install and import the NumPy library.
1. Read
Read/open a colorful image:
pil_img = Image.open("your_image.jpg") # RGBcv2_img = cv2.imread("your_image.jpg") # BGR
Read/open a grayscale image:
pil_img = Image.open("your_image.jpg").convert("L")cv2_img = cv2.imread("your_image.jpg", cv2.IMREAD_GRAYSCALE)
2. Write
Write/save an image:
pil_img.save("new_image.jpg")cv2.imwrite("new_image.jpg", cv2_img)
Write/save a JPEG image with specific quality:
pil_img.save("new_image.jpg", quality=95)cv2.imwrite("new_image.jpg", cv2_img, [int(cv2.IMWRITE_JPEG_QUALITY), 95])
3. Conversion
- Pillow image to OpenCV image:
cv2_img = np.array(pil_img)
cv2_img = cv2.cvtColor(cv2_img, cv2.COLOR_RGB2BGR)
- OpenCV image to Pillow image:
cv2_img = cv2.cvtColor(cv2_img, cv2.COLOR_BGR2RGB)
pil_img = Image.fromarray(cv2_img)
Note: OpenCV images are in BGR color format, while Pillow images are in RGB color format. So we have to manaully convert the color format from one to another.
4. Shape / Size
Get the width & height & depth of an image.
- OpenCV:
if cv2_img.ndim == 2:
height, width = cv2_img.shape
depth = 1
else:
height, width, depth = cv2_img.shape
- Pillow:
width, height = pil_img.size cv2_img = np.array(pil_img)
if cv2_img.ndim == 2:
depth = 1
else:
depth = cv2_img.shape[-1]
Note: It is hard to get the depth/channels directly from a Pillow image object, the easier way to do this would be to first convert it to an OpenCV image (ndarray) and then get the shape.
5. Resize
Resize without preserving the aspect ratio:
pil_img_resized = pil_img.resize((NEW_WIDTH, NEW_HEIGHT))cv2_img_resized = cv2.resize(cv2_img, (NEW_WIDTH, NEW_HEIGHT))
Resize and preserve the aspect ratio:
- OpenCV:
scale_ratio = 0.6
width = int(img.shape[1] * scale_ratio)
height = int(img.shape[0] * scale_ratio)
dim = (width, height)cv2_img_resized = cv2.resize(cv2_img, dim, interpolation=cv2.INTER_AREA)
- Pillow:
# scale ratio = min(max_width/width, max_height/height)
max_width = 256
max_height = 256
pil_img.thumbnail((max_width, max_height), Image.ANTIALIAS)
6. Rotate
- OpenCV (only 90, 180, 270 degrees):
# rotate 90 degrees clockwise
rotated_cv2_img = cv2.rotate(cv2_img, cv2.ROTATE_90_CLOCKWISE)# rotate 180 degrees
rotated_cv2_img = cv2.rotate(cv2_img, cv2.ROTATE_180)# rotate 270 degrees clockwise (90 degrees counterclockwise)
rotated_cv2_img = cv2.rotate(cv2_img, cv2.ROTATE_90_COUNTERCLOCKWISE)
Rotate an image by any angles
- OpenCV (any angles):
# rotate 45 degrees counterclockwiseh, w = cv2_img.shape[:2]
M = cv2.getRotationMatrix2D(center=(w/2, h/2), angle=45, scale=1.0)
rotated_cv2_img = cv2.warpAffine(cv2_img, M, (w, h))
- Pillow (any angles):
# rotate 45 degrees counterclockwiserotated_pil_img = pil_img.rotate(45)
Rotate an image with bounding
- OpenCV:
Note: you need to install imutils by:
pip install imutils
import imutils# rotate 45 degrees counterclockwise
rotated_cv2_img = imutils.rotate_bound(cv2_img, -45)
- Pillow:
rotated_pil_img = pil_img.rotate(45, expand=True)
7. Flip
- OpenCV:
# flip vertically
flipped_cv2_img = cv2.flip(cv2_img, flipCode=0)# flip horizontally
mirrored_cv2_img = cv2.flip(cv2_img, flipCode=1)# flip vertically & horizontally (ratate 180 degrees)
rotated_cv2_img = cv2.flip(cv2_img, flipCode=-1)
- Pillow:
from PIL import Image, ImageOps# flip vertically
flipped_pil_img = ImageOps.flip(pil_img)# flip horizontally
mirrored_pil_img = ImageOps.mirror(pil_img)
8. Translate
- OpenCV:
shift_x = 10
shift_y = 20
h, w = cv2_img.shape[:2]M = np.float32([[1, 0, shift_x],[0, 1, shift_y]])
shifted_cv2_img = cv2.warpAffine(cv2_img, M, (w, h))
- Pillow:
shift_x = 10
shift_y = 20shifted_pil_img = pil_img.rotate(0, translate=(shift_x, shift_y))
Note:
pil_img.rotate(angle, translate)
will translate before rotation.
9. Background Color
You can specify the background color when you rotate/translate the image.
- OpenCV:
c = (127, 255, 255) # BGR
cv2_img = cv2.warpAffine(cv2_img, M, (cols, rows), borderValue=c)
- Pillow:
c = (255, 255, 127) # RGB
pil_img = pil_img.rotate(45, fillcolor=c)
10. Warp / Transform
Warp images using perspective transformation.
- OpenCV:
h, w = cv2_img.shape[:2]
shear_x = int(round(0.5 * w))
new_w = w + shear_xsrc_rect = np.array([[0, 0], [0, h], [w, h], [w, 0]], dtype=np.float32)
dst_rect = np.array([[0, 0], [shear_x, h], [new_w, h], [w, 0]], dtype=np.float32)M = cv2.getPerspectiveTransform(src_rect, dst_rect)
warped_cv2_img = cv2.warpPerspective(cv2_img, M, (new_w, h))
src_rect = np.array([u1, u2, u3, u4], dtype=np.float32)
dst_rect = np.array([v1, v2, v3, v4], dtype=np.float32)
- Pillow:
def find_coeffs(pa, pb):
matrix = []
for p1, p2 in zip(pb, pa):
matrix.append([p1[0], p1[1], 1, 0, 0, 0,
-p2[0]*p1[0], -p2[0]*p1[1]])
matrix.append([0, 0, 0, p1[0], p1[1], 1,
-p2[1]*p1[0], -p2[1]*p1[1]]) A = np.matrix(matrix, dtype=np.float)
B = np.array(pa).reshape(8) res = np.dot(np.linalg.inv(A.T * A) * A.T, B)
return np.array(res).reshape(8)w, h = pil_img.size
shear_x = int(round(0.5 * w))
new_w = w + shear_x
coeffs = find_coeffs([(0, 0), (w, 0), (w, h), (0, h)],
[(0, 0), (w, 0), (new_w, h), (shear_x, h)])warped_pil_img = pil_img.transform((new_w, h), Image.PERSPECTIVE, coeffs, Image.BICUBIC)
Where
pa
is the four vertices in the current plane, andpb
contains four vertices in the resulting plane. (find_coeffs function, modified from here)
coeffs = find_coeffs([u1, u2, u3, u4], [v1, v2, v3, v4])
11. Base64
Read image file as base64:
import base64with open("your_image.jpg", "rb") as f:
base64_str = base64.b64encode(f.read())
Conversion between Pillow & base64:
import base64
from io import BytesIO
from PIL import Imagedef pil_to_base64(pil_img):
img_buffer = BytesIO()
pil_img.save(img_buffer, format='JPEG')
byte_data = img_buffer.getvalue()
base64_str = base64.b64encode(byte_data)
return base64_strdef base64_to_pil(base64_str):
pil_img = base64.b64decode(base64_str)
pil_img = BytesIO(pil_img)
pil_img = Image.open(pil_img)
return pil_img
Conversion between OpenCV & base64:
import base64
import numpy as np
import cv2def cv2_base64(cv2_img):
base64_str = cv2.imencode('.jpg', cv2_img)[1].tostring()
base64_str = base64.b64encode(base64_str)
return base64_strdef base64_cv2(base64_str):
imgString = base64.b64decode(base64_str)
nparr = np.fromstring(imgString, np.uint8)
cv2_img= cv2.imdecode(nparr, cv2.IMREAD_COLOR)
return cv2_img
12. Read from URL
Read an image from a URL.
- OpenCV (without request headers):
import urllib.requestres = urllib.request.urlopen(url)
arr = np.asarray(bytearray(res.read()), dtype=np.uint8)
cv2_img = cv2.imdecode(arr, cv2.IMREAD_UNCHANGED)
- OpenCV (with request headers):
import urllib.requestheaders = {'referer': 'https://www.google.com'}
req = urllib.request.Request(url, headers=headers)
res = urllib.request.urlopen(req)
arr = np.asarray(bytearray(res.read()), dtype=np.uint8)
cv2_img = cv2.imdecode(arr, cv2.IMREAD_UNCHANGED)
- Pillow (without request headers):
import requestspil_img = Image.open(requests.get(url, stream=True).raw)
- Pillow (with request headers):
import requestsheaders = {'referer': 'https://www.google.com'}
pil_img = Image.open(requests.get(url, headers=headers, stream=True).raw)
13. RGBA to RGB
Convert transparent pixels to white pixels (by pasting the RGBA image on a white RGB image).
- OpenCV:
def cv2_RGBA2RGB(img):
b, g, r, a = cv2.split(img)
alpha = a / 255
r = (255 * (1 - alpha) + r * alpha).astype(np.uint8)
g = (255 * (1 - alpha) + g * alpha).astype(np.uint8)
b = (255 * (1 - alpha) + b * alpha).astype(np.uint8)
new_img = cv2.merge((b, g, r))
return new_img
- Pillow:
def pil_RGBA2RGB(img):
img.load() # for png.split()
bg = Image.new("RGB", img.size, (255, 255, 255))
bg.paste(img, mask=img.split()[3]) # 3 is the alpha channel
return bg
Conclusion
These are all the codes that I think will be commonly used. I will update this list if I find any new useful functions in image processing in the future.
If I have left out anything important or you think there are still other useful functions that I can add to this handbook, please leave a comment and let me know.