Tuple and ref

本文介绍了Boost::tuple类,它类似std::pair但元素可为0 - 10个。阐述了其使用方法,如包含头文件、元素访问、引用类型使用、流输出、比较运算符重载等,还提及make_tuple、tie等辅助函数。此外,介绍了ref辅助函数返回的reference_wrapper类,可将值传递转为引用传递,用于构造引用语义的stl容器。

Boost::tuple是类似于std::pair的一个类。Pair有且只能有两个成员(firstsecond),而tuple的元素可以是0-10个。

使用tuple要包含boost/tuple/tuple.hpp文件。

例如:

#include<boost/tuple/tuple.hpp>

...

tuple<int,double,string> t1(1,2.0,”Hello”);

tuple<int,char> t2(1,’a’);

tuple<int,int> t3;//在这种情况下,t3的两个int值是为定义

tuple<int,int> t4(1);//只初始化第一个元素

tuple的元素使用get<N>函数或get<N>成员函数访问。例如:

tuple<int,double,string> t(1,2.0,”Hello”);

cout<<t.get<0>()<<endl;//输出t的第一个元素,即 1

t.get<1>()=2.0;//t的第二个元素赋值为2.0;

//以上都是使用get<N>成员函数,下面使用全局的get<N>函数

cout<<get<0>(t)<<endl;//

get<1>(t)=2.0;

tuple的元素还可以使用引用类型。例如:

int a;

tuple<&int,int> t(a,1);

t.get<0>()=123;

cout<<a<<endl;//这时应该输出123

 

tuple<&int> t1;//这是错的,引用类型一定要初始化

tuple<&int> t2(1);//这也是错的,不能使用常数初始化引用类型

tuple还可以使用流直接输出,但是一定要包含tuple_io.hpp文件。例如:

#include<boost/tuple/tuple_io.hpp>//tuple_io.hpp中已经包含tuple.hpp文件

tuple<int,string,double> t(2,”hello”,1.0);

cout<<t<<endl;//输出(2 hello 1.0)

//tuple默认使用’(‘开始,’)’结尾,空格分隔的方式输出。可以使用操纵器改变。

cout<<tuples::set_open('[')

     <<tuples::set_close(']')

     <<tuples::set_delimiter(',')

     <<t<<endl;//输出[2,hello,1.0]

tuple同样有==!=><等比较运算符的重载,大于小于的使用字典顺序比较。

pair一样,tuplemake_tuple辅助函数来构造tuple。例如:

cout<<make_tuple(1,1.0,”aaa”)<<endl;//构造了tuple<int,double,const char*>

如果要构造引用类型,要使用ref辅助函数。例如:

int a=123;

cout<<make_tuple(1,ref(a))<<endl;//构造了tuple<int,int&>

cout<<make_tuple(1,cref(a))<<endl;// 构造了tuple<int,const int&>

 

int a,b,c;

tuple<int&,int&,int&> t(a,b,c);

t=make_tuple(1,2,3);//这时a==1 b==2 c==3

对于这样的应用,可以使用更为方便的tie函数。例如:

int a,b,c;

tie(a,b,c)=make_tuple(1,2,3);

如果要其中的某几个元素,可以使用tuples::ignore代替不需要使用的部分。例如:

tie(a,tuples::ignore,c)=make_tuple(1,2,3);

tuple的性能:

     cout<<sizeof(tuple<>)<<endl;         //   1

     cout<<sizeof(tuple<int>)<<endl;      //   4

     cout<<sizeof(tuple<int,int>)<<endl;  //   8

     cout<<sizeof(tuple<int,char>)<<endl;//    8

     cout<<sizeof(tuple<double>)<<endl;   //   8

     cout<<sizeof(tuple<int,double>)<<endl;//16

tuple的访问,也是和pair具有相同的效率。

 

Ref

tuple中,我们使用到了refcref辅助函数,那么ref到底是什么呢?

ref(a),其实是返回了一个reference_wrapper<T>类,这个类保存着指向a的指针。

template<class T> class reference_wrapper

{

public:

    typedef T type;

 

#if defined(BOOST_MSVC) && (BOOST_MSVC < 1300)

    explicit reference_wrapper(T& t): t_(&t) {}

#else

    explicit reference_wrapper(T& t): t_(boost::addressof(t)) {}

#endif

 

    operator T& () const { return *t_; }

    T& get() const { return *t_; }

    T* get_pointer() const { return t_; }

 

private:

    T* t_;

};

以上就是reference_wraper的全部代码。其中operator T&()const返回的是*t_的应用,那么对于reference_wrapper的任何操作都会施加于*t_,通过reference_wrapper可以将某些值传递的参数转换成引用传递。例如:

#include<iostream>

#include<boost/ref.hpp>

using namespace std;

using namespace boost;

template<class T> void inc(T t)

{

     ++t;//由于reference_wrapper并没有重载++运算符,所有被隐式转换为int&,也就是对a的引用。

}

int main()

{

    int a=1;

     inc(ref(a));//构造reference_wrapper<int>

     cout<<a<<endl;//输出2

     return 0;

}

有了reference_wrapper,我们可以轻松构造引用语义的stl容器,注意vector<int&>是错误的哦。

     vector<reference_wrapper<int> > ref_vector;

     int a[10];

     for (int i=0;i<10;++i)

         ref_vector.push_back(ref(a[i]));

 


import cv2 import numpy as np from typing import Tuple, List, Optional def imread_chinese(path: str) -> Optional[np.ndarray]: """支持中文路径的图像读取""" try: image = cv2.imdecode(np.fromfile(path, dtype=np.uint8), cv2.IMREAD_COLOR) return image except Exception as e: print(f"读取图像失败 {path}: {e}") return None class OffsetROICalculator: """ 偏移值区域计算器 根据模板匹配中心点 + 参考样本,动态计算目标 ROI 区域 """ def __init__(self, template_path: str, ref_center: Tuple[int, int], ref_roi: Tuple[int, int, int, int]): """ 初始化计算器 :param template_path: 模板图像路径(如 '零.png') :param ref_center: (cx, cy) 参考匹配中心点 :param ref_roi: (x, y, w, h) 对应的目标区域(绝对坐标) """ self.template_path = template_path self.ref_center = ref_center self.ref_roi = ref_roi # (x, y, w, h) # 读取模板并获取尺寸 self.template = imread_chinese(template_path) if self.template is None: raise FileNotFoundError(f"无法加载模板图像: {template_path}") self.h, self.w = self.template.shape[:2] # 计算相对偏移量(基于模板左上角) old_x_tl = ref_center[0] - self.w // 2 old_y_tl = ref_center[1] - self.h // 2 self.rel_offset_x = ref_roi[0] - old_x_tl self.rel_offset_y = ref_roi[1] - old_y_tl print(f"[OffsetROICalculator] 初始化完成") print(f" 模板尺寸: {self.w}x{self.h}") print(f" 相对偏移: (+{self.rel_offset_x}, +{self.rel_offset_y})") def calculate_roi(self, center_point: Tuple[int, int]) -> Tuple[int, int, int, int]: """ 根据新的匹配中心点,计算目标 ROI :param center_point: (cx, cy) 新检测到的中心点 :return: (x, y, w, h) 目标区域 """ cx, cy = center_point cur_x_tl = cx - self.w // 2 cur_y_tl = cy - self.h // 2 target_x = cur_x_tl + self.rel_offset_x target_y = cur_y_tl + self.rel_offset_y return (target_x, target_y, self.ref_roi[2], self.ref_roi[3]) def calculate_rois(self, center_points: List[Tuple[int, int]]) -> List[Tuple[int, int, int, int]]: """ 批量计算多个匹配点的目标区域 :param center_points: 多个中心点列表 :return: 对应的 ROI 列表 """ return [self.calculate_roi(cp) for cp in center_points] def get_template_size(self) -> Tuple[int, int]: """返回模板宽高""" return self.w, self.h # ====================== # 使用示例 # ====================== if __name__ == "__main__": # 参数设置(请根据实际情况修改) TEMPLATE_PATH = r"D:\yunfe\手机悠悠\零.png" REF_CENTER = (159, 673) # 已知的一次匹配中心 REF_ROI = (180, 717, 96, 35) # 对应的目标区域 # 创建计算器 roi_calc = OffsetROICalculator( template_path=TEMPLATE_PATH, ref_center=REF_CENTER, ref_roi=REF_ROI ) # 新检测到的匹配点(例如从 matchTemplate 得到) new_centers = [(160, 674), (161, 675)] # 计算目标区域 results = roi_calc.calculate_rois(new_centers) for i, (center, roi) in enumerate(zip(new_centers, results)): print(f"匹配点 {i+1} {center} -> 目标区域: (x={roi[0]}, y={roi[1]}, w={roi[2]}, h={roi[3]})") 输出零的坐标
最新发布
11-09
import librosa import numpy as np import soundfile as sf import matplotlib.pyplot as plt from scipy.io import wavfile from scipy.spatial.distance import euclidean import pyaudio import wave import time import os from datetime import datetime import threading # 常量定义 FRAME_LENGTH = 2048 HOP_LENGTH = 512 SILENCE_THRESHOLD = 0.001 SEGMENT_DURATION = 0.25 # 秒 FMIN = librosa.note_to_hz('C3') FMAX = librosa.note_to_hz('C7') RECORD_FORMAT = pyaudio.paInt16 CHANNELS = 1 RATE = 44100 CHUNK = 1024 class RealTimePitchAnalyzer: def __init__(self, ref_audio_path): """初始化实时音高分析器""" self.ref_audio_path = ref_audio_path self.ref_audio, self.ref_sr = self.load_and_trim_audio(ref_audio_path) self.ref_duration = len(self.ref_audio) / self.ref_sr self.ref_pitches = self.extract_pitches(self.ref_audio, self.ref_sr) self.ref_notes = [librosa.hz_to_note(p) for p in self.ref_pitches] self.audio = pyaudio.PyAudio() self.is_recording = False self.frames = [] self.user_pitches = [] self.user_notes = [] self.similarity_score = 0 self.feedback = {} self.output_path = "" def load_and_trim_audio(self, file_path: str) -> tuple: try: y, sr = librosa.load(file_path, sr=None) energy = librosa.feature.rms(y=y, frame_length=FRAME_LENGTH, hop_length=HOP_LENGTH) # 寻找起始点 start_sample = 0 for e in energy[0]: if e > SILENCE_THRESHOLD: break start_sample += HOP_LENGTH # 寻找结束点 end_sample = len(y) for e in reversed(energy[0]): if e > SILENCE_THRESHOLD: break end_sample -= HOP_LENGTH return y[start_sample:end_sample], sr except Exception as e: raise RuntimeError(f"Error processing {file_path}") from e def extract_pitches(self, audio: np.ndarray, sr: int) -> list: segment_length = int(SEGMENT_DURATION * sr) pitches = [] for start in np.arange(0, len(audio), segment_length): end = min(start + segment_length, len(audio)) segment = audio[int(start):int(end)] pitch = librosa.yin(segment, fmin=FMIN, fmax=FMAX, sr=sr) pitches.append(pitch) # 数据清洗 processed = [] for arr in pitches: valid_values = arr[~np.isnan(arr)] if len(valid_values) == 0: continue mean = np.mean(valid_values) std = np.std(valid_values) filtered = valid_values[np.abs(valid_values - mean) < 2 * std] processed.append(np.mean(filtered) if len(filtered) > 0 else np.nan) return [p for p in processed if not np.isnan(p)] def calculate_similarity_score(self, audio1: np.ndarray, audio2: np.ndarray) -> float: """计算两个音频的MFCC相似度分数""" try: # 提取MFCC特征 mfcc1 = librosa.feature.mfcc(y=audio1, sr=self.ref_sr) mfcc2 = librosa.feature.mfcc(y=audio2, sr=self.ref_sr) # 计算相似度 distance = euclidean(np.mean(mfcc1.T, axis=0), np.mean(mfcc2.T, axis=0)) return max(0, 100 - (distance / 10)) except Exception as e: raise RuntimeError("Similarity calculation failed") from e def generate_feedback(self, user_notes: list) -> dict: """生成演唱反馈""" feedback = { "accuracy": 0, "differences": [], "total_notes": len(self.ref_notes) } min_length = min(len(self.ref_notes), len(user_notes)) correct = 0 for i in range(min_length): ref = self.ref_notes[i] user = user_notes[i] if ref == user: correct += 1 else: ref_hz = librosa.note_to_hz(ref) user_hz = librosa.note_to_hz(user) status = "唱高了" if ref_hz < user_hz else "唱低了" feedback["differences"].append({ "position": i + 1, "reference": ref, "user": user, "status": status }) feedback["accuracy"] = round(correct / min_length * 100, 2) if min_length > 0 else 0 return feedback def plot_waveforms(self): """绘制波形对比图""" plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 加载参考音频 ref_y, _ = librosa.load(self.ref_audio_path, sr=self.ref_sr) # 加载用户音频 user_y, user_sr = librosa.load(self.output_path, sr=self.ref_sr) min_length = min(len(ref_y), len(user_y)) time = np.linspace(0, min_length / self.ref_sr, min_length) plt.figure(figsize=(12, 6)) plt.plot(time, ref_y[:min_length], label='参考音频', alpha=0.7) plt.plot(time, user_y[:min_length], label='用户音频', alpha=0.7) plt.xlabel('时间 (秒)') plt.ylabel('振幅') plt.title('音频波形对比') plt.legend() plt.grid(True) plt.savefig('waveform_comparison.png') plt.close() print("波形对比图已保存为 waveform_comparison.png") def plot_pitch_comparison(self): """绘制音高对比图""" plt.figure(figsize=(12, 6)) # 绘制参考音高 ref_times = np.linspace(0, self.ref_duration, len(self.ref_pitches)) plt.plot(ref_times, self.ref_pitches, 'bo-', label='参考音高') # 绘制用户音高 user_times = np.linspace(0, self.ref_duration, len(self.user_pitches)) plt.plot(user_times, self.user_pitches, 'ro-', label='用户音高') plt.xlabel('时间 (秒)') plt.ylabel('频率 (Hz)') plt.title('音高对比') plt.legend() plt.grid(True) plt.savefig('pitch_comparison.png') plt.close() print("音高对比图已保存为 pitch_comparison.png") def start_recording(self): """开始实时录音""" self.is_recording = True self.frames = [] stream = self.audio.open(format=RECORD_FORMAT, channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=CHUNK) print(f"开始录音... 请演唱 {self.ref_duration:.1f} 秒") start_time = time.time() while self.is_recording and (time.time() - start_time) < self.ref_duration: data = stream.read(CHUNK) self.frames.append(data) stream.stop_stream() stream.close() # 保存录音 timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") self.output_path = f"user_recording_{timestamp}.wav" wf = wave.open(self.output_path, 'wb') wf.setnchannels(CHANNELS) wf.setsampwidth(self.audio.get_sample_size(RECORD_FORMAT)) wf.setframerate(RATE) wf.writeframes(b''.join(self.frames)) wf.close() print(f"录音已保存为 {self.output_path}") return self.output_path def stop_recording(self): """停止录音""" self.is_recording = False def analyze_performance(self): """分析用户演唱表现""" if not self.output_path: print("没有可分析的录音文件") return # 加载并处理用户音频 user_audio, user_sr = self.load_and_trim_audio(self.output_path) stretched_audio = librosa.effects.time_stretch(user_audio, rate=(len(user_audio) / len(self.ref_audio))) # 保存处理后的音频 processed_path = f"processed_{self.output_path}" sf.write(processed_path, stretched_audio, user_sr) # 提取用户音高 self.user_pitches = self.extract_pitches(stretched_audio, user_sr) self.user_notes = [librosa.hz_to_note(p) for p in self.user_pitches] # 计算相似度 self.similarity_score = self.calculate_similarity_score(self.ref_audio, stretched_audio) # 生成反馈 self.feedback = self.generate_feedback(self.user_notes) # 生成图表 self.plot_waveforms() self.plot_pitch_comparison() return self.feedback, self.similarity_score def realtime_analysis_thread(self): """实时分析线程""" # 此处可以添加实时音高分析逻辑 # 由于实时音高分析需要复杂处理,这里仅作为占位 pass def run(self): """运行实时演唱对比""" print("准备开始实时演唱对比...") print(f"参考歌曲时长: {self.ref_duration:.1f}秒") # 启动录音 recording_thread = threading.Thread(target=self.start_recording) recording_thread.start() # 等待录音完成 recording_thread.join() # 分析演唱 feedback, similarity = self.analyze_performance() # 输出结果 print(f"\n标准音符: {self.ref_notes[:10]}... (共{len(self.ref_notes)}个音符)") print(f"用户音符: {self.user_notes[:10]}... (共{len(self.user_notes)}个音符)") print(f"\n准确率: {feedback['accuracy']}%") print(f"相似度评分: {similarity:.1f}/100") if feedback["differences"]: print("\n需要改进的段落:") for diff in feedback["differences"][:5]: # 只显示前5个差异 print(f"第{diff['position']}段: 标准 {diff['reference']} → 用户 {diff['user']} ({diff['status']})") if len(feedback["differences"]) > 5: print(f"... 还有{len(feedback['differences']) - 5}处差异未显示") print("\n分析完成!图表已保存到当前目录") def __del__(self): """清理资源""" self.audio.terminate() if __name__ == "__main__": # 使用示例 analyzer = RealTimePitchAnalyzer('do-doredo.wav') # 替换为你的参考音频路径 analyzer.run()这段代码那里缩进的不对
08-03
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值