MarcoM
MarcoM

Reputation: 1204

How to extract an intensity profile along a line?

Is there an out-of-the-box funcion in OpenCV to extract an intensity profile from an image along a line, something like the improfile function in MATLAB?

Upvotes: 2

Views: 10039

Answers (2)

HansHirse
HansHirse

Reputation: 18895

Here comes a small, interactive Python application to mimic MATLAB's improfile.

An image is loaded, and shown in an OpenCV window. Mouse button down and up events are recorded to get the line's start and end. The line (white) is shown in the image, and the corresponding RGB intensity profiles are shown in an addition Matplotlib window. The infinite loop can be exited by pressing the c key within the OpenCV window.

Here's how it looks:

Example 1

Example 2

And, here comes the code:

import cv2
from matplotlib import pyplot as plt
import numpy as np
from skimage import draw

# Actual mouse callback function
def print_coords(event, x, y, flags, param):

    # Global variables needed
    global image, image_copy, r_start, c_start

    # If left mouse button is clicked, start of line
    if (event == cv2.EVENT_LBUTTONDOWN):
        r_start = x
        c_start = y

    # If left mouse button is clicked, end of line; plot intensity profile
    if (event == cv2.EVENT_LBUTTONUP):
        r_end = x
        c_end = y
        image = cv2.line(image_copy.copy(), (r_start, c_start), (r_end, c_end), (255, 255, 255), 2)
        line = np.transpose(np.array(draw.line(r_start, c_start, r_end, c_end)))
        data = image_copy.copy()[line[:, 1], line[:, 0], :]
        plt.close()
        plt.figure('Intensity profile')
        plt.plot(data[:, 0], 'b', data[:, 1], 'g', data[:, 2], 'r')
        plt.draw()
        plt.pause(0.001)
        plt.legend(['Blue', 'Green', 'Red'])
        plt.ylim((0, 255))

# Read an image
image = cv2.imread('path/to/your/image.png', cv2.IMREAD_COLOR)
image_copy = image.copy()

# Set up window and mouse callback function
cv2.namedWindow("image")
cv2.setMouseCallback("image", print_coords)

# Loop until the 'c' key is pressed
while True:

    # Display image; wait for keypress
    cv2.imshow("image", image)
    key = cv2.waitKey(1) & 0xFF

    # If 'c' key is pressed, break from loop
    if  key == ord("c"):
        break

cv2.destroyAllWindows()

To get the coordinates of the line, I use the line function from scikit-image. That seems to be fastest Pythonic way.

Hope that helps the Python people looking for such a functionality!

Upvotes: 7

Andrey  Smorodov
Andrey Smorodov

Reputation: 10852

Yes it called LineIterator.

// grabs pixels along the line (pt1, pt2)
// from 8-bit 3-channel image to the buffer
LineIterator it(img, pt1, pt2, 8);
LineIterator it2 = it;
vector<Vec3b> buf(it.count);
for(int i = 0; i < it.count; i++, ++it)
    buf[i] = *(const Vec3b*)*it;
// alternative way of iterating through the line
for(int i = 0; i < it2.count; i++, ++it2)
{
    Vec3b val = img.at<Vec3b>(it2.pos());
    CV_Assert(buf[i] == val);
}

More info here:

https://docs.opencv.org/master/dc/dd2/classcv_1_1LineIterator.html

Upvotes: 2

Related Questions