Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

Thresholding + masking image erratic results

Hi! I'm trying to threshold + mask this image:

Orgiginal image

However, using the code below this produces erratic and random images: Erratic image #1 Erratic image #2 Erratic image #3

I'm guessing it has something to do with the binary header imprinted in beginning of the produced images, however, I'm not where they are coming from. Also, considering that the images change randomly, the header is to some extent unique for each image.

The input image is all positive thresholds.

Any insights are most welcome!

from PIL import Image, ImageMath
import cv2, numpy, math, os, copy

A=0
B=1
C=2
MIN=0
MAX=1
U=0
L=1


class Thresholder():
  """Class for performing thresholding and/or conversions."""
  # _thresholds=[]
  # bounds=[[],[]]
  # conversion_factor = None
  # neg_test = False
  # pos_test = False

  def __init__(self,thresholds,from_cs="rgb",to_cs="rgb"):
    """Instantiates a new thresholding engine.

    Args:
      thresholds: 2d array of the form
        [   [cs_val_min_a,  cs_val_max_a],
          [cs_val_min_b,  cs_val_max_b],
          [cs_val_min_c,  cs_val_max_c]   ]

      ...where a,b and c are the channels of 
        the target (threshold) colorspace.

      Negative value in either max or min of a channel
        will trigger a NOT threshold (i.e. inverted).


      from_colorspace: Originating test set colorspace.
      to_colorspace: Colorspace of threshold values.

    Returns:
      A new Thresholding object.
    """

    self._thresholds=thresholds
    from_cs=from_cs.upper()
    to_cs=to_cs.upper()
    self.conversion_factor = self._check_cs_conversion(from_cs,to_cs)
    upper=(thresholds[A][MAX],thresholds[B][MAX],thresholds[C][MAX])
    lower=(thresholds[A][MIN],thresholds[B][MIN],thresholds[C][MIN])
    self.bounds=(upper,lower)
    upper = []
    lower = []
    self.inverts=[]

    # TODO(jonas): Optimize iterations
    for i in range(3):
      u=self.bounds[U][i]
      l=self.bounds[L][i]
      if l < 0 or u < 0:
        l=abs(l)
        u=abs(u)
        self.inverts.append(True)
      else:
        self.inverts.append(False)
      upper.append(u)
      lower.append(l)
    upper=numpy.array(upper,dtype=numpy.uint8)
    lower=numpy.array(lower,dtype=numpy.uint8)
    self.bounds=[upper,lower]

    if (True not in self.inverts or False not in self.inverts):
      self.pos_test = copy.deepcopy(self.bounds)
      self.neg_test = copy.deepcopy(self.bounds)
      for i,b in enumerate(self.inverts):
        if self.inverts[i] is True:
          self.pos_test[U][i]=255
          self.pos_test[L][i]=0
        else:
          self.neg_test[U][i]=255
          self.neg_test[L][i]=0


  def threshold_image(self,image):
    """Performs thresholding on a whole image.

    Args:
      image: One of either formats below:
          PIL (as RGB)
          (numpy array) todo
          (cv-image) todo

    Returns:
      The thresholded image as PIL (RGB) image,
      with positive bits set to 255 and false to 0.
    """

    image=self.pil_2_cv(image) # Still RGB, not BGR.

    if self.conversion_factor is not None:
      print "I was never here"
      image = cv2.cvtColor(image,self.conversion_factor)
      print "Using %s" % self.conversion_factor

    if True not in self.inverts:        # No inverted tests
      mask = cv2.inRange(image, self.bounds[L], self.bounds[U])
    elif False not in self.inverts:     # All inverted tests
      print "I was never here"
      negMask=cv2.inRange(image, self.bounds[L], self.bounds[U])
      mask = cv2.bitwise_not(negMask)
    else:                               # Mix inverted and non-inverted
      print "I was never here"
      nots=cv2.inRange(image, self.neg_test[L], self.neg_test[U])
      yeses=cv2.inRange(image, self.pos_test[L], self.os_test[U])
      nots=cv2.bitwise_not(nots)
      mask = cv2.bitwise_and(nots,yeses)
    im=cv2.bitwise_and(image,image,mask=mask)
    print im.shape
    return self.cv_2_pil(im).copy()

  def cv_2_pil(self,image):
    # Converts cv-image to PIL.
    return Image.fromarray(image)

  def pil_2_cv(self,image):
    # Converts PIL-image to numpy array (for cv). Still RGB.
    return numpy.array(image)

  def _check_cs_conversion(self,from_cs,to_cs):
    """Checks if conversion between from/to is possible.

    Args:
      from_cs: originating colorspace abbreviation, capitalized
      to_cs: target (threshold) colorspace abbreviation, capitalized

    Returns:
      cv2 constant identifying conversion, or None if not found.
    """
    if "COLOR_%s2%s" % (from_cs,to_cs) in dir(cv2):
      return eval("cv2.COLOR_%s2%s" % (from_cs,to_cs))
click to hide/show revision 2
Removed excessive data

Thresholding + masking image erratic results

Hi! I'm trying to threshold + mask this image:

Orgiginal image

However, using the code below this produces erratic and random images: Erratic image #1 Erratic image #2 Erratic image #3

I'm guessing it has something to do with the binary header imprinted in beginning of the produced images, however, I'm not where they are coming from. Also, considering that the images change randomly, the header is to some extent unique for each image.

The input image is all positive thresholds.

Any insights are most welcome!

from PIL import Image, ImageMath
import cv2, numpy, math, os, copy

A=0
B=1
C=2
MIN=0
MAX=1
U=0
L=1


class Thresholder():
  """Class for performing thresholding and/or conversions."""
  # _thresholds=[]
  # bounds=[[],[]]
  # conversion_factor = None
  # neg_test = False
  # pos_test = False

  def __init__(self,thresholds,from_cs="rgb",to_cs="rgb"):
    """Instantiates a new thresholding engine.

    Args:
      thresholds: 2d array of the form
        [   [cs_val_min_a,  cs_val_max_a],
          [cs_val_min_b,  cs_val_max_b],
          [cs_val_min_c,  cs_val_max_c]   ]

      ...where a,b and c are the channels of 
        the target (threshold) colorspace.

      Negative value in either max or min of a channel
        will trigger a NOT threshold (i.e. inverted).


      from_colorspace: Originating test set colorspace.
      to_colorspace: Colorspace of threshold values.

    Returns:
      A new Thresholding object.
    """

    self._thresholds=thresholds
    from_cs=from_cs.upper()
    to_cs=to_cs.upper()
    self.conversion_factor = self._check_cs_conversion(from_cs,to_cs)
    upper=(thresholds[A][MAX],thresholds[B][MAX],thresholds[C][MAX])
    lower=(thresholds[A][MIN],thresholds[B][MIN],thresholds[C][MIN])
    self.bounds=(upper,lower)
    upper = []
    lower = []
    self.inverts=[]

    # TODO(jonas): Optimize iterations
    for i in range(3):
      u=self.bounds[U][i]
      l=self.bounds[L][i]
      if l < 0 or u < 0:
        l=abs(l)
        u=abs(u)
        self.inverts.append(True)
      else:
        self.inverts.append(False)
      upper.append(u)
      lower.append(l)
    upper=numpy.array(upper,dtype=numpy.uint8)
    lower=numpy.array(lower,dtype=numpy.uint8)
    self.bounds=[upper,lower]

    if (True not in self.inverts or False not in self.inverts):
      self.pos_test = copy.deepcopy(self.bounds)
      self.neg_test = copy.deepcopy(self.bounds)
      for i,b in enumerate(self.inverts):
        if self.inverts[i] is True:
          self.pos_test[U][i]=255
          self.pos_test[L][i]=0
        else:
          self.neg_test[U][i]=255
          self.neg_test[L][i]=0


  def threshold_image(self,image):
    """Performs thresholding on a whole image.

    Args:
      image: One of either formats below:
          PIL (as RGB)
          (numpy array) todo
          (cv-image) todo

    Returns:
      The thresholded image as PIL (RGB) image,
      with positive bits set to 255 and false to 0.
    """

    image=self.pil_2_cv(image) # Still RGB, not BGR.

    if self.conversion_factor is not None:
      print "I was never here"
      image = cv2.cvtColor(image,self.conversion_factor)
      print "Using %s" % self.conversion_factor

    if True not in self.inverts:        # No inverted tests
      mask = cv2.inRange(image, self.bounds[L], self.bounds[U])
    elif False not in self.inverts:     # All inverted tests
      print "I was never here"
      negMask=cv2.inRange(image, self.bounds[L], self.bounds[U])
      mask = cv2.bitwise_not(negMask)
    else:                               # Mix inverted and non-inverted
      print "I was never here"
      nots=cv2.inRange(image, self.neg_test[L], self.neg_test[U])
      yeses=cv2.inRange(image, self.pos_test[L], self.os_test[U])
      nots=cv2.bitwise_not(nots)
      mask = cv2.bitwise_and(nots,yeses)
    im=cv2.bitwise_and(image,image,mask=mask)
    print im.shape
    return self.cv_2_pil(im).copy()

  def cv_2_pil(self,image):
    # Converts cv-image to PIL.
    return Image.fromarray(image)

  def pil_2_cv(self,image):
    # Converts PIL-image to numpy array (for cv). Still RGB.
    return numpy.array(image)

  def _check_cs_conversion(self,from_cs,to_cs):
    """Checks if conversion between from/to is possible.

    Args:
      from_cs: originating colorspace abbreviation, capitalized
      to_cs: target (threshold) colorspace abbreviation, capitalized

    Returns:
      cv2 constant identifying conversion, or None if not found.
    """
    if "COLOR_%s2%s" % (from_cs,to_cs) in dir(cv2):
      return eval("cv2.COLOR_%s2%s" % (from_cs,to_cs))
click to hide/show revision 3
Fixed input description

Thresholding + masking image erratic results

Hi! I'm trying to threshold + mask this image:

Orgiginal image

However, using the code below this produces erratic and random images: Erratic image #1 Erratic image #2 Erratic image #3

I'm guessing it has something to do with the binary header imprinted in beginning of the produced images, however, I'm not where they are coming from. Also, considering that the images change randomly, the header is to some extent unique for each image.

The input image is an RGB (no alpha) and the thresholds are all positive thresholds.positive.

Any insights are most welcome!

from PIL import Image, ImageMath
import cv2, numpy, math, os, copy

A=0
B=1
C=2
MIN=0
MAX=1
U=0
L=1


class Thresholder():
  """Class for performing thresholding and/or conversions."""
  # _thresholds=[]
  # bounds=[[],[]]
  # conversion_factor = None
  # neg_test = False
  # pos_test = False

  def __init__(self,thresholds,from_cs="rgb",to_cs="rgb"):
    """Instantiates a new thresholding engine.

    Args:
      thresholds: 2d array of the form
        [   [cs_val_min_a,  cs_val_max_a],
          [cs_val_min_b,  cs_val_max_b],
          [cs_val_min_c,  cs_val_max_c]   ]

      ...where a,b and c are the channels of 
        the target (threshold) colorspace.

      Negative value in either max or min of a channel
        will trigger a NOT threshold (i.e. inverted).


      from_colorspace: Originating test set colorspace.
      to_colorspace: Colorspace of threshold values.

    Returns:
      A new Thresholding object.
    """

    self._thresholds=thresholds
    from_cs=from_cs.upper()
    to_cs=to_cs.upper()
    self.conversion_factor = self._check_cs_conversion(from_cs,to_cs)
    upper=(thresholds[A][MAX],thresholds[B][MAX],thresholds[C][MAX])
    lower=(thresholds[A][MIN],thresholds[B][MIN],thresholds[C][MIN])
    self.bounds=(upper,lower)
    upper = []
    lower = []
    self.inverts=[]

    for i in range(3):
      u=self.bounds[U][i]
      l=self.bounds[L][i]
      if l < 0 or u < 0:
        l=abs(l)
        u=abs(u)
        self.inverts.append(True)
      else:
        self.inverts.append(False)
      upper.append(u)
      lower.append(l)
    upper=numpy.array(upper,dtype=numpy.uint8)
    lower=numpy.array(lower,dtype=numpy.uint8)
    self.bounds=[upper,lower]

    if (True not in self.inverts or False not in self.inverts):
      self.pos_test = copy.deepcopy(self.bounds)
      self.neg_test = copy.deepcopy(self.bounds)
      for i,b in enumerate(self.inverts):
        if self.inverts[i] is True:
          self.pos_test[U][i]=255
          self.pos_test[L][i]=0
        else:
          self.neg_test[U][i]=255
          self.neg_test[L][i]=0


  def threshold_image(self,image):
    """Performs thresholding on a whole image.

    Args:
      image: One of either formats below:
          PIL (as RGB)
          (numpy array) todo
          (cv-image) todo

    Returns:
      The thresholded image as PIL (RGB) image,
      with positive bits set to 255 and false to 0.
    """

    image=self.pil_2_cv(image) # Still RGB, not BGR.

    if self.conversion_factor is not None:
      print "I was never here"
      image = cv2.cvtColor(image,self.conversion_factor)
      print "Using %s" % self.conversion_factor

    if True not in self.inverts:        # No inverted tests
      mask = cv2.inRange(image, self.bounds[L], self.bounds[U])
    elif False not in self.inverts:     # All inverted tests
      print "I was never here"
      negMask=cv2.inRange(image, self.bounds[L], self.bounds[U])
      mask = cv2.bitwise_not(negMask)
    else:                               # Mix inverted and non-inverted
      print "I was never here"
      nots=cv2.inRange(image, self.neg_test[L], self.neg_test[U])
      yeses=cv2.inRange(image, self.pos_test[L], self.os_test[U])
      nots=cv2.bitwise_not(nots)
      mask = cv2.bitwise_and(nots,yeses)
    im=cv2.bitwise_and(image,image,mask=mask)
    print im.shape
    return self.cv_2_pil(im).copy()

  def cv_2_pil(self,image):
    # Converts cv-image to PIL.
    return Image.fromarray(image)

  def pil_2_cv(self,image):
    # Converts PIL-image to numpy array (for cv). Still RGB.
    return numpy.array(image)

  def _check_cs_conversion(self,from_cs,to_cs):
    """Checks if conversion between from/to is possible.

    Args:
      from_cs: originating colorspace abbreviation, capitalized
      to_cs: target (threshold) colorspace abbreviation, capitalized

    Returns:
      cv2 constant identifying conversion, or None if not found.
    """
    if "COLOR_%s2%s" % (from_cs,to_cs) in dir(cv2):
      return eval("cv2.COLOR_%s2%s" % (from_cs,to_cs))

Thresholding + masking image erratic results

Hi! I'm trying to threshold + mask this image:

Orgiginal image

However, using the code below this produces erratic and random images: Erratic image #1 Erratic image #2 Erratic image #3

I'm guessing it has something to do with the binary header imprinted in beginning of the produced images, however, I'm not where they are coming from. Also, considering that the images change randomly, the header is to some extent unique for each image.

The input image is an RGB (no alpha) and the thresholds are all positive.

Any insights are most welcome!

from PIL import Image, ImageMath
import cv2, numpy, math, os, copy

A=0
B=1
C=2
MIN=0
MAX=1
U=0
L=1


class Thresholder():
  """Class for performing thresholding and/or conversions."""
  # _thresholds=[]
  # bounds=[[],[]]
  # conversion_factor = None
  # neg_test = False
  # pos_test = False

  def __init__(self,thresholds,from_cs="rgb",to_cs="rgb"):
    """Instantiates a new thresholding engine.

    Args:
      thresholds: 2d array of the form
        [   [cs_val_min_a,  cs_val_max_a],
          [cs_val_min_b,  cs_val_max_b],
          [cs_val_min_c,  cs_val_max_c]   ]

      ...where a,b and c are the channels of 
        the target (threshold) colorspace.

      Negative value in either max or min of a channel
        will trigger a NOT threshold (i.e. inverted).


      from_colorspace: Originating test set colorspace.
      to_colorspace: Colorspace of threshold values.

    Returns:
      A new Thresholding object.
    """

    self._thresholds=thresholds
    from_cs=from_cs.upper()
    to_cs=to_cs.upper()
    self.conversion_factor = self._check_cs_conversion(from_cs,to_cs)
    upper=(thresholds[A][MAX],thresholds[B][MAX],thresholds[C][MAX])
    lower=(thresholds[A][MIN],thresholds[B][MIN],thresholds[C][MIN])
    self.bounds=(upper,lower)
    upper = []
    lower = []
    self.inverts=[]

    for i in range(3):
      u=self.bounds[U][i]
      l=self.bounds[L][i]
      if l < 0 or u < 0:
        l=abs(l)
        u=abs(u)
        self.inverts.append(True)
      else:
        self.inverts.append(False)
      upper.append(u)
      lower.append(l)
    upper=numpy.array(upper,dtype=numpy.uint8)
    lower=numpy.array(lower,dtype=numpy.uint8)
    self.bounds=[upper,lower]

    if (True not in self.inverts or False not in self.inverts):
      self.pos_test = copy.deepcopy(self.bounds)
      self.neg_test = copy.deepcopy(self.bounds)
      for i,b in enumerate(self.inverts):
        if self.inverts[i] is True:
          self.pos_test[U][i]=255
          self.pos_test[L][i]=0
        else:
          self.neg_test[U][i]=255
          self.neg_test[L][i]=0


  def threshold_image(self,image):
    """Performs thresholding on a whole image.

    Args:
      image: One of either formats below:
          PIL (as RGB)
          (numpy array) todo
          (cv-image) todo

    Returns:
      The thresholded image as PIL (RGB) image,
      with positive bits set to 255 and false to 0.
    """

    image=self.pil_2_cv(image) # Still RGB, not BGR.

    if self.conversion_factor is not None:
      print "I was never here"
      image = cv2.cvtColor(image,self.conversion_factor)
      print "Using %s" % self.conversion_factor

    if True not in self.inverts:        # No inverted tests
      mask = cv2.inRange(image, self.bounds[L], self.bounds[U])
    elif False not in self.inverts:     # All inverted tests
      print "I was never here"
      negMask=cv2.inRange(image, self.bounds[L], self.bounds[U])
      mask = cv2.bitwise_not(negMask)
    else:                               # Mix inverted and non-inverted
      print "I was never here"
      nots=cv2.inRange(image, self.neg_test[L], self.neg_test[U])
      yeses=cv2.inRange(image, self.pos_test[L], self.os_test[U])
      nots=cv2.bitwise_not(nots)
      mask = cv2.bitwise_and(nots,yeses)
    im=cv2.bitwise_and(image,image,mask=mask)
    print im.shape
    return self.cv_2_pil(im).copy()

  def cv_2_pil(self,image):
    # Converts cv-image to PIL.
    return Image.fromarray(image)

  def pil_2_cv(self,image):
    # Converts PIL-image to numpy array (for cv). Still RGB.
    return numpy.array(image)

  def _check_cs_conversion(self,from_cs,to_cs):
    """Checks if conversion between from/to is possible.

    Args:
      from_cs: originating colorspace abbreviation, capitalized
      to_cs: target (threshold) colorspace abbreviation, capitalized

    Returns:
      cv2 constant identifying conversion, or None if not found.
    """
    if "COLOR_%s2%s" % (from_cs,to_cs) in dir(cv2):
      return eval("cv2.COLOR_%s2%s" % (from_cs,to_cs))

Thresholding + masking image erratic results

Hi! I'm trying to threshold + mask this image:

Orgiginal image

However, using the code below this produces erratic and random images: Erratic image #1 Erratic image #2 Erratic image #3

I'm guessing it has something to do with the binary header imprinted in beginning of the produced images, however, I'm not where they are coming from. Also, considering that the images change randomly, the header is to some extent unique for each image.

The input image is an RGB (no alpha) and the thresholds are all positive.

Any insights are most welcome!welcome!

(As per comments, the full code may be a bit verbose - relevant pieces first, full code below:

def threshold_image(self,image):
  # image = PIL RGB image
  assert(image.size == (258,258))
  assert(len(image.getbands()) == 3)

  image=numpy.array(image) # Still RGB, not BGR.

  assert(image.shape == (258,258,3))

  mask = cv2.inRange(image, numpy.array([0, 0, 0], dtype=numpy.uint8), 
                      numpy.array([200, 250, 251], dtype=numpy.uint8))

  im=cv2.bitwise_and(image,image,mask=mask)
  assert(image.shape == (258,258,3))

  return Image.fromarray(im)

Full code:

from PIL import Image, ImageMath
import cv2, numpy, math, os, copy

A=0
B=1
C=2
MIN=0
MAX=1
U=0
L=1


class Thresholder():
  """Class for performing thresholding and/or conversions."""
  # _thresholds=[]
  # bounds=[[],[]]
  # conversion_factor = None
  # neg_test = False
  # pos_test = False

  def __init__(self,thresholds,from_cs="rgb",to_cs="rgb"):
    """Instantiates a new thresholding engine.

    Args:
      thresholds: 2d array of the form
        [   [cs_val_min_a,  cs_val_max_a],
          [cs_val_min_b,  cs_val_max_b],
          [cs_val_min_c,  cs_val_max_c]   ]

      ...where a,b and c are the channels of 
        the target (threshold) colorspace.

      Negative value in either max or min of a channel
        will trigger a NOT threshold (i.e. inverted).


      from_colorspace: Originating test set colorspace.
      to_colorspace: Colorspace of threshold values.

    Returns:
      A new Thresholding object.
    """

    self._thresholds=thresholds
    from_cs=from_cs.upper()
    to_cs=to_cs.upper()
    self.conversion_factor = self._check_cs_conversion(from_cs,to_cs)
    upper=(thresholds[A][MAX],thresholds[B][MAX],thresholds[C][MAX])
    lower=(thresholds[A][MIN],thresholds[B][MIN],thresholds[C][MIN])
    self.bounds=(upper,lower)
    upper = []
    lower = []
    self.inverts=[]

    for i in range(3):
      u=self.bounds[U][i]
      l=self.bounds[L][i]
      if l < 0 or u < 0:
        l=abs(l)
        u=abs(u)
        self.inverts.append(True)
      else:
        self.inverts.append(False)
      upper.append(u)
      lower.append(l)
    upper=numpy.array(upper,dtype=numpy.uint8)
    lower=numpy.array(lower,dtype=numpy.uint8)
    self.bounds=[upper,lower]

    if (True not in self.inverts or False not in self.inverts):
      self.pos_test = copy.deepcopy(self.bounds)
      self.neg_test = copy.deepcopy(self.bounds)
      for i,b in enumerate(self.inverts):
        if self.inverts[i] is True:
          self.pos_test[U][i]=255
          self.pos_test[L][i]=0
        else:
          self.neg_test[U][i]=255
          self.neg_test[L][i]=0


  def threshold_image(self,image):
    """Performs thresholding on a whole image.

    Args:
      image: One of either formats below:
          PIL (as RGB)
          (numpy array) todo
          (cv-image) todo

    Returns:
      The thresholded image as PIL (RGB) image,
      with positive bits set to 255 and false to 0.
    """

    image=self.pil_2_cv(image) # Still RGB, not BGR.

    if self.conversion_factor is not None:
      print "I was never here"
      image = cv2.cvtColor(image,self.conversion_factor)
      print "Using %s" % self.conversion_factor

    if True not in self.inverts:        # No inverted tests
      mask = cv2.inRange(image, self.bounds[L], self.bounds[U])
    elif False not in self.inverts:     # All inverted tests
      print "I was never here"
      negMask=cv2.inRange(image, self.bounds[L], self.bounds[U])
      mask = cv2.bitwise_not(negMask)
    else:                               # Mix inverted and non-inverted
      print "I was never here"
      nots=cv2.inRange(image, self.neg_test[L], self.neg_test[U])
      yeses=cv2.inRange(image, self.pos_test[L], self.os_test[U])
      nots=cv2.bitwise_not(nots)
      mask = cv2.bitwise_and(nots,yeses)
    im=cv2.bitwise_and(image,image,mask=mask)
    print im.shape
    return self.cv_2_pil(im).copy()

  def cv_2_pil(self,image):
    # Converts cv-image to PIL.
    return Image.fromarray(image)

  def pil_2_cv(self,image):
    # Converts PIL-image to numpy array (for cv). Still RGB.
    return numpy.array(image)

  def _check_cs_conversion(self,from_cs,to_cs):
    """Checks if conversion between from/to is possible.

    Args:
      from_cs: originating colorspace abbreviation, capitalized
      to_cs: target (threshold) colorspace abbreviation, capitalized

    Returns:
      cv2 constant identifying conversion, or None if not found.
    """
    if "COLOR_%s2%s" % (from_cs,to_cs) in dir(cv2):
      return eval("cv2.COLOR_%s2%s" % (from_cs,to_cs))

Thresholding + masking image erratic results

Hi! I'm trying to threshold + mask this image:

Orgiginal image

However, using the code below this produces erratic and random images: Erratic image #1 Erratic image #2 Erratic image #3

I'm guessing it has something to do with the binary header imprinted in beginning of the produced images, however, I'm not where they are coming from. Also, considering that the images change randomly, the header is to some extent unique for each image.image (EDIT: To clarify: using same code and input image, the output is not consistent for each conversion).

The input image is an RGB (no alpha) and the thresholds are all positive.

Any insights are most welcome!

(As (EDIT: As per comments, the full code may be a bit verbose - so now listing relevant pieces first, full code below:below:)

def threshold_image(self,image):
  # image = PIL RGB image
  assert(image.size == (258,258))
  assert(len(image.getbands()) == 3)

  image=numpy.array(image) # Still RGB, not BGR.

  assert(image.shape == (258,258,3))

  mask = cv2.inRange(image, numpy.array([0, 0, 0], dtype=numpy.uint8), 
                      numpy.array([200, 250, 251], dtype=numpy.uint8))

  im=cv2.bitwise_and(image,image,mask=mask)
  assert(image.shape == (258,258,3))

  return Image.fromarray(im)

Full code:

from PIL import Image, ImageMath
import cv2, numpy, math, os, copy

A=0
B=1
C=2
MIN=0
MAX=1
U=0
L=1


class Thresholder():
  """Class for performing thresholding and/or conversions."""
  # _thresholds=[]
  # bounds=[[],[]]
  # conversion_factor = None
  # neg_test = False
  # pos_test = False

  def __init__(self,thresholds,from_cs="rgb",to_cs="rgb"):
    """Instantiates a new thresholding engine.

    Args:
      thresholds: 2d array of the form
        [   [cs_val_min_a,  cs_val_max_a],
          [cs_val_min_b,  cs_val_max_b],
          [cs_val_min_c,  cs_val_max_c]   ]

      ...where a,b and c are the channels of 
        the target (threshold) colorspace.

      Negative value in either max or min of a channel
        will trigger a NOT threshold (i.e. inverted).


      from_colorspace: Originating test set colorspace.
      to_colorspace: Colorspace of threshold values.

    Returns:
      A new Thresholding object.
    """

    self._thresholds=thresholds
    from_cs=from_cs.upper()
    to_cs=to_cs.upper()
    self.conversion_factor = self._check_cs_conversion(from_cs,to_cs)
    upper=(thresholds[A][MAX],thresholds[B][MAX],thresholds[C][MAX])
    lower=(thresholds[A][MIN],thresholds[B][MIN],thresholds[C][MIN])
    self.bounds=(upper,lower)
    upper = []
    lower = []
    self.inverts=[]

    for i in range(3):
      u=self.bounds[U][i]
      l=self.bounds[L][i]
      if l < 0 or u < 0:
        l=abs(l)
        u=abs(u)
        self.inverts.append(True)
      else:
        self.inverts.append(False)
      upper.append(u)
      lower.append(l)
    upper=numpy.array(upper,dtype=numpy.uint8)
    lower=numpy.array(lower,dtype=numpy.uint8)
    self.bounds=[upper,lower]

    if (True not in self.inverts or False not in self.inverts):
      self.pos_test = copy.deepcopy(self.bounds)
      self.neg_test = copy.deepcopy(self.bounds)
      for i,b in enumerate(self.inverts):
        if self.inverts[i] is True:
          self.pos_test[U][i]=255
          self.pos_test[L][i]=0
        else:
          self.neg_test[U][i]=255
          self.neg_test[L][i]=0


  def threshold_image(self,image):
    """Performs thresholding on a whole image.

    Args:
      image: One of either formats below:
          PIL (as RGB)
          (numpy array) todo
          (cv-image) todo

    Returns:
      The thresholded image as PIL (RGB) image,
      with positive bits set to 255 and false to 0.
    """

    image=self.pil_2_cv(image) # Still RGB, not BGR.

    if self.conversion_factor is not None:
      print "I was never here"
      image = cv2.cvtColor(image,self.conversion_factor)
      print "Using %s" % self.conversion_factor

    if True not in self.inverts:        # No inverted tests
      mask = cv2.inRange(image, self.bounds[L], self.bounds[U])
    elif False not in self.inverts:     # All inverted tests
      print "I was never here"
      negMask=cv2.inRange(image, self.bounds[L], self.bounds[U])
      mask = cv2.bitwise_not(negMask)
    else:                               # Mix inverted and non-inverted
      print "I was never here"
      nots=cv2.inRange(image, self.neg_test[L], self.neg_test[U])
      yeses=cv2.inRange(image, self.pos_test[L], self.os_test[U])
      nots=cv2.bitwise_not(nots)
      mask = cv2.bitwise_and(nots,yeses)
    im=cv2.bitwise_and(image,image,mask=mask)
    print im.shape
    return self.cv_2_pil(im).copy()

  def cv_2_pil(self,image):
    # Converts cv-image to PIL.
    return Image.fromarray(image)

  def pil_2_cv(self,image):
    # Converts PIL-image to numpy array (for cv). Still RGB.
    return numpy.array(image)

  def _check_cs_conversion(self,from_cs,to_cs):
    """Checks if conversion between from/to is possible.

    Args:
      from_cs: originating colorspace abbreviation, capitalized
      to_cs: target (threshold) colorspace abbreviation, capitalized

    Returns:
      cv2 constant identifying conversion, or None if not found.
    """
    if "COLOR_%s2%s" % (from_cs,to_cs) in dir(cv2):
      return eval("cv2.COLOR_%s2%s" % (from_cs,to_cs))
click to hide/show revision 7
Updated with binary tests

Thresholding + masking image erratic results

Hi! I'm trying to threshold + mask this image:

Orgiginal image

However, using the code below this produces erratic and random images: Erratic image #1 Erratic image #2 Erratic image #3

I'm guessing it has something to do with the binary header imprinted in beginning of the produced images, however, I'm not where they are coming from. Also, considering that the images change randomly, the header is to some extent unique for each image (EDIT: To clarify: using same code and input image, the output is not consistent for each conversion).

<UPDATE>: This is the part of the code which errs:

mask:
[255 255   0   0]
image:
[200 150 100  50]
result of cv2.bitwise_and(b,b,mask=a):
[[200]
 [150]
 [145]
 [  1]]

Now I'd assume the operation for e.g. element at index 1 (150) is evaluated as follows:

Image       ∧       Image       =   Image∧Image
150         ∧       150         =   150
0b10010110  ∧       0b10010110  =   0b10010110

⇒Image∧Image=Image

Image∧Image ∧       Mask        =   Image∧Image∧Mask
150         ∧       255         =   150
0b10010110  ∧       0b11111111  =   0b10010110

⇒Image∧Image∧Mask=Image∧Mask

And that holds true. However, this is what I get for element at index 2 (100), at different function calls:

Image       ∧       Mask        =   Image∧Mask
100         ∧       0           =   184
0b10010110  ∧       0b11111111  =   0b10111000

and

Image       ∧       Mask        =   Image∧Mask
100         ∧       0           =   153
0b10010110  ∧       0b11111111  =   0b10011001

and

Image       ∧       Mask        =   Image∧Mask
100         ∧       0           =   49
0b10010110  ∧       0b11111111  =   0b00110001

What am I missing here?

</UPDATE>

The input image is an RGB (no alpha) and the thresholds are all positive.

Any insights are most welcome!

(EDIT: As per comments, the full code may be a bit verbose - so now listing relevant pieces first, full code below:)

def threshold_image(self,image):
  # image = PIL RGB image
  assert(image.size == (258,258))
  assert(len(image.getbands()) == 3)

  image=numpy.array(image) # Still RGB, not BGR.

  assert(image.shape == (258,258,3))

  mask = cv2.inRange(image, numpy.array([0, 0, 0], dtype=numpy.uint8), 
                      numpy.array([200, 250, 251], dtype=numpy.uint8))

  im=cv2.bitwise_and(image,image,mask=mask)
  assert(image.shape == (258,258,3))

  return Image.fromarray(im)

Full code:

from PIL import Image, ImageMath
import cv2, numpy, math, os, copy

A=0
B=1
C=2
MIN=0
MAX=1
U=0
L=1


class Thresholder():
  """Class for performing thresholding and/or conversions."""
  # _thresholds=[]
  # bounds=[[],[]]
  # conversion_factor = None
  # neg_test = False
  # pos_test = False

  def __init__(self,thresholds,from_cs="rgb",to_cs="rgb"):
    """Instantiates a new thresholding engine.

    Args:
      thresholds: 2d array of the form
        [   [cs_val_min_a,  cs_val_max_a],
          [cs_val_min_b,  cs_val_max_b],
          [cs_val_min_c,  cs_val_max_c]   ]

      ...where a,b and c are the channels of 
        the target (threshold) colorspace.

      Negative value in either max or min of a channel
        will trigger a NOT threshold (i.e. inverted).


      from_colorspace: Originating test set colorspace.
      to_colorspace: Colorspace of threshold values.

    Returns:
      A new Thresholding object.
    """

    self._thresholds=thresholds
    from_cs=from_cs.upper()
    to_cs=to_cs.upper()
    self.conversion_factor = self._check_cs_conversion(from_cs,to_cs)
    upper=(thresholds[A][MAX],thresholds[B][MAX],thresholds[C][MAX])
    lower=(thresholds[A][MIN],thresholds[B][MIN],thresholds[C][MIN])
    self.bounds=(upper,lower)
    upper = []
    lower = []
    self.inverts=[]

    for i in range(3):
      u=self.bounds[U][i]
      l=self.bounds[L][i]
      if l < 0 or u < 0:
        l=abs(l)
        u=abs(u)
        self.inverts.append(True)
      else:
        self.inverts.append(False)
      upper.append(u)
      lower.append(l)
    upper=numpy.array(upper,dtype=numpy.uint8)
    lower=numpy.array(lower,dtype=numpy.uint8)
    self.bounds=[upper,lower]

    if (True not in self.inverts or False not in self.inverts):
      self.pos_test = copy.deepcopy(self.bounds)
      self.neg_test = copy.deepcopy(self.bounds)
      for i,b in enumerate(self.inverts):
        if self.inverts[i] is True:
          self.pos_test[U][i]=255
          self.pos_test[L][i]=0
        else:
          self.neg_test[U][i]=255
          self.neg_test[L][i]=0


  def threshold_image(self,image):
    """Performs thresholding on a whole image.

    Args:
      image: One of either formats below:
          PIL (as RGB)
          (numpy array) todo
          (cv-image) todo

    Returns:
      The thresholded image as PIL (RGB) image,
      with positive bits set to 255 and false to 0.
    """

    image=self.pil_2_cv(image) # Still RGB, not BGR.

    if self.conversion_factor is not None:
      print "I was never here"
      image = cv2.cvtColor(image,self.conversion_factor)
      print "Using %s" % self.conversion_factor

    if True not in self.inverts:        # No inverted tests
      mask = cv2.inRange(image, self.bounds[L], self.bounds[U])
    elif False not in self.inverts:     # All inverted tests
      print "I was never here"
      negMask=cv2.inRange(image, self.bounds[L], self.bounds[U])
      mask = cv2.bitwise_not(negMask)
    else:                               # Mix inverted and non-inverted
      print "I was never here"
      nots=cv2.inRange(image, self.neg_test[L], self.neg_test[U])
      yeses=cv2.inRange(image, self.pos_test[L], self.os_test[U])
      nots=cv2.bitwise_not(nots)
      mask = cv2.bitwise_and(nots,yeses)
    im=cv2.bitwise_and(image,image,mask=mask)
    print im.shape
    return self.cv_2_pil(im).copy()

  def cv_2_pil(self,image):
    # Converts cv-image to PIL.
    return Image.fromarray(image)

  def pil_2_cv(self,image):
    # Converts PIL-image to numpy array (for cv). Still RGB.
    return numpy.array(image)

  def _check_cs_conversion(self,from_cs,to_cs):
    """Checks if conversion between from/to is possible.

    Args:
      from_cs: originating colorspace abbreviation, capitalized
      to_cs: target (threshold) colorspace abbreviation, capitalized

    Returns:
      cv2 constant identifying conversion, or None if not found.
    """
    if "COLOR_%s2%s" % (from_cs,to_cs) in dir(cv2):
      return eval("cv2.COLOR_%s2%s" % (from_cs,to_cs))

Thresholding + masking image erratic results

Hi! I'm trying to threshold + mask this image:

Orgiginal image

However, using the code below this produces erratic and random images: Erratic image #1 Erratic image #2 Erratic image #3

I'm guessing it has something to do with the binary header imprinted in beginning of the produced images, however, I'm not where they are coming from. Also, considering that the images change randomly, the header is to some extent unique for each image (EDIT: To clarify: using same code and input image, the output is not consistent for each conversion).

<UPDATE>: This is the part of the code which errs:

mask:
[255 255   0   0]
image:
[200 150 100  50]
result of cv2.bitwise_and(b,b,mask=a):
[[200]
 [150]
 [145]
 [  1]]

Now I'd assume the operation for e.g. element at index 1 (150) is evaluated as follows:

Image       ∧       Image       =   Image∧Image
150         ∧       150         =   150
0b10010110  ∧       0b10010110  =   0b10010110

⇒Image∧Image=Image

Image∧Image ∧       Mask        =   Image∧Image∧Mask
150         ∧       255         =   150
0b10010110  ∧       0b11111111  =   0b10010110

⇒Image∧Image∧Mask=Image∧Mask

And that holds true. However, this is what I get for element at index 2 (100), at different (but with identical parameters) function calls:

Image       ∧       Mask        =   Image∧Mask
100         ∧       0           =   184
0b10010110  ∧       0b11111111  =   0b10111000

and

Image       ∧       Mask        =   Image∧Mask
100         ∧       0           =   153
0b10010110  ∧       0b11111111  =   0b10011001

and

Image       ∧       Mask        =   Image∧Mask
100         ∧       0           =   49
0b10010110  ∧       0b11111111  =   0b00110001

What am I missing here?

</UPDATE>

The input image is an RGB (no alpha) and the thresholds are all positive.

Any insights are most welcome!

(EDIT: As per comments, the full code may be a bit verbose - so now listing relevant pieces first, full code below:)

def threshold_image(self,image):
  # image = PIL RGB image
  assert(image.size == (258,258))
  assert(len(image.getbands()) == 3)

  image=numpy.array(image) # Still RGB, not BGR.

  assert(image.shape == (258,258,3))

  mask = cv2.inRange(image, numpy.array([0, 0, 0], dtype=numpy.uint8), 
                      numpy.array([200, 250, 251], dtype=numpy.uint8))

  im=cv2.bitwise_and(image,image,mask=mask)
  assert(image.shape == (258,258,3))

  return Image.fromarray(im)

Full code:

from PIL import Image, ImageMath
import cv2, numpy, math, os, copy

A=0
B=1
C=2
MIN=0
MAX=1
U=0
L=1


class Thresholder():
  """Class for performing thresholding and/or conversions."""
  # _thresholds=[]
  # bounds=[[],[]]
  # conversion_factor = None
  # neg_test = False
  # pos_test = False

  def __init__(self,thresholds,from_cs="rgb",to_cs="rgb"):
    """Instantiates a new thresholding engine.

    Args:
      thresholds: 2d array of the form
        [   [cs_val_min_a,  cs_val_max_a],
          [cs_val_min_b,  cs_val_max_b],
          [cs_val_min_c,  cs_val_max_c]   ]

      ...where a,b and c are the channels of 
        the target (threshold) colorspace.

      Negative value in either max or min of a channel
        will trigger a NOT threshold (i.e. inverted).


      from_colorspace: Originating test set colorspace.
      to_colorspace: Colorspace of threshold values.

    Returns:
      A new Thresholding object.
    """

    self._thresholds=thresholds
    from_cs=from_cs.upper()
    to_cs=to_cs.upper()
    self.conversion_factor = self._check_cs_conversion(from_cs,to_cs)
    upper=(thresholds[A][MAX],thresholds[B][MAX],thresholds[C][MAX])
    lower=(thresholds[A][MIN],thresholds[B][MIN],thresholds[C][MIN])
    self.bounds=(upper,lower)
    upper = []
    lower = []
    self.inverts=[]

    for i in range(3):
      u=self.bounds[U][i]
      l=self.bounds[L][i]
      if l < 0 or u < 0:
        l=abs(l)
        u=abs(u)
        self.inverts.append(True)
      else:
        self.inverts.append(False)
      upper.append(u)
      lower.append(l)
    upper=numpy.array(upper,dtype=numpy.uint8)
    lower=numpy.array(lower,dtype=numpy.uint8)
    self.bounds=[upper,lower]

    if (True not in self.inverts or False not in self.inverts):
      self.pos_test = copy.deepcopy(self.bounds)
      self.neg_test = copy.deepcopy(self.bounds)
      for i,b in enumerate(self.inverts):
        if self.inverts[i] is True:
          self.pos_test[U][i]=255
          self.pos_test[L][i]=0
        else:
          self.neg_test[U][i]=255
          self.neg_test[L][i]=0


  def threshold_image(self,image):
    """Performs thresholding on a whole image.

    Args:
      image: One of either formats below:
          PIL (as RGB)
          (numpy array) todo
          (cv-image) todo

    Returns:
      The thresholded image as PIL (RGB) image,
      with positive bits set to 255 and false to 0.
    """

    image=self.pil_2_cv(image) # Still RGB, not BGR.

    if self.conversion_factor is not None:
      print "I was never here"
      image = cv2.cvtColor(image,self.conversion_factor)
      print "Using %s" % self.conversion_factor

    if True not in self.inverts:        # No inverted tests
      mask = cv2.inRange(image, self.bounds[L], self.bounds[U])
    elif False not in self.inverts:     # All inverted tests
      print "I was never here"
      negMask=cv2.inRange(image, self.bounds[L], self.bounds[U])
      mask = cv2.bitwise_not(negMask)
    else:                               # Mix inverted and non-inverted
      print "I was never here"
      nots=cv2.inRange(image, self.neg_test[L], self.neg_test[U])
      yeses=cv2.inRange(image, self.pos_test[L], self.os_test[U])
      nots=cv2.bitwise_not(nots)
      mask = cv2.bitwise_and(nots,yeses)
    im=cv2.bitwise_and(image,image,mask=mask)
    print im.shape
    return self.cv_2_pil(im).copy()

  def cv_2_pil(self,image):
    # Converts cv-image to PIL.
    return Image.fromarray(image)

  def pil_2_cv(self,image):
    # Converts PIL-image to numpy array (for cv). Still RGB.
    return numpy.array(image)

  def _check_cs_conversion(self,from_cs,to_cs):
    """Checks if conversion between from/to is possible.

    Args:
      from_cs: originating colorspace abbreviation, capitalized
      to_cs: target (threshold) colorspace abbreviation, capitalized

    Returns:
      cv2 constant identifying conversion, or None if not found.
    """
    if "COLOR_%s2%s" % (from_cs,to_cs) in dir(cv2):
      return eval("cv2.COLOR_%s2%s" % (from_cs,to_cs))

Thresholding + masking image erratic results

Hi! I'm trying to threshold + mask this image:

Orgiginal image

However, using the code below this produces erratic and random images: Erratic image #1 Erratic image #2 Erratic image #3

I'm guessing it has something to do with the binary header imprinted in beginning of the produced images, however, I'm not where they are coming from. Also, considering that the images change randomly, the header is to some extent unique for each image (EDIT: To clarify: using same code and input image, the output is not consistent for each conversion).

<UPDATE>: This is the part of the code which errs:

mask:
[255 255   0   0]
image:
[200 150 100  50]
result of cv2.bitwise_and(b,b,mask=a):
[[200]
 [150]
 [145]
 [  1]]

Now I'd assume the operation for e.g. element at index 1 (150) is evaluated as follows:

Image       ∧       Image       =   Image∧Image
150         ∧       150         =   150
0b10010110  ∧       0b10010110  =   0b10010110

⇒Image∧Image=Image

Image∧Image ∧       Mask        =   Image∧Image∧Mask
150         ∧       255         =   150
0b10010110  ∧       0b11111111  =   0b10010110

⇒Image∧Image∧Mask=Image∧Mask

And that holds true. However, this is what I get for element at index 2 (100), at different (but with identical parameters) function calls:

Image       ∧       Mask        =   Image∧Mask
100         ∧       0           =   184
0b10010110  ∧       0b11111111  =   0b10111000

and

Image       ∧       Mask        =   Image∧Mask
100         ∧       0           =   153
0b10010110  ∧       0b11111111  =   0b10011001

and

Image       ∧       Mask        =   Image∧Mask
100         ∧       0           =   49
0b10010110  ∧       0b11111111  =   0b00110001

What am I missing here?

</UPDATE>

The input image is an RGB (no alpha) and the thresholds are all positive.

Any insights are most welcome!

(EDIT: As per comments, the full code may be a bit verbose - so now listing relevant pieces first, full code below:)

def threshold_image(self,image):
  # image = PIL RGB image
  assert(image.size == (258,258))
  assert(len(image.getbands()) == 3)

  image=numpy.array(image) # Still RGB, not BGR.

  assert(image.shape == (258,258,3))

  mask = cv2.inRange(image, numpy.array([0, 0, 0], dtype=numpy.uint8), 
                      numpy.array([200, 250, 251], dtype=numpy.uint8))

  im=cv2.bitwise_and(image,image,mask=mask)
  assert(image.shape == (258,258,3))

  return Image.fromarray(im)

Full code:

from PIL import Image, ImageMath
import cv2, numpy, math, os, copy

A=0
B=1
C=2
MIN=0
MAX=1
U=0
L=1


class Thresholder():
  """Class for performing thresholding and/or conversions."""
  # _thresholds=[]
  # bounds=[[],[]]
  # conversion_factor = None
  # neg_test = False
  # pos_test = False

  def __init__(self,thresholds,from_cs="rgb",to_cs="rgb"):
    """Instantiates a new thresholding engine.

    Args:
      thresholds: 2d array of the form
        [   [cs_val_min_a,  cs_val_max_a],
          [cs_val_min_b,  cs_val_max_b],
          [cs_val_min_c,  cs_val_max_c]   ]

      ...where a,b and c are the channels of 
        the target (threshold) colorspace.

      Negative value in either max or min of a channel
        will trigger a NOT threshold (i.e. inverted).


      from_colorspace: Originating test set colorspace.
      to_colorspace: Colorspace of threshold values.

    Returns:
      A new Thresholding object.
    """

    self._thresholds=thresholds
    from_cs=from_cs.upper()
    to_cs=to_cs.upper()
    self.conversion_factor = self._check_cs_conversion(from_cs,to_cs)
    upper=(thresholds[A][MAX],thresholds[B][MAX],thresholds[C][MAX])
    lower=(thresholds[A][MIN],thresholds[B][MIN],thresholds[C][MIN])
    self.bounds=(upper,lower)
    upper = []
    lower = []
    self.inverts=[]

    for i in range(3):
      u=self.bounds[U][i]
      l=self.bounds[L][i]
      if l < 0 or u < 0:
        l=abs(l)
        u=abs(u)
        self.inverts.append(True)
      else:
        self.inverts.append(False)
      upper.append(u)
      lower.append(l)
    upper=numpy.array(upper,dtype=numpy.uint8)
    lower=numpy.array(lower,dtype=numpy.uint8)
    self.bounds=[upper,lower]

    if (True not in self.inverts or False not in self.inverts):
      self.pos_test = copy.deepcopy(self.bounds)
      self.neg_test = copy.deepcopy(self.bounds)
      for i,b in enumerate(self.inverts):
        if self.inverts[i] is True:
          self.pos_test[U][i]=255
          self.pos_test[L][i]=0
        else:
          self.neg_test[U][i]=255
          self.neg_test[L][i]=0


  def threshold_image(self,image):
    """Performs thresholding on a whole image.

    Args:
      image: One of either formats below:
          PIL (as RGB)
          (numpy array) todo
          (cv-image) todo

    Returns:
      The thresholded image as PIL (RGB) image,
      with positive bits set to 255 and false to 0.
    """

    image=self.pil_2_cv(image) # Still RGB, not BGR.

    if self.conversion_factor is not None:
      print "I was never here"
      image = cv2.cvtColor(image,self.conversion_factor)
      print "Using %s" % self.conversion_factor

    if True not in self.inverts:        # No inverted tests
      mask = cv2.inRange(image, self.bounds[L], self.bounds[U])
    elif False not in self.inverts:     # All inverted tests
      print "I was never here"
      negMask=cv2.inRange(image, self.bounds[L], self.bounds[U])
      mask = cv2.bitwise_not(negMask)
    else:                               # Mix inverted and non-inverted
      print "I was never here"
      nots=cv2.inRange(image, self.neg_test[L], self.neg_test[U])
      yeses=cv2.inRange(image, self.pos_test[L], self.os_test[U])
      nots=cv2.bitwise_not(nots)
      mask = cv2.bitwise_and(nots,yeses)
    im=cv2.bitwise_and(image,image,mask=mask)
    print im.shape
    return self.cv_2_pil(im).copy()

  def cv_2_pil(self,image):
    # Converts cv-image to PIL.
    return Image.fromarray(image)

  def pil_2_cv(self,image):
    # Converts PIL-image to numpy array (for cv). Still RGB.
    return numpy.array(image)

  def _check_cs_conversion(self,from_cs,to_cs):
    """Checks if conversion between from/to is possible.

    Args:
      from_cs: originating colorspace abbreviation, capitalized
      to_cs: target (threshold) colorspace abbreviation, capitalized

    Returns:
      cv2 constant identifying conversion, or None if not found.
    """
    if "COLOR_%s2%s" % (from_cs,to_cs) in dir(cv2):
      return eval("cv2.COLOR_%s2%s" % (from_cs,to_cs))

Thresholding + masking image erratic results

Hi! I'm trying to threshold + mask this image:

Orgiginal image

However, using the code below this produces erratic and random images: Erratic image #1 Erratic image #2 Erratic image #3

I'm guessing it has something to do with the binary header imprinted in beginning of the produced images, however, I'm not where they are coming from. Also, considering that the images change randomly, the header is to some extent unique for each image (EDIT: To clarify: using same code and input image, the output is not consistent for each conversion).

<UPDATE>: This is the part of the code which errs:

mask:
[255 255   0   0]
image:
[200 150 100  50]
result of cv2.bitwise_and(b,b,mask=a):
[[200]
 [150]
 [145]
 [  1]]

Now I'd assume the operation for e.g. element at index 1 (150) is evaluated as follows:

Image       ∧       Image       =   Image∧Image
150         ∧       150         =   150
0b10010110  ∧       0b10010110  =   0b10010110

⇒Image∧Image=Image

Image∧Image ∧       Mask        =   Image∧Image∧Mask
150         ∧       255         =   150
0b10010110  ∧       0b11111111  =   0b10010110

⇒Image∧Image∧Mask=Image∧Mask

And that holds true. However, this is what I get for element at index 2 (100), at different (but with identical parameters) function calls:

Image       ∧       Mask        =   Image∧Mask
100         ∧       0           =   184
0b10010110  ∧       0b11111111  =   0b10111000

and

Image       ∧       Mask        =   Image∧Mask
100         ∧       0           =   153
0b10010110  ∧       0b11111111  =   0b10011001

and

Image       ∧       Mask        =   Image∧Mask
100         ∧       0           =   49
0b10010110  ∧       0b11111111  =   0b00110001

What am I missing here?

</UPDATE>

The input image is an RGB (no alpha) and the thresholds are all positive.

Any insights are most welcome!

(EDIT: As per comments, the full code may be a bit verbose - so now listing relevant pieces first, full code below:)

def threshold_image(self,image):
  # image = PIL RGB image
  assert(image.size == (258,258))
  assert(len(image.getbands()) == 3)

  image=numpy.array(image) # Still RGB, not BGR.

  assert(image.shape == (258,258,3))

  mask = cv2.inRange(image, numpy.array([0, 0, 0], dtype=numpy.uint8), 
                      numpy.array([200, 250, 251], dtype=numpy.uint8))

  im=cv2.bitwise_and(image,image,mask=mask)
  assert(image.shape == (258,258,3))

  return Image.fromarray(im)

Full code:

from PIL import Image, ImageMath
import cv2, numpy, math, os, copy

A=0
B=1
C=2
MIN=0
MAX=1
U=0
L=1


class Thresholder():
  """Class for performing thresholding and/or conversions."""
  # _thresholds=[]
  # bounds=[[],[]]
  # conversion_factor = None
  # neg_test = False
  # pos_test = False

  def __init__(self,thresholds,from_cs="rgb",to_cs="rgb"):
    """Instantiates a new thresholding engine.

    Args:
      thresholds: 2d array of the form
        [   [cs_val_min_a,  cs_val_max_a],
          [cs_val_min_b,  cs_val_max_b],
          [cs_val_min_c,  cs_val_max_c]   ]

      ...where a,b and c are the channels of 
        the target (threshold) colorspace.

      Negative value in either max or min of a channel
        will trigger a NOT threshold (i.e. inverted).


      from_colorspace: Originating test set colorspace.
      to_colorspace: Colorspace of threshold values.

    Returns:
      A new Thresholding object.
    """

    self._thresholds=thresholds
    from_cs=from_cs.upper()
    to_cs=to_cs.upper()
    self.conversion_factor = self._check_cs_conversion(from_cs,to_cs)
    upper=(thresholds[A][MAX],thresholds[B][MAX],thresholds[C][MAX])
    lower=(thresholds[A][MIN],thresholds[B][MIN],thresholds[C][MIN])
    self.bounds=(upper,lower)
    upper = []
    lower = []
    self.inverts=[]

    for i in range(3):
      u=self.bounds[U][i]
      l=self.bounds[L][i]
      if l < 0 or u < 0:
        l=abs(l)
        u=abs(u)
        self.inverts.append(True)
      else:
        self.inverts.append(False)
      upper.append(u)
      lower.append(l)
    upper=numpy.array(upper,dtype=numpy.uint8)
    lower=numpy.array(lower,dtype=numpy.uint8)
    self.bounds=[upper,lower]

    if (True not in self.inverts or False not in self.inverts):
      self.pos_test = copy.deepcopy(self.bounds)
      self.neg_test = copy.deepcopy(self.bounds)
      for i,b in enumerate(self.inverts):
        if self.inverts[i] is True:
          self.pos_test[U][i]=255
          self.pos_test[L][i]=0
        else:
          self.neg_test[U][i]=255
          self.neg_test[L][i]=0


  def threshold_image(self,image):
    """Performs thresholding on a whole image.

    Args:
      image: One of either formats below:
          PIL (as RGB)
          (numpy array) todo
          (cv-image) todo

    Returns:
      The thresholded image as PIL (RGB) image,
      with positive bits set to 255 and false to 0.
    """

    image=self.pil_2_cv(image) # Still RGB, not BGR.

    if self.conversion_factor is not None:
      print "I was never here"
      image = cv2.cvtColor(image,self.conversion_factor)
      print "Using %s" % self.conversion_factor

    if True not in self.inverts:        # No inverted tests
      mask = cv2.inRange(image, self.bounds[L], self.bounds[U])
    elif False not in self.inverts:     # All inverted tests
      print "I was never here"
      negMask=cv2.inRange(image, self.bounds[L], self.bounds[U])
      mask = cv2.bitwise_not(negMask)
    else:                               # Mix inverted and non-inverted
      print "I was never here"
      nots=cv2.inRange(image, self.neg_test[L], self.neg_test[U])
      yeses=cv2.inRange(image, self.pos_test[L], self.os_test[U])
      nots=cv2.bitwise_not(nots)
      mask = cv2.bitwise_and(nots,yeses)
    im=cv2.bitwise_and(image,image,mask=mask)
    print im.shape
    return self.cv_2_pil(im).copy()

  def cv_2_pil(self,image):
    # Converts cv-image to PIL.
    return Image.fromarray(image)

  def pil_2_cv(self,image):
    # Converts PIL-image to numpy array (for cv). Still RGB.
    return numpy.array(image)

  def _check_cs_conversion(self,from_cs,to_cs):
    """Checks if conversion between from/to is possible.

    Args:
      from_cs: originating colorspace abbreviation, capitalized
      to_cs: target (threshold) colorspace abbreviation, capitalized

    Returns:
      cv2 constant identifying conversion, or None if not found.
    """
    if "COLOR_%s2%s" % (from_cs,to_cs) in dir(cv2):
      return eval("cv2.COLOR_%s2%s" % (from_cs,to_cs))