0

I have performed some segmentation task and enumerated the extracted blobs using connectedComponents(). This results in a single channel image with 32bit depth (CV_32S) containing the labels of the extracted objects.

# Result of my segmentation process:
# A binary mask of extracted objects.
mask = ... 

# Create ids of connected components.
n_components, component_ids = cv.connectedComponents(mask)

# A lookup to map component ids to some (numeric) category ids.
# It has the form of { component_id: category }
lookup = ...

Note that in addition to the mask of extracted objects, a lookup that maps components to some (problem specific) categories is available.

The question: How to apply lookup to component_ids? Intuitively, I would have used OpenCV's LUT(). However, this function is limited to 256 values. In my case, n_components can be larger than 256, which is why I think LUT() is not the right method to use here. Any suggestions on how to proceed?

normanius
  • 8,629
  • 7
  • 53
  • 83

1 Answers1

2

You may apply a LUT using NumPy as described here (instead of using OpenCV).

The syntax is simply:

dst = lut[src]

In NumPy there is no range limitation as in OpenCV.


Example:
Using the image from the following post.

import numpy as np
import cv2

def apply_lut(src, lut):
    # dst(I) <-- lut(src)
    # https://stackoverflow.com/questions/14448763/is-there-a-convenient-way-to-apply-a-lookup-table-to-a-large-array-in-numpy
    dst = lut[src]
    return dst


img = cv2.imread('eGaIy.jpg', cv2.IMREAD_GRAYSCALE)  # Read sample image

_, mask = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

n_components, component_ids = cv2.connectedComponents(mask)

# Initialize look up tables with arbitrary data for demonstration
LUT1 = np.linspace(0, 255, n_components).astype(np.uint16)
LUT2 = np.linspace(255, 0, n_components).astype(np.uint16)
LUT3 = np.linspace(50, 150, n_components).astype(np.uint16)
LUT2[0] = 0

r = apply_lut(component_ids, LUT1)
g = apply_lut(component_ids, LUT2)
b = apply_lut(component_ids, LUT3)

bgr = np.dstack((b, g, r)).astype(np.uint8)

cv2.imshow('bgr', bgr)
cv2.waitKey()
cv2.destroyAllWindows()

Result:
enter image description here

Rotem
  • 30,366
  • 4
  • 32
  • 65
  • Using NumPy is very elegant indeed, thanks. But is it fast? If there was a suitable OpenCV function, would you prefer that one over the NumPy solution? Is there any general rule of thumb? – normanius May 30 '21 at 22:03
  • 1
    The rule of thumb is that OpenCV is faster than NumPy in most cases. OpenCV is optimized for very specific cases - `uint8` LUT for example, while NumPy has a more generalized implementation (and also applies validity checks). When I need some code to be super optimized (for x86 CPU), I am using C implementation with SIMD intrinsics (like AVX2) and OpenMP for parallelizing (not OpenCV), but it's a lot of work. – Rotem May 30 '21 at 22:42
  • Thanks, that makes sense. Also, OpenCV uses IPP if available on a system, which NumPy does not AFAIK. And as for the rest - yes, optimizing is an art in itself. – normanius May 30 '21 at 22:50
  • 1
    To illustrate the difference in performance, I performed a small test (unrelated to my OP above): For an 8bit grayscale image with 5000x7500 pixels and a randomly initialized 8-bit LUT (with 256 values), I applied the LUT using OpenCV (version: 4.5, command: `LUT(img, lut)`) and NumPy (version: 1.20, command: `lut[img]`) and compared the performance using [`timeit`](https://docs.python.org/3/library/timeit.html). **Results**: OpenCV 45.2 ms ± 1.37 ms, NumPy 186 ms ± 19.8 ms. System: MacBook Pro (late 2015), Python 3.8. – normanius May 30 '21 at 23:19