将RGB数据映射到图例中的值 [英] Mapping RGB data to values in legend

查看:90
本文介绍了将RGB数据映射到图例中的值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我上一个问题的后续操作

This is a follow-up to my previous question here

我一直在尝试将热图中的颜色数据转换为RGB值.

I've been trying to convert the color data in a heatmap to RGB values.

源图像

在下面的图像中,左侧是源图像的面板D中存在的子图.它具有6 x 6单元(6行和6列).在右侧,我们看到了二值化后的图像,在运行以下代码后单击的单元格中突出显示了白色.下图是运行代码的输入.输出(mean = [ 27.72 26.83 144.17])是单元格中BGR颜色的平均值,在下面的右图中以白色突出显示.

In the below image, to the left is a subplot present in panel D of the source image. This has 6 x 6 cells (6 rows and 6 columns). 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.

以下是我回答的一个非常好的解决方案( ref )

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()

接下来我要做什么?

由此获得的RGB值的平均值必须映射到源图像中提供的以下图例中的值,

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.

注意:在我的上一篇文章中,有人建议

Note: In my previous post it has been suggested that one could

将RGB值拟合到一个可以得出连续结果的方程式中.

fit the RGB values into an equation which gives continuous results.

任何有关此方向的建议也将有所帮助.

Any suggestions in this direction will also be helpful.

回答下面的评论

我做了以下操作来测量图例的RGB值 输入图片:

I did the following to measure the RGB values of legend Input image:

此图像在width列中有8个单元格,在height行中有1个单元格

This image has 8 cells in columns width and 1 cell in rows height

更改了以下代码行:

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]) 

对值进行相同的操作:

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

其中i-图例比例尺的颜色索引,t-[0:1]范围内的参数.

Where i - index of color in legend scale, t - parameter in [0:1] range.

因此,您具有2个值的连续映射,并且jist需要找到最接近样本的颜色参数i和t并从映射中找到值.

So, you have continous mapping of 2 values, and jist need to find color parameters i and t closest to sample and find value from mapping.

更新: 要查找颜色参数,您可以将每对相邻的图例颜色视为一对3d点,并将所需的颜色视为外部3d点.现在,您只需要查找从外部点到直线的垂直线长度,然后遍历图例颜色对,找到最短的垂直线(现在有i). 然后找到垂直线和直线的交点.该点位于距线起点的距离A处,如果线长为L,则参数值t = A/L.

Update: To find the color parameters you can think about every pair of neighbour legend colors as a pair of 3d points, and your quired color as external 3d point. Now you just meed to find a lenght of perpendicular from the external point to a line, then, iterating over legend color pairs, find the shortest perpendicular (now you have 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 lenght is L then parameter value t=A/L.

Update2:

简单的brutforce解决方案来说明分段方法:

Simple brutforce solution to illustrate piece wise approach:

#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();

}

结果:

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 draeing)
        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

# qiired 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 quired 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
# amd now we can extract according value from smoothed values scale
print(Smooth_Values[ind]) # value mapped to quiresd 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()

这篇关于将RGB数据映射到图例中的值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆