简体   繁体   English

将 RGB 数据映射到图例中的值

[英]Mapping RGB data to values in legend

This is a follow-up to my previous question here这是一个后续行动,我刚才的问题在这里

I've been trying to convert the color data in a heatmap to RGB values.我一直在尝试将热图中的颜色数据转换为 RGB 值。

source image 源图像

In the below image, to the left is a subplot present in panel D of the source image.在下图中,左侧是源图像面板 D 中的子图。 This has 6 x 6 cells (6 rows and 6 columns).它有 6 x 6 个单元格(6 行 6 列)。 On the right, we see the binarized image, with white color highlighted in the cell that is clicked after running the code below.在右侧,我们看到二值化图像,在运行以下代码后单击的单元格中突出显示白色。 The input for running the code is the below image.运行代码的输入是下图。 The ouput is (mean = [ 27.72 26.83 144.17]) is the mean of BGR color in the cell that is highlighted in white on the right image below.输出是(mean = [ 27.72 26.83 144.17])是下图右图中以白色突出显示的单元格中 BGR 颜色的平均值。

在此处输入图片说明

A really nice solution that was provided as an answer to my previous question is the following ( ref )作为我上一个问题的答案提供的一个非常好的解决方案如下(参考

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]
        mean.append(np.around(np.array(avg), 2))
        print('mean = ', np.around(np.array(avg), 2))
        # print(mean) # appends data to variable mean


if __name__ == '__main__':

    mean = []  #np.zeros((6, 6))
    # create window and callback
    winname = 'img'
    cv2.namedWindow(winname)
    cv2.setMouseCallback(winname, mouse_callback)

    # read & display image
    image = cv2.imread('ip2.png', 1)
    #image = image[3:62, 2:118]  # crop the image to 6x6 cells

    #---- resize image--------------------------------------------------
    # appended this to the original code

    print('Original Dimensions : ', image.shape)

    scale_percent = 220  # percent of original size
    width = int(image.shape[1] * scale_percent / 100)
    height = int(image.shape[0] * scale_percent / 100)
    dim = (width, height)
    # resize image
    image = cv2.resize(image, dim, interpolation=cv2.INTER_AREA)

    # ----------------------------------------------------------------------
    cv2.imshow(winname, image)
    cv2.waitKey()  # press any key to exit
    cv2.destroyAllWindows()

What do I want to do next?我接下来想做什么?

The mean of the RGB values thus obtained has to be mapped to the values in the following legend provided in the source image,如此获得的 RGB 值的平均值必须映射到源图像中提供的以下图例中的值,

在此处输入图片说明

I would like to ask for suggestions on how to map the RGB data to the values in the legend.我想问一下如何将 RGB 数据映射到图例中的值的建议。

Note: In my previous post it has been suggested that one could注意:在我之前的帖子中,有人建议可以

fit the RGB values into an equation which gives continuous results.将 RGB 值拟合到给出连续结果的等式中。

Any suggestions in this direction will also be helpful.在这方面的任何建议也将有所帮助。

EDIT: Answering the comment below编辑:回答下面的评论

I did the following to measure the RGB values of legend Input image:我做了以下来测量图例输入图像的 RGB 值: 在此处输入图片说明

This image has 8 cells in columns width and 1 cell in rows height此图像的列width有 8 个单元格,行height 1 个单元格

Changed these lines of code:更改了这些代码行:

left_bound = x - (x % round(width/8)) # 6 replaced with 8
right_bound = left_bound + round(width/8) # 6 replaced with 8
up_bound = y - (y % round(height/1)) # 6 replaced with 1
down_bound = up_bound + round(height/1) # 6 replaced with 1

Mean obtained for each cell/ each color in legend from left to right:从左到右为图例中的每个单元格/每种颜色获得的平均值:

mean =  [ 82.15 174.95  33.66]
mean =  [45.55 87.01 17.51]
mean =  [8.88 8.61 5.97]
mean =  [16.79 17.96 74.46]
mean =  [ 35.59  30.53 167.14]
mean =  [ 37.9   32.39 233.74]
mean =  [120.29 118.   240.34]
mean =  [238.33 239.56 248.04]

You can try to apply piece wise approach, make pair wise transitions between colors:您可以尝试应用分段方法,在颜色之间进行成对转换:

c[i->i+1](t)=t*(R[i+1],G[i+1],B[i+1])+(1-t)*(R[i],G[i],B[i]) 

Do the same for these values:对这些值执行相同的操作:

val[i->i+1](t)=t*val[i+1]+(1-t)*val[i]

Where i - index of color in legend scale, t - parameter in [0:1] range.其中 i - 图例比例中的颜色索引,t - [0:1] 范围内的参数。

So, you have continuous mapping of 2 values, and just need to find color parameters i and t closest to sample and find value from mapping.因此,您有 2 个值的连续映射,只需要找到最接近样本的颜色参数 i 和 t 并从映射中找到值。

Update:更新:

To find the color parameters you can think about every pair of neighbour legend colors as a pair of 3d points, and your queried color as external 3d point.要找到颜色参数,您可以将每对邻居图例颜色视为一对 3d 点,将查询的颜色视为外部 3d 点。 Now you just meed to find a length of perpendicular from the external point to a line, then, iterating over legend color pairs, find the shortest perpendicular (now you have i).现在,您只需找到从外部点到一条线的垂线长度,然后遍历图例颜色对,找到最短的垂线(现在您有了 i)。

Then find intersection point of the perpendicular and the line.然后找到垂线和直线的交点。 This point will be located at the distance A from line start and if line length is L then parameter value t=A/L.该点将位于距线起点的距离 A 处,如果线长为 L,则参数值 t=A/L。

Update2:更新2:

Simple brutforce solution to illustrate piece wise approach:简单的 brutforce 解决方案来说明分段方法:

#include "opencv2/opencv.hpp"
#include <string>
#include <iostream>

using namespace std;
using namespace cv;

int main(int argc, char* argv[])
{
    Mat Image=cv::Mat::zeros(100,250,CV_32FC3);
    std::vector<cv::Scalar> Legend;
    Legend.push_back(cv::Scalar(82.15,174.95,33.66));
    Legend.push_back(cv::Scalar(45.55, 87.01, 17.51));
    Legend.push_back(cv::Scalar(8.88, 8.61, 5.97));
    Legend.push_back(cv::Scalar(16.79, 17.96, 74.46));
    Legend.push_back(cv::Scalar(35.59, 30.53, 167.14));
    Legend.push_back(cv::Scalar(37.9, 32.39, 233.74));
    Legend.push_back(cv::Scalar(120.29, 118., 240.34));
    Legend.push_back(cv::Scalar(238.33, 239.56, 248.04));

    std::vector<float> Values;
    Values.push_back(-4);
    Values.push_back(-2);
    Values.push_back(0);
    Values.push_back(2);
    Values.push_back(4);
    Values.push_back(8);
    Values.push_back(16);
    Values.push_back(32);

    int w = 30;
    int h = 10;

    for (int i = 0; i < Legend.size(); ++i)
    {
        cv::rectangle(Image, Rect(i * w, 0, w, h), Legend[i]/255, -1);
    }

    std::vector<cv::Scalar> Smooth_Legend;
    std::vector<float> Smooth_Values;
    for (int i = 0; i < Legend.size()-1; ++i)
    {
        cv::Scalar c1 = Legend[i];
        cv::Scalar c2 = Legend[i + 1];
        float v1 = Values[i];
        float v2 = Values[i+1];
        for (int j = 0; j < w; ++j)
        {
            float t = (float)j / (float)w;
            Scalar c = c2 * t + c1 * (1 - t);
            float v = v2 * t + v1 * (1 - t);
            float x = i * w + j;
            line(Image, Point(x, h), Point(x, h + h), c/255, 1);
            Smooth_Values.push_back(v);
            Smooth_Legend.push_back(c);
        }
    }

    Scalar qp = cv::Scalar(5, 0, 200);
    float d_min = FLT_MAX;
    int ind = -1;
    for (int i = 0; i < Smooth_Legend.size(); ++i)
    {
        float d = cv::norm(qp- Smooth_Legend[i]);
        if (d < d_min)
        {
            ind = i;
            d_min = d;
        }
    }
    std::cout << Smooth_Values[ind] << std::endl;

    line(Image, Point(ind, 3 * h), Point(ind, 4 * h), Scalar::all(255), 2);
    circle(Image, Point(ind, 4 * h), 3, qp/255,-1);
    putText(Image, std::to_string(Smooth_Values[ind]), Point(ind, 70), FONT_HERSHEY_DUPLEX, 1, Scalar(0, 0.5, 0.5), 0.002);


    cv::imshow("Legend", Image);
    cv::imwrite("result.png", Image*255);
    cv::waitKey();

}

The result:结果:

在此处输入图片说明

Python: Python:

import cv2
import numpy as np
height=100
width=250
Image = np.zeros((height, width,3), np.float)
legend =  np.array([ (82.15,174.95,33.66),
          (45.55,87.01,17.51),
          (8.88,8.61,5.97),
          (16.79,17.96,74.46),
          ( 35.59,0.53,167.14),
          ( 37.9,32.39,233.74),
          (120.29,118.,240.34),
          (238.33,239.56,248.04)], np.float)

values = np.array([-4,-2,0,2,4,8,16,32], np.float)

# width of cell, also defines number 
# of one segment transituin subdivisions.
# Larger values will give more accuracy, but will woek slower.
w = 30 
# Only fo displaying purpose. Height of bars in result image.
h = 10


# Plot legend cells ( to check correcrness only )
for i in range(len(legend)):
    col=legend[i]
    cv2.rectangle(Image, (i * w, 0, w, h), col/255, -1)

# Start form smoorhed scales for color and according values
Smooth_Legend=[]
Smooth_Values=[]
for i in range(len(legend)-1): # iterate known knots
    c1 = legend[i] # start color point
    c2 = legend[i + 1] # end color point
    v1 = values[i] # start value 
    v2 = values[i+1] # emd va;ie
    for j in range(w): # slide inside [start:end] interval.
        t = float(j) / float(w) # map it to [0:1] interval
        c = c2 * t + c1 * (1 - t) # transition between c1 and c2
        v = v2 * t + v1 * (1 - t) # transition between v1 and v2
        x = i * w + j # global scale coordinate (for drawing)
        cv2.line(Image, (x, h), (x, h + h), c/255, 1) # draw one tick of smoothed scale
        Smooth_Values.append(v) # append smoothed values for next step
        Smooth_Legend.append(c) # append smoothed color for next step

# queried color    
qp = np.array([5, 0, 200])
# initial value for minimal distance set to large value
d_min = 1e7
# index for clolor search
ind = -1
# search for minimal distance from queried color to smoothed scale color
for i in range(len(Smooth_Legend)):
    # distance
    d = cv2.norm(qp-Smooth_Legend[i])
    if (d < d_min):    
        ind = i
        d_min = d
# ind contains index of the closest color in smoothed scale
# and now we can extract according value from smoothed values scale
print(Smooth_Values[ind]) # value mapped to queried color.
# plot pointer (to check ourself)
cv2.line(Image, (ind, 3 * h), (ind, 4 * h), (255,255,255), 2);
cv2.circle(Image, (ind, 4 * h), 3, qp/255,-1);
cv2.putText(Image, str(Smooth_Values[ind]), (ind, 70), cv2.FONT_HERSHEY_DUPLEX, 1, (0, 0.5, 0.5), 1);
# show window
cv2.imshow("Legend", Image)
# save to file
cv2.imwrite("result.png", Image*255)
cv2.waitKey()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM