Natasha
Natasha

Reputation: 1521

Digitizing heatmap and map pixels to values

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

enter image description here

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 enter image description here gives different RGB values at different points.

Any suggestion on how to obtain an average value for each cell (e.g.enter image description here) will be really helpful.

EDIT2:

I've tried the updated code.

enter image description here

The mean average for this ((e.g.enter image description here)) 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.

Upvotes: 0

Views: 1059

Answers (1)

Burak
Burak

Reputation: 2495

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.

Upvotes: 1

Related Questions