Reputation: 1204
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
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:
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
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