当前位置:   article > 正文

C/C++实现librosa音频处理库melspectrogram和mfcc_librosa c++

librosa c++

C/C++实现librosa音频处理库melspectrogram和mfcc

目录

C/C++实现librosa音频处理库melspectrogram和mfcc

1.项目结构

2.依赖环境

3.C++ librosa音频处理库实现

(1) 对齐读取音频文件

(2) 对齐melspectrogram

(3) 对齐MFCC

4.Demo运行

5.librosa库C++源码下载


深度学习语音处理中,经常要用到音频处理库librosa,奈何librosa目前仅有python版本;而语音识别算法开发中,经常要用到melspectrogram(Mel-spectrogram梅尔语谱图)和MFCC(梅尔频率倒谱系数)这些音频信息,因此需要实现C/C++版本melspectrogram和MFCC;网上已经存在很多版本的C/C++的melspectrogram和MFCC,但测试发现跟Python的librosa的处理结果存在很大差异;经过多次优化测试,本项目实现了C/C++版本的音频处理库librosa中load、melspectrogram和mfcc的功能,项目基本完整对齐Pyhon音频处理库librosa三个功能:

  • librosa.load:实现语音读取
  • librosa.feature.melspectrogram:实现计算梅尔语谱图melspectrogram
  • librosa.feature.mfcc:实现计算梅尔频率倒谱系数MFCC

【尊重原创,转载请注明出处】https://blog.csdn.net/guyuealian/article/details/132077896


1.项目结构


2.依赖环境

项目需要安装Python和C/C++相关的依赖包

Python依赖库,使用pip install即可

  1. numpy==1.16.3
  2. matplotlib==3.1.0
  3. Pillow==6.0.0
  4. easydict==1.9
  5. opencv-contrib-python==4.5.2.52
  6. opencv-python==4.5.1.48
  7. pandas==1.1.5
  8. PyYAML==5.3.1
  9. scikit-image==0.17.2
  10. scikit-learn==0.24.0
  11. scipy==1.5.4
  12. seaborn==0.11.2
  13. tqdm==4.55.1
  14. xmltodict==0.12.0
  15. pybaseutils==0.7.6
  16. librosa==0.8.1
  17. pyaudio==0.2.11
  18. pydub==0.23.1

C++依赖库,主要用到Eigen3和OpenCV


3.C++ librosa音频处理库实现

语音处理中常用的特征值: Mel频谱图(Mel Spectrogram)和Mel频率倒谱系数(Mel Frequency Cepstrum Coefficient, MFCC),参考文章:https://www.cnblogs.com/Ge-ronimo/p/17281385.html

(1) 对齐读取音频文件

Python中可使用librosa.load读取音频文件

data, sr = librosa.load(path, sr, mono)

Python实现读取音频文件:

  1. # -*-coding: utf-8 -*-
  2. import numpy as np
  3. import librosa
  4. def read_audio(audio_file, sr=16000, mono=True):
  5. """
  6. 默认将多声道音频文件转换为单声道,并返回一维数组;
  7. 如果你需要处理多声道音频文件,可以使用 mono=False,参数来保留所有声道,并返回二维数组。
  8. :param audio_file:
  9. :param sr: sampling rate
  10. :param mono: 设置为true是单通道,否则是双通道
  11. :return:
  12. """
  13. audio_data, sr = librosa.load(audio_file, sr=sr, mono=mono)
  14. audio_data = audio_data.T.reshape(-1)
  15. return audio_data, sr
  16. def print_vector(name, data):
  17. np.set_printoptions(precision=7, suppress=False)
  18. print("------------------------%s------------------------\n" % name)
  19. print("{}".format(data.tolist()))
  20. if __name__ == '__main__':
  21. sr = None
  22. audio_file = "data/data_s1.wav"
  23. data, sr = read_audio(audio_file, sr=sr, mono=False)
  24. print("sr = %d, data size=%d" % (sr, len(data)))
  25. print_vector("audio data", data)

 C/C++读取音频文件:需要根据音频的数据格式进行解码,参考:C语言解析wav文件格式 ,本项目已经实现C/C++版本的读取音频数据,可支持单声道和双声道音频数据(mono)

  1. /**
  2. * 读取音频文件,目前仅支持wav格式文件
  3. * @param filename wav格式文件
  4. * @param out 输出音频数据
  5. * @param sr 输出音频采样率
  6. * @param mono 设置为true是单通道,否则是双通道
  7. * @return
  8. */
  9. int read_audio(const char *filename, vector<float> &out, int *sr, bool mono = true);
  1. #include <iostream>
  2. #include <vector>
  3. #include <algorithm>
  4. #include "librosa/audio_utils.h"
  5. #include "librosa/librosa.h"
  6. using namespace std;
  7. int main() {
  8. int sr = -1;
  9. string audio_file = "../data/data_s1.wav";
  10. vector<float> data;
  11. int res = read_audio(audio_file.c_str(), data, &sr, false);
  12. if (res < 0) {
  13. printf("read wav file error: %s\n", audio_file.c_str());
  14. return -1;
  15. }
  16. printf("sr = %d, data size=%d\n", sr, data.size());
  17. print_vector("audio data", data);
  18. return 0;
  19. }

测试和对比Python和C++版本读取音频文件数据,经过多轮测试,二者的读取的音频数值差异已经很小,基本已经对齐python librosa库的librosa.load()函数 

数值对比
C++版本
Python版本

(2) 对齐Mel频谱图melspectrogram

关于melspectrogram梅尔频谱的相关原理,请参考基于梅尔频谱的音频信号分类识别(Pytorch)

Python的librosa库的提供了librosa.feature.melspectrogram()函数,返回一个二维数组,可以使用OpenCV显示该图像

  1. def librosa_feature_melspectrogram(y,
  2. sr=16000,
  3. n_mels=128,
  4. n_fft=2048,
  5. hop_length=256,
  6. win_length=None,
  7. window="hann",
  8. center=True,
  9. pad_mode="reflect",
  10. power=2.0,
  11. fmin=0.0,
  12. fmax=None,
  13. **kwargs):
  14. """
  15. 计算音频梅尔频谱图(Mel Spectrogram)
  16. :param y: 音频时间序列
  17. :param sr: 采样率
  18. :param n_mels: number of Mel bands to generate产生的梅尔带数
  19. :param n_fft: length of the FFT window FFT窗口的长度
  20. :param hop_length: number of samples between successive frames 帧移(相邻窗之间的距离)
  21. :param win_length: 窗口的长度为win_length,默认win_length = n_fft
  22. :param window:
  23. :param center: 如果为True,则填充信号y,以使帧 t以y [t * hop_length]为中心。
  24. 如果为False,则帧t从y [t * hop_length]开始
  25. :param pad_mode:
  26. :param power: 幅度谱的指数。例如1代表能量,2代表功率,等等
  27. :param fmin: 最低频率(Hz)
  28. :param fmax: 最高频率(以Hz为单位),如果为None,则使用fmax = sr / 2.0
  29. :param kwargs:
  30. :return: 返回Mel频谱shape=(n_mels,n_frames),n_mels是Mel频率的维度(频域),n_frames为时间帧长度(时域)
  31. """
  32. mel = librosa.feature.melspectrogram(y=y,
  33. sr=sr,
  34. S=None,
  35. n_mels=n_mels,
  36. n_fft=n_fft,
  37. hop_length=hop_length,
  38. win_length=win_length,
  39. window=window,
  40. center=center,
  41. pad_mode=pad_mode,
  42. power=power,
  43. fmin=fmin,
  44. fmax=fmax,
  45. **kwargs)
  46. return mel

根据Python版本的librosa.feature.melspectrogram(),项目实现了C++版本melspectrogram

  1. /***
  2. * compute mel spectrogram similar with librosa.feature.melspectrogram
  3. * @param x input audio signal
  4. * @param sr sample rate of 'x'
  5. * @param n_fft length of the FFT size
  6. * @param n_hop number of samples between successive frames
  7. * @param win window function. currently only supports 'hann'
  8. * @param center same as librosa
  9. * @param mode pad mode. support "reflect","symmetric","edge"
  10. * @param power exponent for the magnitude melspectrogram
  11. * @param n_mels number of mel bands
  12. * @param fmin lowest frequency (in Hz)
  13. * @param fmax highest frequency (in Hz)
  14. * @return mel spectrogram matrix
  15. */
  16. static std::vector <std::vector<float>> melspectrogram(std::vector<float> &x, int sr,
  17. int n_fft, int n_hop, const std::string &win, bool center,
  18. const std::string &mode,
  19. float power, int n_mels, int fmin, int fmax)

测试和对比Python和C++版本melspectrogram,二者的返回数值差异已经很小,其可视化的梅尔频谱图基本一致。

版本数值对比
C++版本

Python版本


(3) 对齐梅尔频率倒谱系数MFCC

Python版可使用librosa库的librosa.feature.mfcc实现MFCC(Mel-frequency cepstral coefficients)

  1. def librosa_feature_mfcc(y,
  2. sr=16000,
  3. n_mfcc=128,
  4. n_mels=128,
  5. n_fft=2048,
  6. hop_length=256,
  7. win_length=None,
  8. window="hann",
  9. center=True,
  10. pad_mode="reflect",
  11. power=2.0,
  12. fmin=0.0,
  13. fmax=None,
  14. dct_type=2,
  15. **kwargs):
  16. """
  17. 计算音频MFCC
  18. :param y: 音频时间序列
  19. :param sr: 采样率
  20. :param n_mfcc: number of MFCCs to return
  21. :param n_mels: number of Mel bands to generate产生的梅尔带数
  22. :param n_fft: length of the FFT window FFT窗口的长度
  23. :param hop_length: number of samples between successive frames 帧移(相邻窗之间的距离)
  24. :param win_length: 窗口的长度为win_length,默认win_length = n_fft
  25. :param window:
  26. :param center: 如果为True,则填充信号y,以使帧 t以y [t * hop_length]为中心。
  27. 如果为False,则帧t从y [t * hop_length]开始
  28. :param pad_mode:
  29. :param power: 幅度谱的指数。例如1代表能量,2代表功率,等等
  30. :param fmin: 最低频率(Hz)
  31. :param fmax: 最高频率(以Hz为单位),如果为None,则使用fmax = sr / 2.0
  32. :param kwargs:
  33. :return: 返回MFCC shape=(n_mfcc,n_frames)
  34. """
  35. # MFCC 梅尔频率倒谱系数
  36. mfcc = librosa.feature.mfcc(y=y,
  37. sr=sr,
  38. S=None,
  39. n_mfcc=n_mfcc,
  40. n_mels=n_mels,
  41. n_fft=n_fft,
  42. hop_length=hop_length,
  43. win_length=win_length,
  44. window=window,
  45. center=center,
  46. pad_mode=pad_mode,
  47. power=power,
  48. fmin=fmin,
  49. fmax=fmax,
  50. dct_type=dct_type,
  51. **kwargs)
  52. return mfcc

根据Python版本的librosa.feature.mfcc(),项目实现了C++版本MFCC 

  1. /***
  2. * compute mfcc similar with librosa.feature.mfcc
  3. * @param x input audio signal
  4. * @param sr sample rate of 'x'
  5. * @param n_fft length of the FFT size
  6. * @param n_hop number of samples between successive frames
  7. * @param win window function. currently only supports 'hann'
  8. * @param center same as librosa
  9. * @param mode pad mode. support "reflect","symmetric","edge"
  10. * @param power exponent for the magnitude melspectrogram
  11. * @param n_mels number of mel bands
  12. * @param fmin lowest frequency (in Hz)
  13. * @param fmax highest frequency (in Hz)
  14. * @param n_mfcc number of mfccs
  15. * @param norm ortho-normal dct basis
  16. * @param type dct type. currently only supports 'type-II'
  17. * @return mfcc matrix
  18. */
  19. static std::vector<std::vector<float>> mfcc(std::vector<float> &x, int sr,
  20. int n_fft, int n_hop, const std::string &win, bool center, const std::string &mode,
  21. float power, int n_mels, int fmin, int fmax,
  22. int n_mfcc, bool norm, int type)

测试和对比Python和C++版本MFCC,二者的返回数值差异已经很小,其可视化的MFCC图基本一致。 

版本数值对比
C++版本

 ​​​

Python版本


4.Demo运行

  • C++版本,可在项目根目录,终端输入:bash build.sh ,即可运行测试demo
  1. #!/usr/bin/env bash
  2. if [ ! -d "build/" ];then
  3. mkdir "build"
  4. else
  5. echo "exist build"
  6. fi
  7. cd build
  8. cmake ..
  9. make -j4
  10. sleep 1
  11. ./main

main函数

  1. /****
  2. * @Author : 390737991@qq.com
  3. * @E-mail :
  4. * @Date :
  5. * @Brief : C/C++实现Melspectrogram和MFCC
  6. */
  7. #include <iostream>
  8. #include <vector>
  9. #include <algorithm>
  10. #include "librosa/audio_utils.h"
  11. #include "librosa/librosa.h"
  12. #include "librosa/cv_utils.h"
  13. using namespace std;
  14. int main() {
  15. int sr = -1;
  16. int n_fft = 400;
  17. int hop_length = 160;
  18. int n_mel = 64;
  19. int fmin = 80;
  20. int fmax = 7600;
  21. int n_mfcc = 64;
  22. int dct_type = 2;
  23. float power = 2.f;
  24. bool center = false;
  25. bool norm = true;
  26. string window = "hann";
  27. string pad_mode = "reflect";
  28. //string audio_file = "../data/data_d2.wav";
  29. string audio_file = "../data/data_s1.wav";
  30. vector<float> data;
  31. int res = read_audio(audio_file.c_str(), data, &sr, false);
  32. if (res < 0) {
  33. printf("read wav file error: %s\n", audio_file.c_str());
  34. return -1;
  35. }
  36. printf("n_fft = %d\n", n_fft);
  37. printf("n_mel = %d\n", n_mel);
  38. printf("hop_length = %d\n", hop_length);
  39. printf("fmin, fmax = (%d,%d)\n", fmin, fmax);
  40. printf("sr = %d, data size=%d\n", sr, data.size());
  41. //print_vector("audio data", data);
  42. // compute mel Melspectrogram
  43. vector<vector<float>> mels_feature = librosa::Feature::melspectrogram(data, sr, n_fft, hop_length, window,
  44. center, pad_mode, power, n_mel, fmin, fmax);
  45. int mels_w = (int) mels_feature.size();
  46. int mels_h = (int) mels_feature[0].size();
  47. cv::Mat mels_image = vector2mat<float>(get_vector(mels_feature), 1, mels_h);
  48. print_feature("mels_feature", mels_feature);
  49. printf("mels_feature size(n_frames,n_mels)=(%d,%d)\n", mels_w, mels_h);
  50. image_show("mels_feature(C++)", mels_image, 10);
  51. // compute MFCC
  52. vector<vector<float>> mfcc_feature = librosa::Feature::mfcc(data, sr, n_fft, hop_length, window, center, pad_mode,
  53. power, n_mel, fmin, fmax, n_mfcc, norm, dct_type);
  54. int mfcc_w = (int) mfcc_feature.size();
  55. int mfcc_h = (int) mfcc_feature[0].size();
  56. cv::Mat mfcc_image = vector2mat<float>(get_vector(mfcc_feature), 1, mfcc_h);
  57. print_feature("mfcc_feature", mfcc_feature);
  58. printf("mfcc_feature size(n_frames,n_mfcc)=(%d,%d)\n", mfcc_w, mfcc_h);
  59. image_show("mfcc_feature(C++)", mfcc_image, 10);
  60. cv::waitKey(0);
  61. printf("finish...");
  62. return 0;
  63. }
  • Python版本,可在项目根目录,终端输入:python main.py ,即可运行测试demo
  1. # -*-coding: utf-8 -*-
  2. """
  3. @Author :
  4. @E-mail :
  5. @Date : 2023-08-01 22:27:56
  6. @Brief :
  7. """
  8. import cv2
  9. import numpy as np
  10. import librosa
  11. def cv_show_image(title, image, use_rgb=False, delay=0):
  12. """
  13. 调用OpenCV显示图片
  14. :param title: 图像标题
  15. :param image: 输入是否是RGB图像
  16. :param use_rgb: True:输入image是RGB的图像, False:返输入image是BGR格式的图像
  17. :param delay: delay=0表示暂停,delay>0表示延时delay毫米
  18. :return:
  19. """
  20. img = image.copy()
  21. if img.shape[-1] == 3 and use_rgb:
  22. img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) # 将BGR转为RGB
  23. # cv2.namedWindow(title, flags=cv2.WINDOW_AUTOSIZE)
  24. cv2.namedWindow(title, flags=cv2.WINDOW_NORMAL)
  25. cv2.imshow(title, img)
  26. cv2.waitKey(delay)
  27. return img
  28. def librosa_feature_melspectrogram(y,
  29. sr=16000,
  30. n_mels=128,
  31. n_fft=2048,
  32. hop_length=256,
  33. win_length=None,
  34. window="hann",
  35. center=True,
  36. pad_mode="reflect",
  37. power=2.0,
  38. fmin=0.0,
  39. fmax=None,
  40. **kwargs):
  41. """
  42. 计算音频梅尔频谱图(Mel Spectrogram)
  43. :param y: 音频时间序列
  44. :param sr: 采样率
  45. :param n_mels: number of Mel bands to generate产生的梅尔带数
  46. :param n_fft: length of the FFT window FFT窗口的长度
  47. :param hop_length: number of samples between successive frames 帧移(相邻窗之间的距离)
  48. :param win_length: 窗口的长度为win_length,默认win_length = n_fft
  49. :param window:
  50. :param center: 如果为True,则填充信号y,以使帧 t以y [t * hop_length]为中心。
  51. 如果为False,则帧t从y [t * hop_length]开始
  52. :param pad_mode:
  53. :param power: 幅度谱的指数。例如1代表能量,2代表功率,等等
  54. :param fmin: 最低频率(Hz)
  55. :param fmax: 最高频率(以Hz为单位),如果为None,则使用fmax = sr / 2.0
  56. :param kwargs:
  57. :return: 返回Mel频谱shape=(n_mels,n_frames),n_mels是Mel频率的维度(频域),n_frames为时间帧长度(时域)
  58. """
  59. mel = librosa.feature.melspectrogram(y=y,
  60. sr=sr,
  61. S=None,
  62. n_mels=n_mels,
  63. n_fft=n_fft,
  64. hop_length=hop_length,
  65. win_length=win_length,
  66. window=window,
  67. center=center,
  68. pad_mode=pad_mode,
  69. power=power,
  70. fmin=fmin,
  71. fmax=fmax,
  72. **kwargs)
  73. return mel
  74. def librosa_feature_mfcc(y,
  75. sr=16000,
  76. n_mfcc=128,
  77. n_mels=128,
  78. n_fft=2048,
  79. hop_length=256,
  80. win_length=None,
  81. window="hann",
  82. center=True,
  83. pad_mode="reflect",
  84. power=2.0,
  85. fmin=0.0,
  86. fmax=None,
  87. dct_type=2,
  88. **kwargs):
  89. """
  90. 计算音频MFCC
  91. :param y: 音频时间序列
  92. :param sr: 采样率
  93. :param n_mfcc: number of MFCCs to return
  94. :param n_mels: number of Mel bands to generate产生的梅尔带数
  95. :param n_fft: length of the FFT window FFT窗口的长度
  96. :param hop_length: number of samples between successive frames 帧移(相邻窗之间的距离)
  97. :param win_length: 窗口的长度为win_length,默认win_length = n_fft
  98. :param window:
  99. :param center: 如果为True,则填充信号y,以使帧 t以y [t * hop_length]为中心。
  100. 如果为False,则帧t从y [t * hop_length]开始
  101. :param pad_mode:
  102. :param power: 幅度谱的指数。例如1代表能量,2代表功率,等等
  103. :param fmin: 最低频率(Hz)
  104. :param fmax: 最高频率(以Hz为单位),如果为None,则使用fmax = sr / 2.0
  105. :param kwargs:
  106. :return: 返回MFCC shape=(n_mfcc,n_frames)
  107. """
  108. # MFCC 梅尔频率倒谱系数
  109. mfcc = librosa.feature.mfcc(y=y,
  110. sr=sr,
  111. S=None,
  112. n_mfcc=n_mfcc,
  113. n_mels=n_mels,
  114. n_fft=n_fft,
  115. hop_length=hop_length,
  116. win_length=win_length,
  117. window=window,
  118. center=center,
  119. pad_mode=pad_mode,
  120. power=power,
  121. fmin=fmin,
  122. fmax=fmax,
  123. dct_type=dct_type,
  124. **kwargs)
  125. return mfcc
  126. def read_audio(audio_file, sr=16000, mono=True):
  127. """
  128. 默认将多声道音频文件转换为单声道,并返回一维数组;
  129. 如果你需要处理多声道音频文件,可以使用 mono=False,参数来保留所有声道,并返回二维数组。
  130. :param audio_file:
  131. :param sr: sampling rate
  132. :param mono: 设置为true是单通道,否则是双通道
  133. :return:
  134. """
  135. audio_data, sr = librosa.load(audio_file, sr=sr, mono=mono)
  136. audio_data = audio_data.T.reshape(-1)
  137. return audio_data, sr
  138. def print_feature(name, feature):
  139. h, w = feature.shape[:2]
  140. np.set_printoptions(precision=7, suppress=True, linewidth=(11 + 3) * w)
  141. print("------------------------{}------------------------".format(name))
  142. for i in range(w):
  143. v = feature[:, i].reshape(-1)
  144. print("data[{:0=3d},:]={}".format(i, v))
  145. def print_vector(name, data):
  146. np.set_printoptions(precision=7, suppress=False)
  147. print("------------------------%s------------------------\n" % name)
  148. print("{}".format(data.tolist()))
  149. if __name__ == '__main__':
  150. sr = None
  151. n_fft = 400
  152. hop_length = 160
  153. n_mel = 64
  154. fmin = 80
  155. fmax = 7600
  156. n_mfcc = 64
  157. dct_type = 2
  158. power = 2.0
  159. center = False
  160. norm = True
  161. window = "hann"
  162. pad_mode = "reflect"
  163. audio_file = "data/data_s1.wav"
  164. data, sr = read_audio(audio_file, sr=sr, mono=False)
  165. print("n_fft = %d" % n_fft)
  166. print("n_mel = %d" % n_mel)
  167. print("hop_length = %d" % hop_length)
  168. print("fmin, fmax = (%d,%d)" % (fmin, fmax))
  169. print("sr = %d, data size=%d" % (sr, len(data)))
  170. # print_vector("audio data", data)
  171. mels_feature = librosa_feature_melspectrogram(y=data,
  172. sr=sr,
  173. n_mels=n_mel,
  174. n_fft=n_fft,
  175. hop_length=hop_length,
  176. win_length=None,
  177. fmin=fmin,
  178. fmax=fmax,
  179. window=window,
  180. center=center,
  181. pad_mode=pad_mode,
  182. power=power)
  183. print_feature("mels_feature", mels_feature)
  184. print("mels_feature size(n_frames,n_mels)=({},{})".format(mels_feature.shape[1], mels_feature.shape[0]))
  185. cv_show_image("mels_feature(Python)", mels_feature, delay=10)
  186. mfcc_feature = librosa_feature_mfcc(y=data,
  187. sr=sr,
  188. n_mfcc=n_mfcc,
  189. n_mels=n_mel,
  190. n_fft=n_fft,
  191. hop_length=hop_length,
  192. win_length=None,
  193. fmin=fmin,
  194. fmax=fmax,
  195. window=window,
  196. center=center,
  197. pad_mode=pad_mode,
  198. power=power,
  199. dct_type=dct_type)
  200. print_feature("mfcc_feature", mfcc_feature)
  201. print("mfcc_feature size(n_frames,n_mfcc)=({},{})".format(mfcc_feature.shape[1], mfcc_feature.shape[0]))
  202. cv_show_image("mfcc_feature(Python)", mfcc_feature, delay=10)
  203. cv2.waitKey(0)

5.librosa库C++源码下载

C/C++实现librosa音频处理库melspectrogram和mfcc项目代码下载地址:C/C++实现librosa音频处理库melspectrogram和mfcc

项目源码内容包含:

  1. 提供C++版的read_audio()函数读取音频文件,目前仅支持wav格式文件,支持单/双声道音频读取
  2. 提供C++版的librosa::Feature::melspectrogram(),实现melspectrogram功能
  3. 提供C++版的librosa::Feature::mfcc(),实现MFCC功能
  4. 提供OpenCV图谱显示方式
  5. 项目demo自带测试数据,编译build完成后,即可运行
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/655372
推荐阅读
相关标签
  

闽ICP备14008679号