三维点云数据分类与聚类算法对比(附Kmeans应用图像色彩压缩)

数据分类与聚类算法对比

任务描述:

  1. 生成模拟聚类数据。
  2. 调用不同的聚类算法,包括自定义的 K-Means 和 GMM 算法,以及 sklearn 中自带的算法,进行聚类提取。
  3. 在同一张图片中显示聚类结果和算法运行时间,对比不同聚类算法的效果和效率。

算法描述:

数据分类任务通常包括使用聚类算法将数据点分组。聚类算法旨在将数据划分为不同的组(簇),使得同一组中的数据点尽可能相似,而不同组之间的数据点差异较大。常用的聚类算法有 K-Means、Gaussian Mixture Models(GMM)、Mean Shift、DBSCAN、Agglomerative Clustering 等。

  • K-Means 聚类:K-Means 是一种迭代的聚类方法,通过最小化每个点到其最近聚类中心的距离来实现数据点的分配。其主要步骤是随机选择 K 个初始聚类中心,然后进行迭代更新,直到聚类结果收敛。
  • Gaussian Mixture Models(GMM):GMM 是基于概率模型的聚类方法,它假设数据点是从多个高斯分布中生成的。GMM 的优势是能够处理不规则形状的簇。
  • Mean Shift:Mean Shift 是一种基于密度的聚类算法,自动发现数据的密集区域,并将数据点分配到这些区域中。它不需要预先指定簇的数量。
  • DBSCAN:基于密度的空间聚类算法,可以发现任意形状的簇,并且不需要指定簇的数量。它通过指定最小样本数和半径来识别密度较高的区域。

具体步骤:

  1. 数据生成:首先,使用 sklearn.datasets 生成多种模拟数据集,如圆形、月亮形状、气泡等数据集。
  2. 聚类算法初始化:定义多个聚类算法,包括自定义的 K-Means 和 GMM 算法,以及 sklearn 提供的 Mean Shift、MiniBatchKMeans 等。
  3. 聚类执行:对每个数据集应用所有聚类算法,记录聚类结果。
  4. 结果展示与对比:在同一张图中绘制聚类结果,并显示每个算法的运行时间。

代码

class K_Means(object):
    # k是分组数;tolerance‘中心点误差’;max_iter是迭代次数
    def __init__(self, n_clusters=2, tolerance=0.0001, max_iter=300):
        self.k_ = n_clusters
        self.tolerance_ = tolerance
        self.max_iter_ = max_iter

    def fit(self, data):
        # 作业1
        # 从所有数据点钟随机选择K个点作为初始聚类中心
        centers = data[random.sample(range(data.shape[0]),self.k_)]
        old_centers = np.copy(centers)
        # K 个列表,用于存储属于第K个聚类的数据点索引
        labels = [ []for i in range(self.k_) ]
        for iter_ in range(self.max_iter_):
            # E step
            for idx, point in enumerate(data):
                diff = np.linalg.norm(old_centers - point, axis=1)
                labels[np.argmin(diff)].append(idx)
            # M step
            for i in range(self.k_):
                points = data[labels[i], :]
                centers[i] = points.mean(axis=0)
            if np.sum(np.abs(centers - old_centers)) < self.tolerance_ + self.k_:
                break
            old_centers = np.copy(centers)
        self.centers = centers
        self.fitted = True

    def predict(self, p_datas):
        result = []
        # 作业2
        if not self.fitted:
            print('Unfitted.')
            return result
        for point in p_datas:
            diff = np.linalg.norm(self.centers - point, axis=1)
            result.append(np.argmin(diff))
        return result

完整程序


# 文件功能:
# 1. 生成模拟聚类数据
# 2. 调用聚类算法,包括自编的K-Means、GMM,以及sklearn中自带的算法,进行聚类提取
# 3. 在同一张图片中显示聚类结果和算法运行时间,对比查看

import time
import warnings

import numpy as np
import matplotlib.pyplot as plt

from sklearn import cluster, datasets, mixture
from sklearn.neighbors import kneighbors_graph
from sklearn.preprocessing import StandardScaler
from itertools import cycle, islice

from KMeans import K_Means
from GMM import GMM

np.random.seed(0)

# ============
# 模拟原始数据
# ============
print('start generate datasets ...')
n_samples = 1500
noisy_circles = datasets.make_circles(n_samples=n_samples, factor=.5,
                                      noise=.05)
noisy_moons = datasets.make_moons(n_samples=n_samples, noise=.05)
blobs = datasets.make_blobs(n_samples=n_samples, random_state=8)
no_structure = np.random.rand(n_samples, 2), None

# Anisotropicly distributed data
random_state = 170
X, y = datasets.make_blobs(n_samples=n_samples, random_state=random_state)
transformation = [[0.6, -0.6], [-0.4, 0.8]]
X_aniso = np.dot(X, transformation)
aniso = (X_aniso, y)

# blobs with varied variances
varied = datasets.make_blobs(n_samples=n_samples,
                             cluster_std=[1.0, 2.5, 0.5],
                             random_state=random_state)
print('datasets generated over')
# ============
# 设置聚类算法参数
# ============
plt.figure(figsize=(9 * 2 + 3, 12.5))
plt.subplots_adjust(left=.02, right=.98, bottom=.001, top=.96, wspace=.05,
                    hspace=.01)

plot_num = 1

default_base = {'quantile': .3,
                'eps': .3,
                'damping': .9,
                'preference': -200,
                'n_neighbors': 10,
                'n_clusters': 3,
                'min_samples': 20,
                'xi': 0.05,
                'min_cluster_size': 0.1}

datasets = [
    (noisy_circles, {'damping': .77, 'preference': -240,
                     'quantile': .2, 'n_clusters': 2,
                     'min_samples': 20, 'xi': 0.25}),
    (noisy_moons, {'damping': .75, 'preference': -220, 'n_clusters': 2}),
    (varied, {'eps': .18, 'n_neighbors': 2,
              'min_samples': 5, 'xi': 0.035, 'min_cluster_size': .2}),
    (aniso, {'eps': .15, 'n_neighbors': 2,
             'min_samples': 20, 'xi': 0.1, 'min_cluster_size': .2}),
    (blobs, {}),
    (no_structure, {})]


# 一共两层循环,实现每个仿真数据被每种聚类算法调用以此
# 此处是外层循环,遍历所有仿真数据
for i_dataset, (dataset, algo_params) in enumerate(datasets):
    # update parameters with dataset-specific values
    params = default_base.copy()
    params.update(algo_params)
    print('start deal dataset_' + str(i_dataset) + '...')
    print('dataset params is:')
    print(params)

    X, y = dataset

    # normalize dataset for easier parameter selection
    X = StandardScaler().fit_transform(X)

    # estimate bandwidth for mean shift
    bandwidth = cluster.estimate_bandwidth(X, quantile=params['quantile'])

    # connectivity matrix for structured Ward
    connectivity = kneighbors_graph(
        X, n_neighbors=params['n_neighbors'], include_self=False)
    # make connectivity symmetric
    connectivity = 0.5 * (connectivity + connectivity.T)

    # ============
    # 初始化所有聚类算法
    # ============
    # 自编的K-Means、GMM算法
    my_kmeans = K_Means(n_clusters=params['n_clusters'])
    my_gmm = GMM(n_clusters=params['n_clusters'])
    # sklearn中自带的算法
    ms = cluster.MeanShift(bandwidth=bandwidth, bin_seeding=True)
    two_means = cluster.MiniBatchKMeans(n_clusters=params['n_clusters'])
    ward = cluster.AgglomerativeClustering(
        n_clusters=params['n_clusters'], linkage='ward',
        connectivity=connectivity)
    spectral = cluster.SpectralClustering(
        n_clusters=params['n_clusters'], eigen_solver='arpack',
        affinity="nearest_neighbors")
    dbscan = cluster.DBSCAN(eps=params['eps'])
    optics = cluster.OPTICS(min_samples=params['min_samples'],
                            xi=params['xi'],
                            min_cluster_size=params['min_cluster_size'])
    affinity_propagation = cluster.AffinityPropagation(
        damping=params['damping'], preference=params['preference'])
    average_linkage = cluster.AgglomerativeClustering(
        linkage="average", metric="cityblock",
        n_clusters=params['n_clusters'], connectivity=connectivity)
    birch = cluster.Birch(n_clusters=params['n_clusters'])
    gmm = mixture.GaussianMixture(
        n_components=params['n_clusters'], covariance_type='full')
    
    clustering_algorithms = (
        ('My_KMeans', my_kmeans),
        ('My_GMM', my_gmm),
        ('MiniBatchKMeans', two_means),
        ('AffinityPropagation', affinity_propagation),
        ('MeanShift', ms),
        ('SpectralClustering', spectral),
        ('Ward', ward),
        ('AgglomerativeClustering', average_linkage),
        ('DBSCAN', dbscan),
        ('OPTICS', optics),
        ('Birch', birch),
        ('GaussianMixture', gmm)
    )

    # 此处是内层循环,遍历每种算法
    for name, algorithm in clustering_algorithms:
        t0 = time.time()

        print('clustering with algorithm', name, '...')
        # catch warnings related to kneighbors_graph
        with warnings.catch_warnings():
            warnings.filterwarnings(
                "ignore",
                message="the number of connected components of the " +
                "connectivity matrix is [0-9]{1,2}" +
                " > 1. Completing it to avoid stopping the tree early.",
                category=UserWarning)
            warnings.filterwarnings(
                "ignore",
                message="Graph is not fully connected, spectral embedding" +
                " may not work as expected.",
                category=UserWarning)
            algorithm.fit(X)

        t1 = time.time()
        if hasattr(algorithm, 'labels_'):
            y_pred = algorithm.labels_.astype(int)
        else:
            y_pred = algorithm.predict(X)

        plt.subplot(len(datasets), len(clustering_algorithms), plot_num)
        if i_dataset == 0:
            plt.title(name, size=7)

        colors = np.array(list(islice(cycle(['#377eb8', '#ff7f00', '#4daf4a',
                                             '#f781bf', '#a65628', '#984ea3',
                                             '#999999', '#e41a1c', '#dede00']),
                                      int(max(y_pred) + 1))))
        # add black color for outliers (if any)
        colors = np.append(colors, ["#000000"])
        plt.scatter(X[:, 0], X[:, 1], s=10, color=colors[y_pred])

        plt.xlim(-2.5, 2.5)
        plt.ylim(-2.5, 2.5)
        plt.xticks(())
        plt.yticks(())
        plt.text(.99, .01, ('%.2fs' % (t1 - t0)).lstrip('0'),
                 transform=plt.gca().transAxes, size=15,
                 horizontalalignment='right')
        plot_num += 1

plt.show()

结果

请添加图片描述

聚类算法优缺点对比

不同的聚类算法在处理不同类型的数据时具有不同的表现。以下是几种常见聚类算法的优缺点对比,帮助你根据数据特性选择合适的算法。

  1. K-Means 聚类
  • 算法流程:
    K-Means 算法通过随机选择 K 个初始聚类中心,然后迭代地分配数据点到最近的聚类中心,更新聚类中心的位置,直到收敛为止。算法的核心步骤包括:
    1. 随机选择 K 个聚类中心。
    2. 对每个数据点,计算其到所有聚类中心的距离,并将其分配给最近的聚类中心。
    3. 计算每个簇内所有点的均值,并将其作为新的聚类中心。
    4. 重复上述过程,直到聚类中心不再发生变化或达到最大迭代次数。
  • 优点:
    • 效率高:K-Means 算法简单且计算效率较高,适用于大规模数据集。
    • 易于理解和实现:算法直观、易于实现,适合初学者使用。
    • 适用于球形簇:K-Means 假设簇是球形的,适合处理数据集中的簇具有类似形状的情况。
  • 缺点:
    • 对初始化敏感:K-Means 算法对初始聚类中心的选择非常敏感,可能会陷入局部最优解。常用的改进方法是使用 K-Means++ 来更智能地初始化中心。
    • 需要预设聚类数目:K-Means 需要提前指定簇的数量 k,但在实际应用中,很难确定最合适的 k 值。
    • 不适应非球形簇:对于形状不规则的簇(如椭圆形簇),K-Means 的效果较差。
    • 对噪声和离群点敏感:离群点会影响簇的形成,尤其是对于高维数据。
  1. Gaussian Mixture Models(GMM)
  • 算法流程:
    GMM 是基于概率的聚类算法,假设数据是由多个高斯分布(高斯混合模型)生成的。算法的核心步骤包括:
    1. 初始化各个高斯分布的均值、协方差和权重。
    2. 通过 EM 算法(期望最大化)迭代地优化高斯分布的参数。
    3. E步:计算每个点属于各个高斯分布的概率。
    4. M步:根据计算得到的概率,更新高斯分布的均值、协方差和权重。
  • 优点:
    • 适应复杂簇形状:GMM 通过拟合多个高斯分布,适用于形状不规则的簇。
    • 软聚类:GMM 是基于概率的聚类方法,提供了每个点属于每个簇的概率(软标签),相比硬聚类方法(如 K-Means),能提供更多的信息。
    • 自动估计簇的数量:通过 AIC/BIC(赤池信息准则/贝叶斯信息准则)等准则,GMM 可以自动估计最佳的簇数量。
  • 缺点:
    • 计算复杂度高:GMM 在训练时需要计算协方差矩阵,其计算复杂度比 K-Means 高得多,适用于数据集较小或中等规模的场景。
    • 对初始化敏感:与 K-Means 类似,GMM 也对初始参数(例如均值、协方差矩阵等)较为敏感,容易陷入局部最优解。
    • 假设数据符合高斯分布:GMM 假设簇是由高斯分布生成的,若簇形状较为复杂,可能不适用。
  1. DBSCAN(Density-Based Spatial Clustering of Applications with Noise)
  • 算法流程:
    DBSCAN 是一种基于密度的聚类算法,能够发现任意形状的簇。算法的核心步骤包括:
    1. 对每个点,计算其邻域内的点数(根据 eps 和 min_samples 参数)。
    2. 如果点的邻域内点数大于或等于 min_samples,则将其标记为核心点,开始扩展簇。
    3. 对于核心点的邻域内所有点进行扩展,形成一个簇。
    4. 对于密度不够的区域,标记为噪声点。
  • 优点:
    • 无需预设簇数:DBSCAN 不需要指定簇的数量,可以自动识别簇的数量。
    • 适应任意形状簇:DBSCAN 基于密度,可以识别任意形状的簇,不局限于球形或椭圆形。
    • 抗噪声:DBSCAN 能够自动识别并排除噪声点(即“离群点”),这使得它在处理有噪声的数据时表现良好。
  • 缺点:
    • 对参数敏感:DBSCAN 需要两个参数:eps(距离阈值)和 min_samples(最小样本数)。选择不当的参数可能导致聚类效果不佳。
    • 高维数据效果差:在高维数据中,DBSCAN 的表现可能较差,因为距离度量在高维空间中往往不再可靠(即“维度灾难”)。
    • 不适合处理大小差异较大的簇:对于簇大小差异较大的数据集,DBSCAN 可能无法很好地聚类。
    • 低密度连接会导致类合并:假设有两个明显分离的簇,簇 A 和簇 B,它们之间的点密度较低。假如 eps 参数设置较大,或者簇之间的边界区域恰好有一些点,这些点会被误认为是簇之间的桥梁。DBSCAN 会把这两个簇连接在一起,形成一个错误的聚类结果。(解决方法:缩小eps邻域半径,增大min_samples一个簇的最小点数;数据预处理时去除噪声)
  1. Mean Shift
  • 算法流程:
    Mean Shift 是基于密度的聚类算法,通过找到数据密度的“峰值”来进行聚类。算法的核心步骤包括:

    1. 对数据中的每个点,计算其邻域内的密度中心。
    2. 将每个点移动到其密度峰值方向,直到收敛。
    3. 聚类由密度峰值所在的区域组成,每个区域即为一个簇。
  • 优点:

    • 无需预设簇数:Mean Shift 是基于密度的算法,不需要指定簇的数量,能够根据数据分布自动确定簇数。
    • 适应任意形状簇:Mean Shift 可以发现任意形状的簇,适应性强。
    • 能处理密度变化的簇:适用于簇的密度变化较大的数据。
  • 缺点:

    • 计算复杂度高:Mean Shift 需要对数据集中的每个点进行密度估计,计算量较大,效率较低。
    • 对窗口大小敏感:Mean Shift 算法的效果依赖于窗口(带宽)大小,选择不当的窗口大小可能会导致结果不理想。
  1. Spectral Clustering
  • 算法流程:
    Spectral Clustering 是基于图论的聚类算法。它首先计算数据点之间的相似度矩阵,并使用图的拉普拉斯矩阵进行特征分解。核心步骤包括:

    1. 计算相似度矩阵,表示数据点之间的关系。
    2. 计算图的拉普拉斯矩阵,并进行特征分解。
    3. 选择前 K 个特征向量,并将数据点嵌入到新的特征空间中。
    4. 在嵌入空间中使用 K-Means 进行聚类。
  • 优点:

    • 适应复杂簇形状:Spectral Clustering 是通过图论的方法来进行聚类,能够处理复杂形状的簇,尤其适合非凸形状的数据。
    • 可以利用图的结构信息:通过构造相似度矩阵,Spectral Clustering 能够考虑数据点之间的全局关系,有时能找到一些基于局部结构的有趣模式。
  • 缺点:

    • 计算开销大:Spectral Clustering 的计算复杂度较高,特别是当数据集很大时,计算相似度矩阵的时间成本较高。
    • 需要设置相似度度量:如何定义数据点之间的相似度(例如,使用欧式距离或其他度量)对结果有较大影响。
  1. Agglomerative Clustering(层次聚类)
  • 算法流程:
    层次聚类通过逐步合并(或分割)簇来生成树状图(树形结构)。其核心步骤包括:

    1. 从每个数据点开始,每个数据点都属于一个独立的簇。
    2. 计算簇之间的距离,选择距离最小的两个簇合并。
    3. 重复以上过程,直到所有数据点都合并为一个簇。
  • 优点:

    • 无需预设簇数:层次聚类通过构建树形结构(树状图)来聚类,不需要预设簇数,可以根据树的高度选择合适的聚类数。
    • 适用于不规则簇形:能处理形状复杂的簇,尤其适用于数据的层次结构。
  • 缺点:

    • 计算复杂度较高:层次聚类需要计算所有点对的距离,计算复杂度较高,特别是当数据集很大时。
    • 对噪声敏感:层次聚类对数据中的噪声点比较敏感,可能会影响结果。
  1. Birch(Balanced Iterative Reducing and Clustering Using Hierarchies)
  • 算法流程:
    Birch 是一种高效的层次聚类算法,通过构建一个 CF(Clustering Feature)树来组织数据。核心步骤包括:
    1. 将数据分成多个簇,每个簇都有一个 CF 树节点。
    2. 逐步合并簇,直到满足停止条件。
  • 优点:
    • 高效处理大规模数据:Birch 适用于大规模数据集,通过构建 CF(Clustering Feature)树进行聚类,计算效率较高。
    • 能够处理不规则簇:通过对数据进行层次结构的划分,Birch 可以处理复杂形状的簇。
  • 缺点:
    • 需要预设簇数:Birch 需要事先指定簇的数量,可能无法自动选择最优簇数。
    • 对离群点敏感:Birch 在处理有噪声或离群点的数据时,表现不如其他聚类算法。

图像压缩

图像压缩——色彩压缩算法实现

任务描述:

本任务的目标是通过对图像进行色彩压缩,保留 K 个颜色来实现图像压缩。具体而言,使用 K-Means 聚类算法,将图像中的像素分成 K 个簇,并用每个簇的中心点(聚类中心)来代表该簇内的所有像素,从而减少图像的色彩种类,并实现图像压缩。

算法流程:

  1. 加载图像:读取输入的图像并将其转换为 RGB 格式。
  2. 数据预处理:将图像数据转换为二维数组,每行表示一个像素点的 RGB 值。
  3. K-Means 聚类:使用 K-Means 聚类算法将图像的所有像素分为 K 个簇,并计算每个簇的中心点(即色彩代表)。
  4. 重建图像:根据 K-Means 聚类的结果,使用每个簇的中心点替换该簇中的所有像素,从而得到压缩后的图像。
  5. 显示与保存图像:将原始图像和压缩后的图像展示在同一图表中,同时保存压缩后的图像。

具体步骤:

  1. 加载图像数据:读取输入图像,并将其转换为 RGB 格式,以便进行后续处理。
  2. 点云数据转换:将图像转换为一个二维数组,每个元素代表一个 RGB 值。
  3. K-Means 聚类处理:使用 K-Means 算法对图像的颜色进行聚类,将原图的颜色压缩为 K 个颜色。
  4. 图像重建:用聚类中心的颜色替换原图像中的像素,从而实现图像的色彩压缩。

核心代码

import os.path

import numpy as np
import cv2
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans



def load_image(image_path):
    img = cv2.imread(image_path)  # 读取图像
    if img is None:
        print("Error: Unable to load image. Check the file path or file format.")
        return None  # 图像加载失败
    img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # 转换为 RGB
    return img_rgb



# 2. 将图像数据转化为二维数组(每行代表一个像素的RGB值)
def reshape_image(img_rgb):
    pixels = img_rgb.reshape(-1, 3)  # 将图像数据从 (height, width, 3) 转为 (num_pixels, 3)
    return pixels


# 3. 使用 KMeans 对颜色进行聚类
def apply_kmeans(pixels, k=5):
    kmeans = KMeans(n_clusters=k)
    kmeans.fit(pixels)
    return kmeans


# 4. 根据聚类结果重建图像
def reconstruct_image(kmeans, pixels, img_shape):
    # 用聚类中心替换每个像素的颜色
    new_pixels = kmeans.cluster_centers_[kmeans.labels_]
    new_pixels = new_pixels.astype(np.uint8)
    # 将一维数据恢复为图像的原始形状
    new_img = new_pixels.reshape(img_shape)
    return new_img


# 5. 显示并保存图像
def show_image(img_rgb, new_imgs, k_list):
    # 获取原始图像的尺寸
    height, width, _ = img_rgb.shape

    # 自适应设置 figsize (根据图像的大小,自动设置图形窗口的大小)
    figsize = (width / 100, height / 100)  # 设置为每个像素约 1/100 英寸

    # 根据 k_list 的长度创建子图
    n_images = len(k_list) + 1  # 1 个是原始图像,剩下的是 KMeans 聚类后的图像
    fig, ax = plt.subplots(1, n_images, figsize=figsize)

    # 显示原始图像
    ax[0].imshow(img_rgb)
    ax[0].set_title('Original Image')
    ax[0].axis('off')  # 不显示坐标轴

    # 显示每个降维后的图像
    for i, new_img in enumerate(new_imgs):
        ax[i + 1].imshow(new_img)
        ax[i + 1].set_title(f'KMeans (k={k_list[i]})')
        ax[i + 1].axis('off')  # 不显示坐标轴

    # 自适应展示图像
    plt.tight_layout()  # 调整布局,避免图片显示重叠
    plt.show()


# 6. 主程序
def main(root_path, image_path, k):
    img_rgb = load_image(image_path)  # 读取图像
    pixels = reshape_image(img_rgb)  # 转换为二维像素数组

    img_name, img_extension = os.path.splitext(os.path.basename(image_path))
    new_imgs = []
    for i in k:
        new_img_name = f"{img_name}_k={i}{img_extension}"
        output_path = os.path.join(root_path, new_img_name)
        kmeans = apply_kmeans(pixels, i)  # 进行KMeans聚类
        new_img = reconstruct_image(kmeans, pixels, img_rgb.shape)  # 重建图像
        # 如果图像的深度不是 CV_8U(uint8),进行转换
        if new_img.dtype != np.uint8:
            # 将图像像素值限定在 [0, 255] 范围,并转换为 uint8 类型
            new_img = np.clip(new_img, 0, 255).astype(np.uint8)
        new_img_bgr = cv2.cvtColor(new_img, cv2.COLOR_RGB2BGR)
        new_imgs.append(new_img)
        cv2.imwrite(output_path, new_img_bgr, [int(cv2.IMWRITE_JPEG_QUALITY), 45])

    show_image(img_rgb, new_imgs, k)  # 显示图像


# 调用主程序
if __name__ == "__main__":
    image_path = os.path.join('E:\\', 'WY', 'Dataset', 'pic', 'child.jpeg')
    print("Image path:", image_path)  # 打印路径
    root_path = os.path.join('E:\\', 'WY', 'Dataset', 'pic')
    # 替换成你的图像路径
    k = [8,16,32]
    main(root_path, image_path, k)  # k=8 表示将图像的颜色降到8种颜色

注意点:

  1. cv2.imwrite(output_path, new_img_bgr, [int(cv2.IMWRITE_JPEG_QUALITY), 45]) 45用来调整压缩程序 过高会导致压缩图比原图存储更大
  2. opencv展示的是bgr 存储需要转rgb

结果展示

在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值