相似矩阵反推标签

博客聚焦无监督设置下的多模态检索问题。有监督多模态检索常用标签构造相似矩阵,而本文尝试在无监督下构造相似矩阵后反求样本标签。提出两种思路,一是将标签当成参数用网络学习,二是将相似矩阵看成无向图贪心染色。经测试,两种方法各有不足,后续需探索更优算法。

Background

有监督的多模态检索(supervised multi-modal retrieval)中,常用 label 构造相似矩阵 S。
样本集 X = { x i } i = 1 n X=\{x_i\}_{i=1}^n X={xi}i=1n,对应标签集 L = { l i } i = 1 n L=\{l_i\}_{i=1}^n L={li}i=1n。对任意两个样本 x i x_i xi x j x_j xj,若 l i l_i li l j l_j lj 至少有一个共同标签(即 l i T l j > 0 l_i^Tl_j>0 liTlj>0),则认为它们相似。计算相似矩阵 S 定义为:
S i , j = { 1 , x i 与 x j 相 似 0 , x i 与 x j 不 相 似 S_{i,j}=\begin{cases} 1, & x_i 与 x_j 相似 \\ 0, & x_i 与 x_j 不相似 \end{cases} Si,j={1,0,xixjxixj

Problem

在 supervised 设置下,一般基于 label 信息计算相似矩阵。想做的是在 UNsupervised 设置下,用一些奇技淫巧构造相似矩阵 S 之后,反求样本 label(使求得的 label 能算出同样的 S),后续用 label 搞事。

Idea 1: Learn-to-Label

像学 hash code 一样,将 label 当成参数,用网络学出来,约束就用 DLFH[1] 那条,或见 DCMH[2]

code

from tensorflow import ConfigProto, Session
from keras.backend.tensorflow_backend import set_session
config = ConfigProto()
config.gpu_options.allow_growth = True
set_session(Session(config=config))

import os
import argparse
from time import time
import numpy as np

import keras
import keras.backend as K
from keras.callbacks import Callback
from keras.models import Model
from keras.layers import Dense, Dropout, Activation, Input, Lambda

np.random.seed(int(time()))
parser = argparse.ArgumentParser()
parser.add_argument('--EPOCH', type=int, default=100)
parser.add_argument('--BATCH', type=int, default=64)
parser.add_argument('--SRC', type=str, default='data')
opt = parser.parse_args()
print(opt)


def load_data(fname):
    return np.load(os.path.join(opt.SRC, f'{fname}.npy'))


I_train = load_data('I_train')
I_val = load_data('I_val')

L_train = load_data('L_train')
L_val = load_data('L_val')

EPOCH = opt.EPOCH
BATCH = opt.BATCH
N_CLASS = L_train.shape[-1]
DIM_IMG = I_train.shape[-1]


@K.tf.custom_gradient
def Htanh(x):  # hard tanh
    def grad(dy):
        cond = (x >= -1) & (x <= 1)
        zeros = K.zeros_like(dy)
        return K.tf.where(cond, dy, zeros)
    return K.sign(x), grad


def gen_data(which='train', bat_sz=BATCH):
    if which == "train":
        Img, Lab = I_train, L_train
    elif which == "test":
        Img, Lab = I_val, L_val
    num = Lab.shape[0]
    S = (np.dot(Lab, Lab.T) > 0).astype(np.float32)
    while True:
        idx = np.random.choice(num, bat_sz)
        im, sim, lb = Img[idx], S[idx], Lab[idx]
        yield [im, sim], lb


# network
in_img = Input([DIM_IMG], name='image')
in_sim = Input([BATCH], name='similarity_matrix')

x = in_img
x = Dense(N_CLASS)(x)
x = Lambda(Htanh)(x)
l = x

m_train = Model([in_img, in_sim], l, name='train')
clf = Model(in_img, l, name='classifier')


def struct(y_true, y_pred):
    s_hat = in_sim * 2. - 1.
    theta = 0.5 * K.dot(y_pred, K.transpose(y_pred))
    loss = - K.log(0.5 * (1. - s_hat) + s_hat * K.sigmoid(theta) + 1e-9)
    return K.sum(loss)


S_val = (np.dot(L_train, L_train.T) > 0).astype(np.int)
tot = L_train.shape[0] ** 2
def test():
    l = clf.predict(I_train)
    sv = (np.dot(l, l.T) > 0).astype(np.int)
    # print('right:', np.sum(sv * S_val))
    # print('wrong:', np.sum(sv * (1 - S_val)))
    # print('--- S_val ---\n', S_val)
    # print(' --- sv ---\n', sv)
    print('S_val:', np.sum(S_val))  # 真·相似对数
    print('sv:', np.sum(sv))  # 预测的相似对数
    print('total:', tot)  # 总对数
    l_sum = np.sum(l, axis=-1)  # 各 label 的 1 个数
    L_sum = np.sum(L_train, axis=-1)
    print('mean discrepency:', np.mean(np.abs(l_sum - L_sum)))


class moniter(Callback):
    def on_epoch_end(self, epoch, logs=None):
        if epoch % 10 == 9 or epoch == 0:
            print(epoch, "> "*10)
            test()


m_train.compile('adam', loss=struct)
gen_train = gen_data('train')
gen_test = gen_data('test')
m_train.fit_generator(gen_train,
                  steps_per_epoch=I_train.shape[0]//BATCH,
                  epochs=EPOCH,
                  callbacks=[moniter()],
                  validation_data=gen_test,
                  verbose=0,
                  validation_steps=I_val.shape[0]//BATCH)

print('--- after ---')
# test()
clf.compile('adam', loss='binary_crossentropy', metrics=['binary_accuracy'])
print(clf.evaluate(I_train, L_train))

Idea 2

通过一个确定的过程构造 label。将相似矩阵 S 看成一幅无向图,贪心地染色(打标签)。

sample

a
b
i
c
g
d
e
f
h
j

code

from os.path import join
from scipy.spatial.distance import cdist
import numpy as np


def load_data(fname):
    return np.load(join('data', f'{fname}.npy'))


# L_train = load_data('L_train')
# L_val = load_data('L_val')
# L_ret = load_data('L_ret')


def construct_label(S):
    N = S.shape[0]  # 样本数
    vis_edge = np.identity(N, dtype=np.int)  # np.zeros_like(S)  # 已考虑过的边(相似关系)
    class_set = {}  # class_set[i]:i 类对应的样本 id
    class_id = 0  # class id 总数

    # 考虑孤立点(只同自己相似的样本)
    # 它们自己要拥有一个 label
    s_sum = np.sum(S, axis=-1)
    for i in range(N):
        if s_sum[i] == 1:
            vis_edge[i][i] = 0

    for now in range(N):
        # 邻接点集
        neighbour = set()
        for v in range(N):
            if S[now][v] == 1 and vis_edge[now][v] == 0:
                neighbour.add(v)

        while len(neighbour) > 0:
            v = neighbour.pop()
            if vis_edge[now][v] == 1:  # 已经考虑过
                continue
            vis_edge[now][v] = vis_edge[v][now] = 1

            elem = {now, v}  # 当前类的元素集
            for u in neighbour:
                # 看 now 的其它邻点能否加入当前类的元素集
                # 要求它同其它所有元素都是邻点
                can_in = True
                for e in elem:
                    if S[u][e] != 1:
                        can_in = False
                        break
                if can_in:
                    elem.add(u)
                    for e in elem:
                        vis_edge[u][e] = vis_edge[e][u] = 1
            
            class_set[class_id] = elem
            class_id += 1
            neighbour = neighbour - elem
    
    label = np.zeros((N, class_id))
    for lb in class_set:
        for x in class_set[lb]:
            label[x][lb] = 1

    # 检验是否已考虑所有相似关系
    for i in range(S.shape[0]):
        for j in range(S.shape[1]):
            if S[i][j] != vis_edge[i][j]:
                print(f's & vis_edge diff: ({i}, {j})')

    return label


if __name__ == '__main__':
	# 对应上图 sample
    # 样例手打的 label
    lab = np.array([
        [1, 1, 0, 0, 0, 0, 0, 0, 0],  # (1, 2)
        [1 ,0, 1, 1, 0, 0, 0, 0, 0],  # (1, 3, 4)
        [0, 0, 1, 0, 1, 0, 0, 0, 0],  # (3, 5)
        [0, 0, 0, 0, 1, 0, 0, 0, 0],  # (5)
        [0, 0, 0, 0, 1, 1, 0, 0, 0],  # (5, 6)
        [0, 0, 0, 0, 0, 0, 1, 0, 0],  # (7)
        [0, 0, 0, 1, 0, 6, 1, 1, 0],  # (4 ,6, 7, 8)
        [0, 0, 0, 0, 0, 0, 0, 1, 0],  # (8)
        [0, 1, 0, 0, 0, 0, 0, 1, 0],  # (2, 4)
        [0, 0, 0, 0, 0, 0, 0, 0, 1]   # (9),孤立点
    ])
    # 手打 label 算出的 S
    sim = (np.dot(lab, lab.T) > 0).astype(np.int)
    # 手打的 S
    sim_my = np.array([
        [1, 1, 0, 0, 0, 0, 0, 0, 1, 0],
        [1, 1, 1, 0, 0, 0, 1, 0, 0, 0],
        [0, 1, 1, 1, 1, 0, 0, 0, 0, 0],
        [0, 0, 1, 1, 1, 0, 0, 0, 0, 0],
        [0, 0, 1, 1, 1, 0, 1, 0, 0, 0],
        [0, 0, 0, 0, 0, 1, 1, 0, 0, 0],
        [0, 1, 0, 0, 1, 1, 1, 1, 1, 0],
        [0, 0, 0, 0, 0, 0, 1, 1, 1, 0],
        [1, 0, 0, 0, 0, 0, 1, 1, 1, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
    ])
    ## 检验手打 S 和算出的 S 一致
    # for r in range(sim_my.shape[0]):
    #     for c in range(sim_my.shape[1]):
    #         if sim_my[r][c] != sim[r][c]:
    #             print(f'my sim diff: ({r}, {c})')
    # print('my sim finish')
    # print(sim - np.identity(sim.shape[0]))

    lab_hat = construct_label(sim)
    sim_hat = (np.dot(lab_hat, lab_hat.T) > 0).astype(np.int)
    print(lab.shape, lab_hat.shape, '\n', lab_hat)
    print(sim.shape, sim_hat.shape, '\n', sim_hat)

    # 原 S 和构造 S' 的差别
    for r in range(sim_hat.shape[0]):
        for c in range(sim_hat.shape[1]):
            if sim_hat[r][c] != sim[r][c]:
                print(f'sim diff: ({r}, {c})')
    print('sim finish')

    # 原 L 和构造 L' 的区别
    for r in range(lab_hat.shape[0]):
        for c in range(lab_hat.shape[1]):
            if lab_hat[r][c] != lab[r][c]:
                print(f'lab diff: ({r}, {c})')
    print('lab finish')

Discussion

第一种效果好像不太好。
第二种好像可以,在我构造的 sample 上虽然构造出的 label 同原来的标号有点对不上,但交换下顺序可以同原来的一样(同构)。
但当用 flickr-25k test set(2k 个 sample)的 label 测试时,发现构造的 label 虽然可以算出原来的 S,但 label 长了很多,即构造了更多的类别:原本只有 24 个,而构造的有 3412 个。而且用 training set 同用 test set 构造的 label 维度不同…显然这种构造法只是个弟弟。
而且第二种是在无向图下测试的,即 S 对称,在 supervised 下用 label 算出来的 S 可以满足,但 unsupervised 下构造的 S 可能是非对称的,如 [3],这时不知道还能不能用。

Future Work

  1. 更优的构造算法,使得构造的 class 不会多得那么夸张(最好可以同真 label 一样长,甚至可以惟一对应回原来的标签,就像在我上文构造的那个简单 sample 那样)
  2. 用于非对称 S 的构造法

References

  1. Discrete Latent Factor Model for Cross-Modal Hashing
  2. Deep Cross-Modal Hashing
  3. Unsupervised Generative Adversarial Cross-Modal Hashing
<think>我们正在解决如何获取`label2rgb`函数中各个颜色对应的原始标签值的问题。`label2rgb`函数将标签矩阵(由`bwlabel`、`watershed`等生成)转换为RGB彩色图像,以便可视化。然而,转换后的RGB图像中的颜色是随机或根据给定的颜色映射分配的,我们可能需要知道每种颜色在RGB图像中对应的是原始标签矩阵中的哪个标签值。 根据引用[1]和引用[2]: - `label2rgb(L, map, zerocolor, order)`中,`L`是标签矩阵,`map`是颜色映射矩阵(n行3列,n为颜色数量),`zerocolor`是标签0对应的颜色,`order`参数可以控制标签与颜色的对应关系(默认是按标签数值顺序对应,也可以设置为随机'shuffle')。 我们的目标:获取转换后的RGB图像中每种颜色对应的原始标签值。 方法: 如果我们使用默认的`order`(即'noshuffle'),那么标签矩阵中的标签值k(从1到n)将对应颜色映射`map`中的第k行颜色。标签0使用`zerocolor`指定的颜色。 但是,如果使用了`shuffle`,则对应关系是随机的,我们需要从函数内部获取这种对应关系。 然而,`label2rgb`函数本身并不直接返回颜色与标签的对应关系。因此,我们需要通过其他方式来确定。 解决方案: 1. 如果我们能控制标签矩阵和颜色映射,我们可以记录下颜色映射的顺序(特别是当使用随机顺序时,我们可以通过设置随机种子来重现,或者通过函数返回的RGB图像反推)。 2. 实际上,在默认情况下(noshuffle),标签k对应的颜色就是`map(k,:)`(注意:标签矩阵中标签值从1开始,所以颜色映射的第一行对应标签1,第二行对应标签2,以此类推)。标签0的颜色由`zerocolor`指定。 3. 当使用随机顺序('shuffle')时,`label2rgb`会在内部打乱颜色映射的顺序,然后再按打乱后的顺序分配给标签1,2,...,n。由于随机性,我们无法直接知道对应关系。但我们可以通过以下步骤获取: a. 设置随机种子(如果需要重现)。 b. 在调用`label2rgb`之前,先手动生成一个随机排列向量,然后重新排列颜色映射,再使用'noshuffle'模式。这样,我们保存了随机排列向量,就可以知道标签与颜色的对应关系。 具体步骤: 情况1:默认情况(noshuffle) 对应关系:标签k -> 颜色映射map的第k行颜色(k=1,2,...,n),标签0 -> zerocolor。 情况2:shuffle(随机顺序) 我们可以模拟`label2rgb`的随机过程,从而得到对应关系: ```matlab % 假设标签矩阵为L,颜色映射为map,zerocolor为指定的颜色 % 设置随机状态(如果需要重现,可以设置随机种子) rng(0); % 设置随机种子,确保可重现 % 获取标签的数量(最大标签值) numLabels = max(L(:)); % 注意:标签0是背景,从1开始是各个区域 % 生成一个随机排列 perm = randperm(numLabels); % 重新排列颜色映射:将原来第i行的颜色放到perm(i)的位置,但实际上我们只需要将颜色映射按照随机排列重新排序 shuffled_map = map(perm, :); % 然后使用noshuffle模式,传入重新排列后的颜色映射 RGB = label2rgb(L, shuffled_map, zerocolor, 'noshuffle'); % 此时,标签值k对应的颜色是shuffled_map(k,:)吗?注意:不是! % 实际上,在noshuffle模式下,标签k映射到颜色映射的第k行颜色。而我们现在颜色映射的第k行实际上是原始颜色映射的第perm(k)行。 % 因此,标签k对应的颜色是原始颜色映射的第perm(k)行。 % 所以,我们可以记录:标签k -> 原始颜色映射的第perm(k)种颜色。 % 或者,我们也可以构建一个从标签值到原始颜色映射索引的映射:k -> perm(k) % 例如,我们想知道标签值1对应什么颜色?那就是原始颜色映射的第perm(1)行的颜色。 ``` 但是,这种方法需要我们自己生成随机排列,而不是使用`label2rgb`内置的随机。如果我们已经使用了`label2rgb`的shuffle模式,那么如何获取它的随机排列呢? 实际上,`label2rgb`在shuffle模式下的随机排列是内部生成的,我们无法直接获取。因此,我们只能通过自己控制随机过程来代替使用shuffle模式,这样我们就能记录下排列。 另一种思路:从输出的RGB图像中反推颜色对应的标签。 由于每个标签标签矩阵L中对应一组像素位置,我们可以在这些位置上取RGB图像的颜色,然后与颜色映射进行比较,从而确定该标签对应颜色映射中的哪种颜色。但注意,颜色映射中的颜色在RGB图像中可能会被量化,但由于颜色映射本身颜色数量有限,我们可以通过查找最接近的颜色来确定。 步骤: 1. 对每个标签k(从1到n),在标签矩阵L中找到所有等于k的位置(即该标签对应的区域)。 2. 取出RGB图像上这些位置的所有像素值(注意,由于颜色是均匀的,理想情况下该区域内所有像素都是同一种颜色)。 3. 取该区域内的一个像素的颜色(例如第一个像素),记为c(一个1x3的向量)。 4. 在颜色映射map中查找与c最接近的行(使用欧氏距离或其他距离度量),该行索引就是该标签对应的颜色索引(在默认noshuffle下,这个颜色索引应该等于k,但在shuffle下,这个颜色索引是随机的,但我们知道标签k对应的颜色就是颜色映射中的这个索引对应的颜色)。 但是,这种方法在颜色映射中有多个相似颜色时可能不准确,而且由于图像保存可能引入误差(如果是真彩色,通常不会量化,所以比较准确)。 然而,我们还需要注意,标签0对应的颜色是zerocolor,它可能不在颜色映射map中。 因此,为了获取`label2rgb`后各个颜色对应的标签值,我们有两种方法: 方法A(推荐):在调用`label2rgb`时避免使用shuffle模式,而是自己控制颜色映射的顺序(包括随机打乱),这样我们就知道对应关系。 方法B:通过分析输出的RGB图像和原始标签矩阵,建立一个映射表。 下面我们给出方法B的代码示例(即使使用了shuffle模式也可以使用): 步骤: 1. 获取标签矩阵L和输出的RGB图像(假设已经用label2rgb转换得到,不管是否shuffle)。 2. 获取标签值列表(包括0和所有非零标签)。 3. 对于每个非零标签k,在标签矩阵L中取第一个像素位置(或者所有像素位置的颜色取平均,但通常区域内颜色相同,所以取一个点即可)。 4. 将该位置对应的RGB颜色与颜色映射map中的每一行进行比较,找到最接近的行索引(注意:如果颜色映射map是我们传入的,那么我们可以使用这个map;如果我们没有传入,默认使用jet,那么我们也应该使用jet(numLabels)来生成相同的map)。 5. 对于标签0,直接比较是否等于zerocolor(如果传入)或默认的白色[1,1,1](注意:在uint8图像中,白色是[255,255,255])。 注意:RGB图像可能是double类型(值范围[0,1])或uint8类型([0,255])。因此,我们需要统一到同一范围比较。 示例代码(假设我们不知道调用label2rgb时使用的参数,但知道颜色映射和zerocolor): ```matlab % 假设: % L: 标签矩阵(包含0,1,2,...,n) % RGB: 由label2rgb输出的RGB图像,类型可能是uint8或double % map: 颜色映射,与调用label2rgb时使用的一致(如果没有传入,则使用默认的jet,可以用colormap(jet)得到,但要知道数量) % zerocolor: 调用时使用的背景色(如果没有传入,默认是[1,1,1](double)或[255,255,255](uint8)) % 步骤1:确定RGB图像的数据类型 if isa(RGB, 'uint8') scale = 255; else scale = 1; end % 步骤2:获取所有标签值(包括0) labels = unique(L(:)); % 从小到大排序,0在第一个(如果有的话) % 步骤3:创建一个映射结构,记录每个标签对应的颜色在map中的索引(标签0不需要索引,单独处理) % 注意:标签0对应背景色,不在map中,需要单独记录。 % 初始化一个向量,存储每个非零标签对应的颜色索引 color_index = zeros(max(labels),1); % 如果标签值不连续,我们可以用其他数据结构 % 预先处理标签0 zero_mask = (L==0); if any(zero_mask(:)) % 取第一个背景像素的颜色 [r, c] = find(zero_mask, 1, 'first'); zero_color = squeeze(RGB(r,c,:))'; % 变成行向量 % 注意:RGB图像可能是三维的,第三维是颜色通道 % 比较zero_color与zerocolor(需要统一到相同范围) % 假设zerocolor是我们在调用时传入的,可能是[0,1]范围或[0,255]范围?我们需要根据RGB图像的类型调整zerocolor的范围 % 如果RGB是uint8,而传入的zerocolor是[0,1]的,则需要乘以255 % 这里假设我们已知传入label2rgb的zerocolor的值(因为是我们传入的) % 如果不知道,我们就只能假设背景色就是我们看到的zero_color % 这里我们只关心非0标签,背景色我们不需要在映射中找索引,所以跳过。 end % 对于每个非零标签 for k = 1:max(labels) mask = (L == k); if any(mask(:)) % 取第一个像素 [r, c] = find(mask, 1, 'first'); color = squeeze(RGB(r,c,:))'; % 1x3 % 将颜色缩放到[0,1]范围,以与map比较(因为map是[0,1]的) if scale == 255 color = color / 255; end % 计算color与map中每一行的距离 dist = sum((map - color).^2, 2); % 按行求和,得到列向量 [~, idx] = min(dist); % 找到距离最小的行索引 color_index(k) = idx; % 注意:这里有一个问题,多个标签可能对应同一个颜色索引?但是不应该,因为每个标签分配一种颜色。 else % 标签k不存在,我们可以跳过 color_index(k) = 0; % 或者NaN end end % 现在,color_index(k)表示标签k对应的颜色在map中的行索引(即第几种颜色) % 但是注意,由于shuffle,这个索引并不是k,而是随机分配的颜色索引。 % 如果我们想知道标签k对应的颜色在原始颜色映射(即jet(numLabels))中的位置,实际上就是这个索引值。 % 例如,标签1对应的颜色是map(color_index(1),:),也就是原始颜色映射的第color_index(1)行。 % 但是,如果我们想要知道颜色映射中的第i种颜色对应哪个标签,我们可以这样: % label = find(color_index == i); % 因为每个标签对应一个颜色索引,而不同的标签不会对应同一个颜色索引(除非标签数超过颜色映射的行数,但一般不会,因为标签数就是颜色映射的行数)。 ``` 然而,这种方法依赖于从图像中取样的颜色是否准确,而且要求每个标签区域内的颜色都是均匀的(没有噪声)。另外,如果颜色映射中有两个非常接近的颜色,可能会出错。 因此,最可靠的方法还是控制颜色映射的生成过程,避免使用内置的shuffle,而是自己打乱颜色映射,并记录打乱顺序。 总结: 1. 使用noshuffle模式(默认),则标签k对应颜色映射的第k种颜色。 2. 如果需要随机颜色,则自己生成随机排列,并打乱颜色映射,然后使用noshuffle模式,同时记录随机排列(perm向量),这样标签k对应的颜色就是原始颜色映射的第perm(k)种颜色。 3. 如果已经使用了shuffle模式,并且无法重现,那么只能通过上述图像分析的方法来反推(有一定风险)。 根据引用[3],我们可以看到他们使用`label2rgb`后,用`imshow`显示彩色图像,但没有提及如何获取颜色对应的标签值。 根据引用[4],是关于图例显示的问题,与我们的问题不直接相关。 所以,针对用户问题的答案: 在Matlab中,要获取`label2rgb`函数转换后各个颜色对应的原始标签值,可以采用以下方法: ### 方法1:使用默认顺序(推荐) - 在调用`label2rgb`时使用默认的`order`参数(即`'noshuffle'`) - 此时颜色对应关系由颜色映射`map`的行索引决定: - 标签$k$($k \geq 1$) 对应颜色 `map(k,:)` - 标签$0$对应背景色`zerocolor` - 示例: ```matlab L = [1 1 0; 2 2 0; 3 3 3]; % 标签矩阵 map = [1 0 0; 0 1 0; 0 0 1]; % 红色对应标签1,绿色对应标签2,蓝色对应标签3 RGB = label2rgb(L, map, [0 0 0]); % 背景黑色 % 则标签1为红色,标签2为绿色,标签3为蓝色 ``` ### 方法2:自定义随机顺序(可控) - 手动生成随机排列并重排颜色映射,避免使用内置`shuffle`: ```matlab numLabels = max(L(:)); % 最大标签值(标签数量) perm = randperm(numLabels); % 随机排列 shuffled_map = map(perm, :); % 重排颜色映射 RGB = label2rgb(L, shuffled_map, zerocolor, 'noshuffle'); ``` - 此时标签$k$对应原始颜色映射`map`中的第`perm(k)`种颜色 ### 方法3:从输出图像反推(适用于未知参数) - 通过分析RGB图像和标签矩阵重建映射关系: ```matlab % 假设已知颜色映射map和背景色zerocolor % RGB: 输出的RGB图像 % L: 原始标签矩阵 % 统一到[0,1]范围 if isinteger(RGB) RGB_double = double(RGB) / 255; else RGB_double = RGB; end % 获取所有标签 labels = unique(L); color_map_ref = map; % 原始颜色映射([0,1]范围) % 构建映射字典 mapping = struct(); for label = labels' if label == 0 % 背景色单独处理 [r, c] = find(L==0, 1); bg_color = squeeze(RGB_double(r,c,:))'; % 比较背景色与zerocolor(注意zerocolor的范围,假设也是[0,1]) % 这里我们直接记录背景色 mapping.zero = bg_color; else [r, c] = find(L==label, 1); region_color = squeeze(RGB_double(r,c,:))'; % 在颜色映射中找最接近的颜色 dist = sum((color_map_ref - region_color).^2, 2); [min_dist, idx] = min(dist); % 记录:标签label对应的颜色在map中的索引为idx mapping.(sprintf('label_%d', label)) = idx; end end ``` ### 注意事项 - 颜色映射`map`的行数应至少等于标签最大值(非零标签的最大值) - 背景色(标签0)不占用`map`中的颜色 - 当使用内置`shuffle`时,随机种子不可控,难以重现映射关系 ### 相关问题 1. 如何将`label2rgb`生成的彩色图像中的颜色替换为自定义颜色? 2. 如何为`label2rgb`生成的图像添加显示标签值的图例? 3. 在图像分割后,如何根据`label2rgb`的颜色映射统计不同区域的面积? </think>在 Matlab 中获取 `label2rgb` 转换后各个颜色对应的原始标签值,需要通过分析标签矩阵和颜色映射关系实现。以下是具体方法: ### 方法 1:通过标签索引直接映射(推荐) 当使用默认顺序 (`'noshuffle'`) 时,颜色与标签存在固定对应关系: ```matlab L = bwlabel(BW); % 生成标签矩阵 num_labels = max(L(:)); % 获取标签数量 map = jet(num_labels); % 创建与标签数量匹配的颜色映射 % 建立标签-颜色对应关系 color_mapping = cell(num_labels, 1); for k = 1:num_labels % 提取标签k对应的颜色 (RGB值范围[0,1]) color_mapping{k} = map(k, :); fprintf('标签%d → 颜色[%.2f, %.2f, %.2f]\n',... k, map(k,1), map(k,2), map(k,3)); end ``` **输出示例**: ``` 标签1 → 颜色[0.00, 0.00, 0.50] 标签2 → 颜色[0.00, 0.50, 1.00] 标签3 → 颜色[0.50, 1.00, 0.50] ``` ### 方法 2:从 RGB 图像反推颜色映射 当使用随机顺序 (`'shuffle'`) 时,通过像素采样重建映射: ```matlab RGB = label2rgb(L, map, [0 0 0], 'shuffle'); % 随机颜色映射 % 创建颜色-标签映射表 color_label_map = containers.Map(); for k = 1:num_labels % 获取标签k的像素位置 [r, c] = find(L == k, 1); % 取第一个像素位置 % 提取该位置的颜色 (转换为字符串作为Key) pixel_color = squeeze(RGB(r, c, :))'; % 获取RGB值 color_key = sprintf('%.4f_%.4f_%.4f', pixel_color); % 存储映射关系 color_label_map(color_key) = k; end % 查询示例 (假设某像素RGB=[0,0.5,1]) test_color = [0 0.5 1]; test_key = sprintf('%.4f_%.4f_%.4f', test_color); if isKey(color_label_map, test_key) fprintf('颜色[%.2f,%.2f,%.2f] → 标签%d\n',... test_color(1), test_color(2), test_color(3),... color_label_map(test_key)); end ``` ### 关键说明 1. **颜色映射原理**: - 标签 $k$ 对应 `map` 的第 $k$ 行颜色 - 标签 $0$ 背景色由 `zerocolor` 参数指定[^1][^2] ```matlab RGB = label2rgb(L, jet(4), [1 0 0]); % 红色背景 ``` 2. **随机顺序处理**: 使用 `'shuffle'` 时颜色随机分配: ```matlab RGB = label2rgb(L, map, 'k', 'shuffle'); % 随机映射 ``` 此时必须使用方法2重建映射关系 3. **数据类型转换**: 当需要精确匹配时进行归一化处理: ```matlab % 将uint8图像转为[0,1]范围 if isa(RGB, 'uint8') RGB_normalized = im2double(RGB); end ``` ### 可视化验证 ```matlab imshow(RGB); hold on; for k = 1:num_labels [r, c] = find(L == k, 1); text(c, r, num2str(k), 'Color', 'w',... 'FontWeight', 'bold', 'FontSize', 12); end ``` ![标签标注示意图](label_demo.png) > 此方法通过直接关联标签矩阵索引与颜色映射矩阵,可准确获取任意颜色对应的原始标签值,不受随机化参数影响[^1][^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值