user366312
user366312

Reputation: 17006

How can I improve color thresholding output?

In the above article they have the following image:

enter image description here

And, they wanted to obtain an output like the following:

enter image description here

I ran the following script:

import cv2

window_name = 'image'

img = cv2.imread("photo.png")

cv2.imshow(window_name, img)
cv2.waitKey(0)


gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.medianBlur(gray, 5)
edges = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 9, 9)

cv2.imshow(window_name, edges)
cv2.waitKey(0)

color = cv2.bilateralFilter(img, 9, 250, 250)
cartoon = cv2.bitwise_and(color, color, mask=edges)

cv2.imshow(window_name, cartoon)
cv2.waitKey(0)

Firstly, the script is very slow.

Secondly, the output is not what they promised would be:

enter image description here

How can I fix these two issues?

Upvotes: 1

Views: 749

Answers (2)

cyang
cyang

Reputation: 166

Brief description

I'm so interested in your question, so I tried your suggested website's code, the code you posted, and myself googled a few to tried. Even discussed with my peers, my professor who taught introductory image processing/computer vision using C# that I took couple years ago.

Discussion feedback

Sadly they all respond the same and like what I initially thought, it's not possible to transform/convert directly into the second picture in your post, the posted second picture is most likely to be an artistic graphics photo. Well, maybe you dig deeper maybe there's actually a module or library that can actually transform/convert it 100% like the second picture.

Examples code testing

So, I begin trying out the contents of your posted website, snipped a bit there, adjusted some, but overall, no where near to the second cartoon picture.

  1. The code and result of "Converting An Image To A Cartoon Using OpenCV"
import cv2
from matplotlib import pyplot as plt


# Reading image
img = cv2.imread("img.png")
plt.imshow(img)

# Converting to RGB
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(img)

# Detecting edges of the input image
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.medianBlur(gray, 9)
edges = cv2.adaptiveThreshold(
            gray, 255, 
            cv2.ADAPTIVE_THRESH_MEAN_C,
            cv2.THRESH_BINARY, 9, 9
        )

# Cartoonifying the image
color = cv2.bilateralFilter(img, 9, 250, 250)
cartoon = cv2.bitwise_and(color, color, mask=edges)
plt.imshow(cartoon)
plt.savefig("cartoonify.png")
plt.show()

result of "Converting An Image To A Cartoon Using
OpenCV"

  1. Moving on, then I tried your code in the post, and it's actually made some differences, and it doesn't run slow or didn't make changes. I ran your code, and it did made some change, the code stays pretty much the same, just added saving image methods at the end, cv2.imwrite().
import cv2
import matplotlib.pyplot as plt

window_name = "image"

img = cv2.imread("img.png")

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.medianBlur(gray, 5)
edges = cv2.adaptiveThreshold(
            gray, 255,
            cv2.ADAPTIVE_THRESH_MEAN_C,
            cv2.THRESH_BINARY,
            9, 9
        )

color = cv2.bilateralFilter(img, 9, 250, 250)
cartoon = cv2.bitwise_and(color, color, mask=edges)

cv2.imshow(window_name, cartoon)
cv2.waitKey(0)

cv2.imwrite("cartoon_op.png", cartoon)
cv2.waitKey(0)
cv2.destroyAllWindows()

op's code's result image

  1. The third, I searched on github, found this code, but for this I used my stackoverlfow profile picture, which it's a headshot, I thought maybe the white background would make more visible difference, but it didn't, compared to previous examples, it's pretty much close.
import cv2
import numpy as np
from tkinter.filedialog import *

photo = askopenfilename()
img = cv2.imread(photo)

grey = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
grey = cv2.medianBlur(grey, 5)
edges = cv2.adaptiveThreshold(grey, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 9, 9)

#cartoonize
color = cv2.bilateralFilter(img, 9, 250, 250)
cartoon = cv2.bitwise_and(color, color, mask = edges)

cv2.imshow("Image", img)
cv2.imshow("Cartoon", cartoon)

#save
cv2.imwrite("cartoon-git.png", cartoon)
cv2.waitKey(0)
cv2.destroyAllWindows()

headshot

  1. Just before almost finished with the answer, I found this example gives the closest result of cartoonized picture example on Dev - How to cartoonize an image with Python, this example used Elon Musk's photo to demonstrate, although it's the closest to cartoon, but the size somehow just got really small.
import numpy as np
import cv2

file_name = "elon.jpg"

def resize_image(image):
    scale_ratio = 0.3
    width = int(image.shape[1] * scale_ratio)
    height = int(image.shape[0] * scale_ratio)
    new_dimensions = (width, height)
    resized = cv2.resize(
                image, new_dimensions,
                interpolation=cv2.INTER_AREA
            )
    return resized

def find_countours(image):
    contoured_image = image
    gray = cv2.cvtColor(contoured_image, cv2.COLOR_BGR2GRAY)
    edged = cv2.Canny(gray, 30, 100)
    contours, hierarchy = cv2.findContours(
                            edged, cv2.RETR_EXTERNAL,
                            cv2.CHAIN_APPROX_NONE
                        )
    cv2.drawContours(
        contoured_image, contours, 
        contourIdx=-1, color=1,
        thickness=1
    )
    cv2.imshow("Image after contouring", contoured_image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

    return contoured_image

def color_quantization(image, k=4):
    z = image.reshape((-1, 3))
    z = np.float32(z)
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 
                10000, 0.0001)
    compactness, label, center = cv2.kmeans(z, k, None, criteria,
                                            1, cv2.KMEANS_RANDOM_CENTERS)
    center = np.uint8(center)
    res = center[label.flatten()]
    res2 = res.reshape((image.shape))

    return res2

if __name__ == '__main__':
    
    image = cv2.imread(file_name)
    resized_image = resize_image(image)
    coloured = color_quantization(resized_image)
    contoured = find_countours(coloured)
    final_image = contoured
    save_q = input("Save the image? [y]/[n]: ")

    if save_q == "y":
        cv2.imwrite("cartoonized_" + file_name, final_image)
        print("Image saved!")

Original Elon.jpg

orig elon.jpg

Cartoonized Elon.jpg

Elon-Must-Cartoonized

Wrapping up

I hope this long answer that sounded like no definitive answer helps, it's just what I found interested and decided to share the process of discovering it.

Upvotes: 1

fmw42
fmw42

Reputation: 53174

One simple approach is to use stylization in Python/OpenCV in the Non-Photorealistic Rendering in the Computational Photography section to make a "cartoon". Reference to algorithm is at https://www.inf.ufrgs.br/~eslgastal/DomainTransform/Gastal_Oliveira_SIGGRAPH2011_Domain_Transform.pdf

Input:

enter image description here

import cv2

# read image
img = cv2.imread('beard_man.png')

# use mask with input to do inpainting
result = cv2.stylization(img, sigma_s=50, sigma_r=0.8) 

# write result to disk
cv2.imwrite("beard_man_cartoon.png", result)

# display it
cv2.imshow("RESULT", result)
cv2.waitKey(0)

Result:

enter image description here

Upvotes: 2

Related Questions