-1

I want to threshold an image regardless of the color variations. Ex:

input image

I want to threshold both these letters in the input and show in output. But when I threshold the image, only one letter is shown. How can I do this using Opencv and Python?

Additional Info with example: new exampleThe two input images of the example show that color of the letter of one image is lighter than background color and the other image, letter is darker. What I want is to threshold both these images to get the same output. (A black letter in white background)

BuddyRJ
  • 9
  • 1
  • 5
  • I'm still unclear, but let's continue anyway. How about thresholding and checking if the top-left pixel is black and inverting the image if so? – Mark Setchell Jan 14 '18 at 11:13
  • Thanx a lot @MarkSetchell. Yeah that logic could solve most of the cases. I think it is more suitable for instances where the required region (letter) is near to the center of the image, and not near to the image's boundaries. And It could be extended to scan more than 1 pixel of the corners. Thanx again for your help to figure this out. – BuddyRJ Jan 14 '18 at 11:31

3 Answers3

2

If you directly convert this color image to gray and threshold it, then you will get this. It's not suitable for threshold the two A:

enter image description here

But if you split the channels in BGR, you will get this:

enter image description here


Clearly, threshold the B channel will work.

enter image description here


#!/usr/bin/python3
# 2018.01.14 16:31:39 CST
# 2018.01.14 16:50:45 CST
import cv2
img = cv2.imread("img12.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
b,g,r = cv2.split(img)
th, threshed1 = cv2.threshold(gray, 100, 255, cv2.THRESH_BINARY|cv2.THRESH_OTSU)
th, threshed2 = cv2.threshold(b, 100, 255, cv2.THRESH_BINARY|cv2.THRESH_OTSU)
cv2.imwrite("threshed_gray.png", threshed1)
cv2.imwrite("threshed_blue.png", threshed2)
Kinght 金
  • 17,681
  • 4
  • 60
  • 74
  • Thank you so much for helping @Silencer. Can you do one more clarification? Is there a general method to threshold any image to get that same output? What I meant is if I split the input Image as two separate images and sent it as input, each time, the output must be same. – BuddyRJ Jan 14 '18 at 10:06
  • @BuddyRJ If you find a general method, please tell me. – Kinght 金 Jan 14 '18 at 10:12
  • Thank you so much. I'll let you know. – BuddyRJ Jan 14 '18 at 10:18
  • @BuddyRJ Maybe you could click `edit` under your original question and clearly state what *"the general question"* is please? You may get a general answer then. At the moment, it's unclear to me at least, what the constraints are? Are we always talking about 2 differently coloured letters on a 3rd colour, unpatterned background for example? – Mark Setchell Jan 14 '18 at 10:54
  • @MarkSetchell Thank you. I edited the question with more clarifications. Hope my question is clearer than before. – BuddyRJ Jan 14 '18 at 11:09
  • @BuddyRJ You can add new thing, but do not delete the original. – Kinght 金 Jan 14 '18 at 11:29
  • @Silencer Yes, sorry if I was unclear - I definitely meant for you to add clarification rather than change the question wholesale. – Mark Setchell Jan 14 '18 at 12:11
  • Sorry about that @Silencer. I rolled back the edits. And thank you for the advice and help. – BuddyRJ Jan 14 '18 at 17:17
0

If your background is a constant color, you can search for an exact or very close match and create a new image from that. Wherever the color is the background color set a white pixel, otherwise set a black pixel. No thresholding required.

Here's what that would look like in PIL rather than Opencv:

from PIL import Image
im = Image.open(filename)
ld = im.load()
bg_r, bg_g, bg_b = ld[0,0]
for y in range(im.size[1]):
    for x in range(im.size[0]):
        r, g, b = ld[x,y]
        if abs(r - bg_r) <= 1 and abs(g - bg_g) <= 1 and abs(b - bg_b) <= 1:
            ld[x,y] = (255, 255, 255)
        else:
            ld[x,y] = (0, 0, 0)

enter image description here

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
0

If you know that the background color is constant you can create a binary image of non-background colors.

out = im != BG_COLOR

i.e.

import cv2
im = cv2.imread(r"A.png")
BG_COLOR = im[0:0]

out = im != BG_COLOR

cv2.imwrite("A_masked.png", out * 255) # make it 8bit for output

Outputs the masked image:

enter image description here

Mike Robins
  • 1,733
  • 10
  • 14