问题
I'd like to digitize a heatmap, Panel D in this source
image
As a first step, I tried to read the image in opencv and obtain a matrix
import cv2
from pprint import pprint
def read_as_digital(image):
# mage dimensions
h = image.shape[0]
w = image.shape[1]
print(h, w)
pass
if __name__ == '__main__':
image = cv2.imread('ip.jpg', 1)
pprint(image)
read_as_digital(image)
I could read the image as a matrix, but I don't know how to specify the beginning of the cells (corresponding to different subplots in panel D of the image) in the heatmap. finally, I want to map the pixels to values.
Any suggestions on how to proceed will be really helpful
EDIT1:
I tried to obtain the values on click
For instance, when I consider a small subsection of the heatmap provided in the source
I expect to obtain the average value for each cell (centered around yellow dots) in the image. Clicking at different points yields different values. Clicking on the cell that's coored gives different RGB values at different points.
Any suggestion on how to obtain an average value for each cell (e.g.) will be really helpful.
EDIT2:
I've tried the updated code.
The mean average for this ((e.g.)) works really well. However, there is a problem with the cell next to it. When I click the adjacent cell, the mean that is displayed by the code is for 3 cells that are with the same color. It would be really nice if there is a way to limit the cell size, kind of specify a boundary in which the mean should be calculated in the code. The image presented in edit 1 has 6 rows and 6 columns. If we view this as 6 by 6 matrix say, A, the mean should be obtained for each Aijth entry of the matrix.
回答1:
import cv2
import numpy as np
# print pixel value on click
def mouse_callback(event, x, y, flags, params):
if event == cv2.EVENT_LBUTTONDOWN:
# get specified color
row = y
column = x
color = image[row, column]
print('color = ', color)
# calculate range
thr = 20 # ± color range
up_thr = color + thr
up_thr[up_thr < color] = 255
down_thr = color - thr
down_thr[down_thr > color] = 0
# find points in range
img_thr = cv2.inRange(image, down_thr, up_thr) # accepted range
height, width, _ = image.shape
left_bound = x - (x % round(width/6))
right_bound = left_bound + round(width/6)
up_bound = y - (y % round(height/6))
down_bound = up_bound + round(height/6)
img_rect = np.zeros((height, width), np.uint8) # bounded by rectangle
cv2.rectangle(img_rect, (left_bound, up_bound), (right_bound, down_bound), (255,255,255), -1)
img_thr = cv2.bitwise_and(img_thr, img_rect)
# get points around specified point
img_spec = np.zeros((height, width), np.uint8) # specified mask
last_img_spec = np.copy(img_spec)
img_spec[row, column] = 255
kernel = np.ones((3,3), np.uint8) # dilation structuring element
while cv2.bitwise_xor(img_spec, last_img_spec).any():
last_img_spec = np.copy(img_spec)
img_spec = cv2.dilate(img_spec, kernel)
img_spec = cv2.bitwise_and(img_spec, img_thr)
cv2.imshow('mask', img_spec)
cv2.waitKey(10)
avg = cv2.mean(image, img_spec)[:3]
print('mean = ', np.around(np.array(avg), 2))
global avg_table
avg_table[:, 6 - int(x / (width/6)), 6 - int(y / (height/6))] = avg
print(avg_table)
# average value of each cell in 6x6 matrix
avg_table = np.zeros((3, 6, 6))
# create window and callback
winname = 'img'
cv2.namedWindow(winname)
cv2.setMouseCallback(winname, mouse_callback)
# read & display image
image = cv2.imread('ip.jpg', 1)
image = image[3:62, 2:118] # crop the image to 6x6 cells
cv2.imshow(winname, image)
cv2.waitKey() # press any key to exit
cv2.destroyAllWindows()
Note that OpenCV has BGR color format instead of RGB. So, clicking on the red color will print out [0, 0, 255]
, for instance.
You can change thr
to adjust range for accepted colors.
The image is cropped to include only 6 by 6 matrix part.
来源:https://stackoverflow.com/questions/60777750/digitizing-heatmap-and-map-pixels-to-values