当前位置:   article > 正文

【完结】cyのMemo(20240209~20240312)

【完结】cyのMemo(20240209~20240312)

序言

除夕,年夜饭回来后特别累(赶早,下午又一路十公里跑回老家),把明天要看的东西准备好,然后糊完手头的日记本最后一页(糊了一堆“烦”字),准备封存起来,找本新的软抄就睡觉了,即兴又想翻翻之前的本子,从十二年前开始,初中狂妄羞耻的中二魂,每天还都起个特别二的标题,什么《闭关修炼!黑色星期五!双重创击!XXX称霸扬州》《迈向破灭的镇魂曲—巅峰对决—败绩—病倒》《自陷—精神暗杀》,到高中群雄并起,画风一下子正常了许多,再到刚入大学时的迷惘,再后来事情都是现在记忆里的模样了。

本来,今天还是很高兴的,终于把破事摆平顺利回家。但到晚上突然很痛苦,因为怎么都听不见回声,我感觉是之前做的事逾矩了,覆水难收,到底还是让别人产生芥蒂了。害。



20240209

dowhy库构建因果图,旨在简化因果推断的过程,特别是针对那些希望从数据中估计因果效应的研究者。

开源地址:https://github.com/py-why/dowhy

pip install dowhy
  • 1

demo:

# 加载数据
import numpy as np
import pandas as pd
from dowhy import CausalModel
import dowhy.datasets
rvar = 1 if np.random.uniform() >0.5 else 0 
data_dict = dowhy.datasets.xy_dataset(10000, effect=rvar, sd_error=0.2) 
df = data_dict['df']

# 现在你可以使用 DoWhy 定义因果模型。这涉及指定分析中的变量、治疗、结果和潜在的混杂因素。
model= CausalModel(
        data=df,
        treatment=data_dict["treatment_name"],
        outcome=data_dict["outcome_name"],
        common_causes=data_dict["common_causes_names"]
        )
model.view_model(layout="dot")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

参数说明:

  • data,包含所有相关数据的 DataFrame。这应该包括处理变量、结果变量、共同原因(共变量)、以及(如果有的话)工具变量。
  • treatment,指定作为处理(干预)的变量名。在因果推断中,我们关心的是改变这个变量会如何影响结果变量。
  • outcome,指定结果变量的名称。这是我们想要了解其因果效应的变量。
  • common_causes,一个包含所有已知共同原因(也称为共变量或混杂变量)名称的列表。这些是既影响处理变量又影响结果变量的变量,必须控制以避免偏差。
  • instruments,一个包含所有工具变量名称的列表(如果有的话)。工具变量是与处理变量相关但只通过它影响结果变量的变量,常用于处理内生性问题。

输出结果:

w 0 → T r e a t m e n t → O u t c o m e ← w 0 w_0 \rightarrow Treatment \rightarrow Outcome \leftarrow w_0 w0TreatmentOutcomew0

这是个典型的三角带混杂的因果图,即 w 0 w_0 w0为混杂变量,它既影响处理变量 T r e a t m e n t Treatment Treatment又影响结果变量 O u t c o m e Outcome Outcome


20240210

  • 现在CSDN学坏了,搞什么流量券,全是阴兵刷数据,真是恶心。
  • 早上不到七点自然醒,果然还是家里床睡得舒服,半夜鞭炮声都没吵醒我。起来翻了一下消息,还是挺失落。算了,等开学,不想动脑,心急不了一点。
  • 扭伤完全好了,昨天AK跑山,11km+,700+爬升,而且只是一座山,太恐怖,穹窿山200米的山下坡就一眼望不到底,走的让人特别绝望,AK说我是不会下山,会下山后会很爽,可是整个扬州一马平川,从小对山就一点概念没有。
  • 今年再没有去年《流浪地球3》那种神作了,几乎清一色的快餐喜剧,就像春节不看喜剧就活不下去似的。其实不太想去看电影,但年初一也没啥事可做,还是去看了《热辣滚烫》,据说口碑两极分化,怎么说呢,春节看这种以催泪(乃至破防)结局收尾的电影确实无法合乎所有人的胃口(小孩不一定看得懂,中年人又觉得无感,只有我们这些年轻一辈才有很强共鸣),还是快餐喜剧更有普适性点儿,但就电影本身而言,几乎没有可挑剔的地方:
    • 首先是学院派的标准艺术表现形式,只提三点:①开头常规的倒叙手法,结局比赛进场时胖瘦贾玲的对视呼应开头;②月莹同意远房表妹(杨紫饰)节目录制后回家的那段上四层楼、回宿舍、歇斯底里的长外景镜头(其实当时就觉得应该会跳楼,这样社会性死亡又被人当众侮辱,果然后面走马灯的时候提到真的跳楼了,只是没死成),从狂风到暴雨,偏蒙太奇式的叙事手法;③最后月莹被KO在地,脑子里回马灯式地回忆之前若干情节中的留白部分(录制节目晕倒后,没有哭出来,但听到远房表妹冰冷自私的话语时的绝望,一行清泪留下,回家后跳楼自杀,以及和昊坤的初夜、与妹妹的争执房产的结局等等),既影射现实的残酷,又使得剧情完满,愈发催人泪下。
    • 其次是题材,至少对我很有共鸣(真的破防了,我其实没哭,因为我也是这么走过来的,但旁边两个30多岁的姐姐都哭了,觉得月莹被打得好惨),尤其最后月莹参加比赛被打得鼻青脸肿,努力不代表你就能赢,比你有天赋有能力的人一抓一大把,但是不努力、没有梦想你还是那个两百多斤的月莹,我自己也是这样,本科时体测1000米从没跑进过4分10秒,但如今万米已经达38分台,但是相比那些身体条件好的人依然差得很远,但至少我也比之前好太多。
    • 最后是对贾玲老师的敬佩。贾玲老师真的是狠人,电影七成的剧情都是胖月莹的故事。这意味着她在一年前就把减肥之前所有剧情(七成左右)都拍好了,如果一年内减不下来,她就赶不上春节档了,这真是史上最强的减肥计划,而且真的好夸张,现在练出马甲线,甚至能做引体向上,跑姿也好标准,太恐怖了,有钱能使磨推鬼(雾)。
  • 看完去吃了烤鱼,我一个人硬吃完一整条海鲈鱼,加上一些牛蛙和小酥肉,撑得要死,回程备受鼓舞,背着书包一路从外婆家跑回家,又补了3000跳绳,100双摇,以及核心训练。我也要跟贾玲一样自律,吃吃喝喝是破不了三的,既然现在状态这么好,我就应该继续保持下去,而不是想着摆烂几天再捡起来。决心直到比赛前,都要保持现在的节奏生活下去,一定。

交接备份(反正过年不搞,年后也得搞,不如过年搞掉):

# -*- coding: utf-8 -*- 
# @author : caoyang
# @email: caoyang@stu.sufe.edu.cn

import re
import logging
import pandas as pd

from settings import RAW_DATA_SUMMARY
from src.pipelines.base import NLPPipelines


class JobPreprocessPipeline(NLPPipelines):
	# 1. DEFINE POSTAGs
	# Comment@caoyang: Here I only use pkuseg, if you use jieba, you need to update the following 4 dicts 
	uncritical_pos_tags = {"pkuseg": ['r', 'z', 'y', 'e', 'o', 'i', 'x']}	# Pos tags which can be ignored
	noun_pos_tags = {"pkuseg": ['n', "nr", "ns", "nt", "nx", "nz", "an"]}	# Pos tags which refer to nouns
	verb_pos_tags = {"pkuseg": ['v', "vx", "vn"]}							# Pos tags which refer to verbs
	other_pos_tags = {"pkuseg": ['c', 'u', 'w', 'd']}						# Pos tags which are not nouns or verbs (but cannot be ignored, e.g. "的", "和", etc)
	# ----------------------------------------------------------------
	# 2. DEFINE EDUCATIONs AND MAJORs
	educations_lv0 = ["小学", "初中", "中学", "高中"]	# Education Lv.0 (Note that there are descriptions like "辅导小学/初中小孩", "初中级技术人员", "初高中教学辅导", "从中学习")
	educations_lv1 = ["职校", "职高", "技校", "中专"]	# Education Lv.1
	educations_lv2 = ["大专", "高职", "本科", "学士"]	# Education Lv.2
	educations_lv3 = ["硕士", "研究生", "博士"]		# Education Lv.3
	educations_main = educations_lv1 + educations_lv2 + educations_lv3
	majors_need_suffix = ["计算机", "英语",
						  ]	# These majors must have a suffix "专业"
	majors_all = ["法律", "金融", "机电", "机械", "人力资源", "财务", "师范",
				  "机电", "人力资源", "师范",
				  ]			# You can add new major strings here
	# ----------------------------------------------------------------
	# 3. DEFINE SOME KEY TOKENs
	# Phrases containing forbidden tokens can be ignored when extracting tasks and skills
	forbidden_tokens = ["00", "周岁", "岗位职责", "任职", "岗位要求", "岗位目的",
						 "职责", "职位", "招聘岗位", "工作内容", "工作描述",
						 "男女", "不限", "身高", "体重", "优先", "招聘", "入职",
						 "科技园", "有无经验", "诚聘", "身高", "体重", "优先",
						 "招聘", "入职", "科技园",
						 ]
	forbidden_tokens = list(set(filter(None, forbidden_tokens)))
	# All the tokens appearing behind uncritical tokens can be ignored when extracting tasks and skills
	# comment@20231114: I found that treatment (e.g. salary, accommodation, etc) may appear at the start of many job ads, so that I set a `threshold_for_skip` for ignoring the tokens behind uncritical tokens
	uncritical_tokens = ["福利", "晋升", "薪资", "工资", "工作地址",
						 "联系人", "联系电话", "待遇", "五险", "底薪",
						 "五险", "一金", "奖金", "上班", "工作时间",
						 "职位发展", "培训与发展", "绩效", "技能培训", "免费",
						 "职前培训", "级别", "年龄", "截止", "有无经验",
						 "包吃包住", "加班", "社保", "包住宿", "持有",
						 "学历", "专业不限", "毕业", "相关专业", "等专业",
						 "管理专业", "设计专业", "工科专业", "工程专业",
						 "营销专业", "计算机专业", "语专业", "会计专业",
						 "以上专业", "金融专业", "法律专业", "机械专业",
						 "自动化专业", "财务专业", "电器专业", "类专业",
						 "教育专业", "护理专业", "美术专业", "机电专业",
						 "法学专业", "建筑专业", "师范", "画专业",
						 ]
	uncritical_tokens = list(set(filter(None, uncritical_tokens)))
	forbidden_tokens = list(set(forbidden_tokens + uncritical_tokens))	# Add uncritical tokens into forbidden tokens
	threshold_for_skip = .5	# See comment@20231114 above
	# ----------------------------------------------------------------
	# 4. Strip symbols for post-processing phrases
	punctuations = ",,.。;;[]【】-——、"
	
	def __init__(self, **kwargs):
		super(JobPreprocessPipeline, self).__init__(**kwargs)
		# Check 
		for pos_tags in ["uncritical_pos_tags", "noun_pos_tags", "verb_pos_tags", "other_pos_tags"]:
			if self.library not in eval(f"self.{pos_tags}"):
				raise NotImplementedError(f"You want to use library {self.library} for parsing, please update `{pos_tags}` above!")
		self._load_major_zyml(filepath=RAW_DATA_SUMMARY["major_zyml"])
		self._define_regex()


	# Regex definitions for extracting salary(*), experience, age, education, major and for substitute, sentence tokenization
	def _define_regex(self):
		# 1. Regex matching salary numbers
		# regex_salary_string = "[^a-zA-Z\d][1-9]\d{1,2}00[^\d号年届]"	# This regex is better, but when processing "工资4000-6000元/月", it can match "4000" only but ignore "6000", because `re.findall` find only non-overlapping matches
		regex_salary_string = "([1-9]\d{1,2}00)[^\d号年届]"				# This regex avoid the problem above, but may extract something wrong	
		self.regex_salary = re.compile(regex_salary_string)
		# --------------------------------
		# 2. Regex matching experience strings
		regex_experience_string = "((\d{1,2}|一|二|三|四|五|六|七|八|九|十|十五|二十|二十五|三十)(年)?[\~\-至到—])?(\d{1,2}|一|二|三|四|五|六|七|八|九|十|十五|二十|二十五|三十)(年)(以[上内下])?([^,。(\\\\N);\|]+)(工作)?(经验)|(有?无经验)"
		self.regex_experience = re.compile(regex_experience_string)
		# --------------------------------
		# 3. Regex matching age strings
		regex_age_string = "(([二三四五六七八九]?十[一二三四五六七八九]?|\d{2})(周?岁)?[\-\~至到])?([二三四五六七八九]?十[一二三四五六七八九]?|\d{2})(周?岁)(以[内下上])?"
		self.regex_age = re.compile(regex_age_string)
		# --------------------------------
		# 4. Regexs matching education strings
		or_string_education_main = f"({'|'.join(self.educations_main)})"
		or_string_education_lv0 = f"({'|'.join(self.educations_lv0)})"
		# You can copy from `regexs_for_substitute`, but you'd better add brackets to each substring(if you wish `re.findall` to capture it)
		regex_education_strings = [f"(学历)?([::])?{or_string_education_main}(以?及)?{or_string_education_main}?(以[上内下])?(学历|毕业)?",
								   f"(学历)([::])?{or_string_education_lv0}(以?及)?{or_string_education_lv0}?(以[上内下])?",
								   f"({or_string_education_lv0})(以?及)?({or_string_education_lv0})?(以[上内下])?(学历|毕业)",
								   ]
		self.regexs_education = list(map(re.compile, regex_education_strings))
		# --------------------------------
		# 5. Regexs matching major strings
		# Update `majors_all`
		majors_all = list(set(self.majors_all))							# Drop duplicates
		for major in self.majors_all:
			if major.endswith("大类") and len(major) > 4:
				# e.g. "农林牧渔大类" -> "农林牧渔", "资源环境与安全大类" -> "资源环境与安全"
				majors_all.append(major[:-2])
			elif major.endswith("类") and len(major) > 3:
				# e.g. "医学技术类" -> "医学技术", "经济学类" -> "经济学"
				majors_all.append(major[:-1])
		# Split majors by whether they need suffix
		majors_need_suffix = list(set(self.majors_need_suffix))			# Drop duplicates
		majors_not_need_suffix = list()									# Generate those do not need suffix (i.e. "专业")
		for major in majors_all:
			if major not in self.majors_need_suffix:
				majors_not_need_suffix.append(major)
		majors_not_need_suffix = list(set(majors_not_need_suffix))		# Drop duplicates
		regex_major_strings = [f"({'|'.join(majors_need_suffix)})(相关)?(专业)(优先)?",
							   f"({'|'.join(majors_not_need_suffix)})(相关)?(专业)?(优先)?",
							   ]
		self.regexs_major = list(map(re.compile, regex_major_strings))
		# --------------------------------
		# 6. Regex for substitute and sentence tokenization
		# @variable regexs_for_substitute: List[Tuple(<regex>, <substitute>)]
		regexs_for_substitute = [("[<《「【(\(][^))】」》>]+[\))】」》>]", str()),	# e.g. 【岗位职责】, <span id="sda"></span>
								 ("\\\\[a-zA-Z]", str()),							# e.g. "\N", "\R"
								 ("[【】✅☞'️⃣\\\\]", str()),							# Delete special tokens (You can add more special tokens here)
								 ("原?标题", str()),									# Note that "原标题" and "标题" appears many times in corpus
								 (regex_education_strings[0], str()),	# e.g. "本科及以上", "硕士以下学历", "学历大专"				 
								 (regex_education_strings[1], str()),	# e.g. "学历高中及高中以上", "学历初中", "学历小学"		
								 (regex_education_strings[2], str()),	# e.g. "小学毕业", "初中以上学历", "高中及以上学历"		
								 ("\d{2}周?岁以[内下上]", str()),	# e.g. "35岁以下", "18周岁以上"
								 ("\d{1,2}周?岁", str()),		# e.g. "35周岁", "24岁"
								 ("\d{1,2}年及?以[上下内](相关)?(工作)?(经验)?", str()),				# e.g. "10年以上工作经验", "2年以内相关工作"
								 ("[一二两三四五六七八九十]年及?以[上下内](相关)?(工作)?(经验)?", str()),	# e.g. "两年及以上工作经验", 
								 ("\d+[、.\)))]", '。'),								# Replace index term with sentence split symbol (i.e. "。")
								 ("[一二三四五六七八九十①②③④⑤⑥⑦⑧⑨⑩][、.]", "。"),	# Replace index term with sentence split symbol (i.e. "。")
								 ("\d{1,2}[::]\d{2}", str()),								# e.g. "8:30", "19:00"
								 ("(\d{1,5})?元?[-~到或]?\d{1,5}元?[/每][周天月年]", str()),	# e.g. "40到50每天", "5000元-6000元/每天"
								 ("([^\d])\d([^\d])", r"\1。\2"),							# Match single digitals (It must be set at the last position in `regexs_for_substitute` list)
								 ]
		self.regexs_for_substitute = list(map(lambda _tuple: (re.compile(_tuple[0]), _tuple[1]), regexs_for_substitute))
		# Split symbols for sentence tokenization
		self.regex_for_split = re.compile("[。;!?::\\(\\\\N)]+|\-\-\-+")


	# Load majors from 《普通高等学校本科专业目录(2022)》 and 《普通高等学校高等职业教育(专科)专业目录(2021)》
	def _load_major_zyml(self, filepath = RAW_DATA_SUMMARY["major_zyml"]):
		df_major_zyml = pd.read_csv(filepath, sep='\t', header=0)
		self.majors_lv1 = df_major_zyml["major1"].unique().tolist()	# Lv1: 31
		self.majors_lv2 = df_major_zyml["major2"].unique().tolist()	# Lv2: 79
		self.majors_lv3 = df_major_zyml["major3"].unique().tolist()	# Lv3: 1451
		logging.info(f"Lv1 majors: {len(self.majors_lv1)}")
		logging.info(f"Lv2 majors: {len(self.majors_lv2)}")
		logging.info(f"Lv3 majors: {len(self.majors_lv3)}")
		# --------------------------------
		logging.info(f"Default majors({len(self.majors_all)}): {self.majors_all}")
		self.majors_all = list(set(self.majors_lv1 + self.majors_lv2 + self.majors_lv3))
		print(f"Total Majors: {len(self.majors_all)}")





	# Extract verb-object phrase
	# comment@caoyang: Sorry for my mistake in naming this function, maybe `extract_vo_phrase` is more suitable, but now everywhere is `nv`, I do not wanner change it
	# @param df_job_phrase: @return of function `extract_phrase`
	# @param use_column: The default setting is "raw"(, and it is what the current results derive from), you can set `use_column = "brief"` for faster running
	def extract_nv_phrase(self,
						  df_job_phrase,
						  save_path,
						  use_column = "raw",
						  ):
		logging.info("Function `extract_nv_phrase` starts!")
		# Define easy function for efficiency
		if use_column == "raw":
			def _easy_extract_nv_phrase(_phrases, _nv, _tokenizer):
				for _phrase_list in _phrases:
					for _phrase in _phrase_list:
						_nv.extend(self._extract_nv_from_phrase(_phrase, _tokenizer))
		elif use_column == "brief":
			def _easy_extract_nv_phrase(_phrases, _nv, _tokenizer):
				for _phrase in _phrases:
					_nv.extend(self._extract_nv_from_phrase(_phrase, _tokenizer))
		else:
			raise NotImplementedError(f'Expect keyword argument `use_column` in {"raw", "brief"} but got "{use_column}"!')
		# If the dtype is not List, we should do eval operation first
		if not isinstance(df_job_phrase[use_column][0], list):
			df_job_phrase[use_column] = df_job_phrase[use_column].map(eval)
		tokenizer = self.load_tokenizer(pos_tag=True)	# Load tokenizer
		nv_freq = dict()
		data_dict = {"mid": list(), "nv": list()}
		for i in range(df_job_phrase.shape[0]):
			mid = df_job_phrase.loc[i, "mid"]
			phrases = df_job_phrase.loc[i, use_column]
			nv = list()
			_easy_extract_nv_phrase(phrases, nv, tokenizer)
			data_dict["mid"].append(mid)
			data_dict["nv"].append(nv)
		dataframe = pd.DataFrame(data_dict, columns=list(data_dict.keys()))
		dataframe.to_csv(save_path, sep='\t', header=True, index=False)
		logging.info("Function `extract_nv_phrase` finishes!")
		return dataframe

	# Extract short verb-object phrase from a piece of long phrase
	# Methodology description
	# Step1: Split all the tokens by verb tokens
	# Step2: Splited tokens block can be treated as noun block
	# Step3: Find the nearest(i.e behind and before) noun block for each verb token
	# comment@caoyang: Sorry for my mistake in naming this function, maybe `_extract_vo_from_phrase` is more suitable, but now everywhere is `nv`, I do not wanner change it
	# @param phrase: Str, a piece of string catching from "raw" or "brief" from `df_job_phrase`
	# @param tokenizer: e.g. pkuseg tokenizer
	# @param tolerate_forbidden: Boolean, indicating that whether to return empty list for those phrases containing `forbidden_tokens`
	# @return nv_list: List[Tuple(<verb: str>, <noun: str>)], e.g. [("编写", "代码"), ("完成", "任务), ...]
	def _extract_nv_from_phrase(self,
								phrase,
								tokenizer,
								tolerate_forbidden = False,
								):
		# Check validation
		if not tolerate_forbidden:
			for token in self.forbidden_tokens:
				if token in phrase:
					return list()
		_postprocess_noun_block = lambda _noun_block: _noun_block.strip("的并和及与,,.。;;[]【】-——、>/")
		# --------------------------------
		# 1. Deal with blocks connected by "的"
		i = 0
		token_pos_tuples_new = list()				# Modified (<token>, <pos>) tuple
		token_pos_tuples_raw = tokenizer(phrase)	# Raw (<token>, <pos>) tuple
		while i < len(token_pos_tuples_raw):
			# comment@20231201: If a verb is followed by "的", then
			#   - Rule 1: consider this verb and "的" together as an adjective
			#   - Rule 2: consider this verb and "的" and the next token together as a noun 
			token, pos = token_pos_tuples_raw[i]
			if i < len(token_pos_tuples_raw) - 1 and token_pos_tuples_raw[i + 1][0] == "的":
				# There is at least one token before a "的" token
				if i < len(token_pos_tuples_raw) - 2:
					# There is at least one token behind the "的" token, then take Rule 2
					token_pos_tuples_new.append((f"{token}{token_pos_tuples_raw[i + 2][0]}", 'n'))
					i += 3
				else:
					# There is no token behind the "的" token, then take Rule 1
					token_pos_tuples_new.append((f"{token}的", 'a'))
					i += 2
			else:
				# Default case
				token_pos_tuples_new.append((token, pos))
				i += 1
		# --------------------------------
		# 2. Generate noun/verb blocks
		noun_flag = False			# Flag indicating that if any noun component in a non-verb block
		blocks = list()				# List[Tuple(<phrase: str>, <pos: "n"/"v">)]
		current_noun_block = str()	# Current noun block
		for i, (token, pos) in enumerate(token_pos_tuples_new):
			if pos in ['p', "pn"] or token in ["是", "能", "有", "为", "公司"]:
				# Do not process prepositions and some special verbs or nouns
				continue
			elif pos in ['v']:
				# Appear verb
				# comment@caoyang: You could take "vn" into account, but I think "vn" appears more like a noun than a verb
				current_noun_block = _postprocess_noun_block(current_noun_block)
				if noun_flag and len(current_noun_block) > 1:
					blocks.append((current_noun_block, 'n'))
				blocks.append((token, 'v'))
				current_noun_block = str()
			else:
				# Appear noun components or other pos which can be seemed as noun components
				if pos in self.noun_pos_tags[self.library] + ["vn", 'i', 'j', 'l']:
					# comment@caoyang: You could take "vn" out of account, but I think "vn" appears more like a noun than a verb
					# 'i' refers to 成语(i.e. idiom): e.g. "吃苦耐劳", "精益求精"
					# 'j' refers to 简称(i.e. abbr), e.g. "数控", "大中小微"
					# 'l' refers to 习惯用语(i.e. slang), e.g. "售后服务", "多劳多得"
					noun_flag = True
				current_noun_block += token
		# Process the last noun block
		current_noun_block = _postprocess_noun_block(current_noun_block)
		if noun_flag and len(current_noun_block) > 1:
			blocks.append((current_noun_block, 'n'))
		# *** Finally, there may be continous verb blocks in `blocks` but never continous noun blocks, i.e. [..., 'n', 'n', ...] is impossible
		# --------------------------------
		# 3. Match verb block to noun block
		current_noun_block = None
		nv_list = list()	# List[Tuple(<verb: str>, <noun: str>)]: Final return
		for i, (nv_phrase, nv_pos) in enumerate(blocks):
			if nv_pos == 'v':
				# Match verb block to the nearest noun block before
				if current_noun_block is not None:
					nv_list.append((nv_phrase, current_noun_block))
				# Match verb block to the nearest noun block behind
				for j in range(i + 1, len(blocks)):
					if blocks[j][1] == 'n':
						nv_list.append((nv_phrase, blocks[j][0]))
						break
			else:
				# i.e. nv_pos == 'n'
				current_noun_block = nv_phrase
		return nv_list

	# ------------------------------------------------------------------
	# TODO!!!
	# @param df_job_ad: Refers to RAW_DATA_SUMMARY["job_ad"] in settings.py, whose required columns are ["mid", "intro", "title", "industry_name_lv2", "industry_name_lv4"]
	# @param save_path: File path used to save the title classification result
	# @param encoder: A loaded pre-trained model(see the keys of PRETRAINED_MODEL_SUMMARY in settings.py)
	# @param occupations: List[Str], stardard occupations
	# @param occupation_embeddings: numpy.ndarrays, embedding matrix of @param occupations, `shape(n_occupations, n_dim)`, 
	# @param query_template: e.g. "t2", "pt2", "pt24", "pt24i"
	# - 'p' refers to using prompt-like query
	# - 't' refers to using "title" as query
	# - '2' refers to using "industry_name_lv2" as query
	# - '4' refers to using "industry_name_lv4" as query 
	# - 'i' refers to using "intro" as query
	# @param top_k: How many matched occupation reserved
	# @param batch_size: How many samples encoder deals with at each time
	# @param title_embedding_save_path: File path used to save the title embeddings
	def classify_title(self,
					   df_job_ad,
					   save_path,
					   encoder,
					   occupations,
					   occupation_embeddings,
					   query_template = "t2",
					   top_k = 16,
					   batch_size = 1024,
					   title_embedding_save_path = None,
					   ):
		logging.info("Function `extract_metadata` starts!")
		df_job_ad["title"] = df_job_ad["title"].fillna(str())
		df_job_ad["industry_name_lv2"] = df_job_ad["industry_name_lv2"].fillna(str())
		df_job_ad["industry_name_lv4"] = df_job_ad["industry_name_lv4"].fillna(str())
		
		def _query_function(_entry):
			_query_split_by = "。\n"
			_prompt_suffix = None
			if 'p' in query_template:
				_prompt_suffix = {'t': "标题:",
								  '2': "一级行业分类:",
								  '4': "二级行业分类:",
								  'i': "具体内容:",
								  }		
			if 't' in query_template:
				pass

			elif '2' in query_template:
				pass

			elif '4' in query_template:
				pass

			elif 'i' in query_template:
				pass
				
			def __query(__entry):
				__query_strings = list()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353

20240211

  • 下午去仪征捺山地质公园,似乎是最近才开发出的4A景区,我活这么大真没听说过扬州有山,不过仪征离市区也很远,也从来没去过。远观就一座小土山(一百多米,说实话没啥好景色,但是徒步走走倒也不错),门票居然收70块(穹窿山不过60块,虽然是走野路上山白嫖门票),不能忍,我和老表直接翻两道围栏进去,但是因为没人开过路,路上全是荆棘树枝,特别难走,最后还有三只恶犬当道,还好捡到根粗壮结实的枝干,三英战吕布,成功打跑然后溜进景区。
  • 虽然山一般,但路修得不错,山上石板路(其实不如土路松软些好),山下柏油路,游览路线是一条圆环线,跟老表走了一圈后我表示这是极好的跑山路线,我要自己再来跑一圈。于是脱了外套开表猛冲(均配4’57"),几乎没有保留,虽不到4km,爬升150米,但上坡跑顶到心率187bpm(最近基本都是养生,要给心肺来点强度),跑完一圈感觉很累,没有强求再补一圈。可惜仪征太远,要不然我每天也来这跑两三圈训练,太高效了。
  • 跑完还是挺有成就感,结果看到一二九群里AK干了27km越野,配速6’01",爬升1119米,海拔2000多米,给我直接干沉默了。AK指定是本着开春PB在练,虽然他这大半年都没能跑出甚至接近PB的成绩,果然还是不甘心也不服输。
  • 目前三天都维持住训练量,除夕春节各800C+的消耗,今日1100C的消耗,基本持平学校的量,吃饭完全不忌嘴(但是回来后不会吃很多米饭,因为菜管够),饭点往死里猛干,非饭点能管住嘴,12点前熄灯,7点半前起床,回来每天喝不到10杯水,但七八杯还是保证的。我以前从没这么约束过自己(但我觉得王总就是能这么约束自己活这么久),这次就是很想保持住状态直到开赛。
    在这里插入图片描述在这里插入图片描述在这里插入图片描述

修正网易云音乐的下载,这么多年,它终于肯换JS里的明文密钥了:

# -*- coding: UTF-8 -*-
# @author: caoyang
# @email: caoyang@163.sufe.edu.cn
 
import os
import sys
import math
import time
import json
import random
import base64
import codecs

from requests import Session
from bs4 import BeautifulSoup
from Crypto.Cipher import AES
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.action_chains import ActionChains

class NetEase(object):
	"""
	Download music from [NetEase Online](https://music.163.com/)
	"""
	def __init__(self) -> None:			
		self.url_main = 'https://music.163.com/'
		self.headers = {'User-Agent': 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:82.0) Gecko/20100101 Firefox/82.0'}
		self.url_api = self.url_main + 'weapi/song/enhance/player/url?csrf_token='
		self.url_song = self.url_main + 'song?id={}'
		self.url_search = self.url_main + 'search/m/?s={}'
		self.renew_session()

	def renew_session(self) -> None:
		"""
		Refresh the session in the class.
		"""
		self.session = Session()
		self.session.headers = self.headers.copy()
		self.session.get(self.url_main)

	def search_for_song_id(self, song_name: str, driver: webdriver.Firefox, n_result: int=1) -> list:
		"""
		Search the song ids for the given song name.
		Here the selenium driver is used because the search results of NetEase is in the <iframe>...</iframe> label which is hard to deal with by simple requests.
		
		:param song_name: Name of the song needed to search.
		:param driver: A browser driver initiated by selenium.
		:param n_result: Number of results returned, default 1 means select the top-1 search result.
		:return song_ids: List of song ids matching ```song_name```.
		"""
		driver.get(self.url_main)
		xpath_input_frame = '//input[@id="srch"]'
		input_frame = driver.find_element_by_xpath(xpath_input_frame)
		input_frame.send_keys(song_name)
		input_frame.send_keys(Keys.ENTER)
		driver.switch_to_frame('g_iframe')
		WebDriverWait(driver, 15).until(lambda driver: driver.find_element_by_xpath('//div[@class="srchsongst"]').is_displayed())
		html = driver.page_source
		soup = BeautifulSoup(html, 'lxml')
		result_list = soup.find('div', class_='srchsongst')
		divs = list(result_list.children)[:n_result]
		song_ids = []
		for div in divs:
			div.find('div', class_='td')
			a = div.find('a')
			song_id = a.attrs['id'][5:]
			song_ids.append(song_id)
		driver.quit()
		return song_ids

	def download_by_song_id(self, song_id: str, save_path: str=None, driver: webdriver.Firefox=None) -> None:
		"""
		Download the song by id and write it into local file. 
		"""
		song_url = self.request_for_song_url(song_id, driver=driver)	
		r = self.session.get(song_url)									
		if save_path is None: 
			save_path = 'netease_{}'.format(song_id)					
		with open(save_path, 'wb') as f: 
			f.write(r.content)											


	def request_for_song_url(self, song_id: str, driver: webdriver.Firefox=None) -> str:
		"""
		Get the resource URL of the song needed to be downloaded. 
		"""
		formdata = self.encrypt_formdata(song_id, driver=driver)		
		r = self.session.post(self.url_api, data=formdata)				
		song_url = json.loads(r.text)['data'][0]['url']					
		return song_url

		
	def encrypt_formdata(self, song_id: str, driver: webdriver.Firefox=None) -> dict:
		"""
		Encrypt the formdata by the logic in NetEase javascript.
		Note that the formdata post to download music is encrypted.
		"""
		d='{"ids":"[%s]","br":128000,"csrf_token":""}'			
		e='010001'									
		f='00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
		g='0CoJUm6Qyw8W8jud'									
		d %= song_id													
		if driver is not None:											
			JS = 'return window.asrsea("{}", "{}", "{}", "{}")'.format(d, e, f, g)
			driver.get(self.url_song.format(song_id))
			formdata = driver.execute_script(JS)						
			formdata = dict(params=formdata['encText'], encSecKey=formdata['encSecKey'])
			return formdata
			
		def _javascript2python_a(a):									
			b = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
			c = str()
			for i in range(a): c += b[math.floor(random.random()*len(b))]
			return c
 
		def _javascript2python_b(a, b):									
			pad = 16 - len(a.encode())%16								
			a += pad * chr(pad)											
			encryptor = AES.new(b.encode('UTF-8'), AES.MODE_CBC, b'0102030405060708')	
			f = base64.b64encode(encryptor.encrypt(a.encode('UTF-8')))
			return f
			
		def _javascript2python_c(a, b, c):								
			b = b[::-1]													
			e = int(codecs.encode(b.encode('UTF-8'), 'hex_codec'), 16)**int(a,16) % int(c,16)
			return format(e, 'x').zfill(256)							
 
		random_text = _javascript2python_a(16)							
		params = _javascript2python_b(d, g)								
		params = _javascript2python_b(params.decode('UTF-8'), random_text)
		encSecKey = _javascript2python_c(e, random_text, f)				
		formdata = dict(params=params, encSecKey=encSecKey)				
		return formdata													
 
	def test(self):
		# options = webdriver.FirefoxOptions()							
		# options.add_argument("--headless")								
		# driver = webdriver.Firefox(options=options)
		# song_ids = self.search_for_song_id("燕归巢", driver, n_result=3)
		# print(song_ids)
		# driver.quit()
		song_id = '1375266614'
		r = self.download_by_song_id(
			song_id,
			save_path='netease_{}.mp3'.format(song_id),
			driver=None,
		)
if __name__ == "__main__":
	ne = NetEase()
	ne.test()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151

20240212

  • 早上七点半看到24岁的基普图姆车祸去世的消息,我第一感就觉得背后是阴谋。因为他崛起得太快了,仅正式参加三场马拉松比赛,就全部都跑到2小时02分以内,而就在昨天,他的芝马成绩2:00:35被世界田联认可为世界纪录,他去年就放言要在今年四月的鹿特丹正式破二。乔神虽然值得尊敬,但他的1:59:40水分和代价实在太大,基普图姆出道两年就达到这种地步,势必动了别人的利益,而且说实话他去年4月在伦敦马拉松还当众打了中乔的脸(签约中乔,结果穿耐克比赛),肯定也得罪了不少人,很难想象这场单方车祸仅仅是个意外。
  • 上午公路跑9.85km回乡,均配4’23",除夕那天跑这条线起得太猛,把右跟腱磕得很疼,今天长了些记性,换了厚底的飞飙,尽量小心了些。不是很累,但太阳很晒,心率偏高,本来这次回来老妈还夸我变白(因为下半年工作日根本见不到太阳),这样晒一周又该黑回去咯(不过,我觉得AK肯定会变更黑,确信,一个个的天天搁这黑练)。
  • 回头练车,以前对开车我是一点兴趣没有,现在觉得必须老司机,不然去哪儿都要高铁好不方便。当年颜老板在吉大比赛时就是开共享汽车带我们飙,今年也是自己开车回老家福建,妥妥老司机,急人。
    在这里插入图片描述在这里插入图片描述

去年11月电脑莫名其妙间歇性变卡,一直没搞明白根源,拆后盖清灰也没清出多少灰,直到今天我终于猜到一个可能的原因,是温度太低。因为之前在公司用就一直没卡过,一旦带回学校就经常卡。还有一次先在图书馆一楼台灯区(冷)很卡,然后搬到五楼自习室(热)就不卡了。网上也没人提过这种情况,但是感觉上温度低时某些硬件确实工作效率很差,然后把电脑扔阳台晒了半天,就完全好了。

基于深度学习的人脸检测器(链接), 模型加载、预测,并将结果取出来并绘制到图片上,遍历代码的逻辑如下:

  1. 遍历检测结果。
  2. 然后,提取置信度并将其与置信度阈值进行比较。 执行此检查以过滤掉弱检测。 如果置信度满足最小阈值,继续绘制一个矩形以及检测概率。
  3. 为此,首先计算边界框的 (x, y) 坐标。 然后构建包含检测概率的置信文本字符串。 如果文本偏离图像(例如当面部检测发生在图像的最顶部时),将其向下移动 10 个像素。 面部矩形和置信文本绘制在图像上。
  4. 然后,再次循环执行该过程后的其他检测。 如果没有检测到,准备在屏幕上显示我们的输出图像)。
import numpy as np
import cv2

if __name__=='__main__':
    low_confidence=0.5
    image_path='2.jpg'
    proto_txt='deploy.proto.txt'
    model_path='res10_300x300_ssd_iter_140000_fp16.caffemodel'

    # 加载模型
    print("[INFO] loading model...")
    net = cv2.dnn.readNetFromCaffe(proto_txt, model_path)

    # 加载输入图像并为图像构建一个输入 blob
    # 将大小调整为固定的 300x300 像素,然后对其进行标准化
    image = cv2.imread(image_path)
    (h, w) = image.shape[:2]
    blob = cv2.dnn.blobFromImage(cv2.resize(image, (300, 300)), 1.0,
        (300, 300), (104.0, 177.0, 123.0))

    # 通过网络传递blob并获得检测和预测
    print("[INFO] computing object detections...")
    net.setInput(blob)
    detections = net.forward()

	# 循环检测
	for i in range(0, detections.shape[2]):
	    # 提取与相关的置信度(即概率)
	    # 预测
	    confidence = detections[0, 0, i, 2]
	    # 通过确保“置信度”来过滤掉弱检测
	    # 大于最小置信度
	    if confidence > low_confidence:
	        # 计算边界框的 (x, y) 坐标
	        box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
	        (startX, startY, endX, endY) = box.astype("int")
	        # 绘制人脸的边界框以及概率
	        text = "{:.2f}%".format(confidence * 100)
	        y = startY - 10 if startY - 10 > 10 else startY + 10
	        cv2.rectangle(image, (startX, startY), (endX, endY),
	                      (0, 0, 255), 2)
	        cv2.putText(image, text, (startX, y),
	                    cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 0, 255), 2)

    # 展示图片并保存
    cv2.imshow("Output", image)
    cv2.imwrite("01.jpg",image)
    cv2.waitKey(0)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

20240213

  • 看到一句话,基普图姆以为自己只用三场比赛就超越了乔老爷子,其实用了整整一生。第五个年头,我学会的最重要的事情就是要舍得跑慢些,以前总觉得4’30"开外的配速都不能叫跑步(有点觉得路跑太慢好丢人),现在跑六分配我都脸不红心不跳(物理上&心理上)。
  • 跑休日(这两天练车、学做菜,在家还是得把只有在家才能学的事给做了),为明后长距离做准备(本想着后天要去城区吃席,可以跑些新鲜的路线,但是后天大降温+雨雪,明天是最后一个晴天,就很扫兴),但晚饭后还是出去溜达了会儿,走走停停,挺热,顺便探探明天的路线,发现沿湖线已经修得很好,据说能一路通到高邮湖,准备明早破釜沉舟跑一回,先跑出去15km,然后回程,就是爬也得爬回来,正好试试160x3.0pro,虽然不太舍得这么快就拿来路跑(反正早用早享受呗)。
  • 目前净重应该不足69kg(其实每顿我都吃到撑,根本不节制,但体重就是不长),扛过最后3天,开学即可拿捏!

姿态估计(Opencv+Mediapipe)

Mediapipe是主要用于构建多模式音频,视频或任何时间序列数据的框架。借助MediaPipe框架,可以构建一些的ML管道,比如TensorFlow,TFLite等推理模型以及媒体处理功能。

pip install mediapipe
  • 1

通过视频或实时馈送进行人体姿态估计在诸如全身手势控制,量化体育锻炼和手语识别等各个领域中发挥着至关重要的作用。例如,可用作健身,瑜伽和舞蹈应用程序的基本模型。

Media Pipe Pose是用于高保真人体姿势跟踪的框架,该框架从RGB视频帧获取输入并推断出整个人类的33个3D界标。当前最先进的方法主要依靠强大的桌面环境进行推理,而此方法优于其他方法,并且可以实时获得很好的结果。模型可以预测33个关键点,如下图:

在这里插入图片描述

DEMO代码:

import cv2
import mediapipe as mp
import time

mpPose = mp.solutions.pose
pose = mpPose.Pose()
mpDraw = mp.solutions.drawing_utils
cap = cv2.VideoCapture('1.mp4')
pTime = 0

#TODO(You): 请在此实现并输出检测结果
while True:
    success, img = cap.read()
    if success is False:
        break
    imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    results = pose.process(imgRGB)
    if results is None:
        continue
    print(results.pose_landmarks)
    if results.pose_landmarks:
        mpDraw.draw_landmarks(img, results.pose_landmarks, mpPose.POSE_CONNECTIONS)
    for id, lm in enumerate(results.pose_landmarks.landmark):
        h, w, c = img.shape
    print(id, lm)
    cx, cy = int(lm.x * w), int(lm.y * h)
    cv2.circle(img, (cx, cy), 5, (255, 0, 0), cv2.FILLED)
    cTime = time.time()
    fps = 1 / (cTime - pTime)
    pTime = cTime
    cv2.putText(img, str(int(fps)), (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 3)
    cv2.imshow("Image", img)
    key = cv2.waitKey(1) & 0xFF

# do a bit of cleanup
cv2.destroyAllWindows()
cap.release()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

20240214

  • 情人节,过个鸟,反正从来没过过,简单点儿,不如跑步。
  • 七点空腹称了个净重,67.5kg(我这几天真没少吃,也没练太狠,怎么掉这么多),喝一杯水,吃些碳水和牛肉,薅起还躺着的老爹(在家直接抓壮丁当移动补给箱)。寒潮前最后一日暖冬,热得离谱,上午气温20℃,适逢初五迎财神,昨晚十一点半起,鞭炮声就没停过,比除夕和春节还密集(一群财迷)。虽然没有睡得很好,但也算是休息到位。
  • XTEP160x3.0pro,脚感跟飞飙差不太多(外形、颜色、鞋底也很像,但是回弹要好些,而且轻),起跑有些不合脚,感觉前掌面翘起角度太大,如果要完全踩满前脚掌,几乎要完全踮起,太吃力。但是慢慢能找到合适的方式落地,挺舒适的。寿命是2000km,从鞋底来看应该和飞飙是一个耐磨度,可以认为是飞飙的加强版(但是现在500多的飞飙已经买不到的了,最便宜的也要900向上),很好,再也不用垃圾NIKE,穿两三次鞋底就磨烂了。
  • 船闸湖堤线(向高邮湖方向),很棒的一段路,没有起伏(但进船闸会有一段很长的桥要爬),沿湖景色很好,又直又长,而且几乎无人(过年这段时间两头都封路,只能步行或骑车进入),计划是25k-30k的LSD,预定配速4’20",起手第一个10km用时42’30"(均配4’15"),心率顶到170bpm以上,虽然体感没有很吃力,但已经预料到要凉了。
  • 15km折返(破釜沉舟,逼自己再跑完回程15km),到20km处开始跑崩,均配已经掉到4’21",算了下最后10km慢慢摇个4’45"的配速也够跑得进4’30"的均配,但20km以后每隔2km多就已经坚持不住,必须停下喝水休息会儿,太阳晒得很痛苦。直到27km(刚好2小时少20秒左右)重新跑回船闸(这时带的两瓶水都喝光了,但还是渴得不行,老爹只能削块苹果给我吃),终点还有3km,人也多了起来,一下子就来劲了,提速冲完最后3km,用时2:12:59,均配4’26"(从8:24跑到10:45,中间休息加起来不到10分钟,差强人意)。
  • 除心肺跑崩外,基本无伤(大腿和髋有些抽筋),回去拉伸二十分钟复原,基本满意的一次LSD训练,没有很累,下午去医院闭关(本来初五是该出去玩,但实在不想情人节出去,给自己找不自在,而且天太热)。说实话,我很难想象今天这个状态(最近真的保养得很好,已经是相当好的状态)还能再坚持12.2km跑完全马,而且这个配速差破三10秒不止,破三任重道远,我有点想拼阳寿先去抽一下4.21的上半马,基本上跑进130就能直通上马,但又觉得自己无法再保持住状态等到下半年,唉。
    在这里插入图片描述在这里插入图片描述

opencv.dnn模块已经支持大部分格式的深度学习模型推理,该模块可以直接加载tensorflow、darknet、pytorch等常见深度学习框架训练出来的模型,并运行推理得到模型输出结果。opecnv.dnn模块已经作为一种模型部署方式,应用在工业落地实际场景中。

在这里插入图片描述

模型具体加载和使用流程如下:

加载网络,读取模型、网络结构配置等文件
创建输入,opencv.dnn模块对图片输入有特殊格式要求
运行推理
解析输出
应用输出、显示输出

下面是opencv.dnn模块加载yolov3-tiny车辆检测模型并运行推理的代码:

import numpy as np
import cv2
import os
import time
from numpy import array

# some variables
weightsPath = './yolov3-tiny.weights'
configPath = './yolov3-tiny.cfg'
labelsPath = './obj.names'

LABELS = open(labelsPath).read().strip().split("\n")
colors = [(255, 255, 0), (255, 0, 255), (0, 255, 255), (0, 255, 0), (255, 0, 255)]
min_score = 0.3
# read darknet weights using opencv.dnn module
net = cv2.dnn.readNetFromDarknet(configPath, weightsPath)

# read video using opencv
cap = cv2.VideoCapture('./MY_TEST/8.h264') 

# loop for inference
while True:
    boxes = []
    confidences = []
    classIDs = []
    start = time.time()

    ret, frame = cap.read()
    frame = cv2.resize(frame, (744, 416), interpolation=cv2.INTER_CUBIC)
    image = frame
    (H, W) = image.shape[0: 2]


    # get output layer names
    ln = net.getLayerNames()
    out = net.getUnconnectedOutLayers()
    x = []
    for i in out:
        x.append(ln[i[0]-1])
    ln = x

    # create input data package with current frame
    blob = cv2.dnn.blobFromImage(image, 1 / 255.0, (416, 416), swapRB=True, crop=False)
    # set as input
    net.setInput(blob)

    # run!
    layerOutputs = net.forward(ln)

    # post-process
    # parsing the output and run nms

    # TODO
	for output in layerOutputs:
	    for detection in output:
	        scores = detection[5:]
	        # class id
	        classID = np.argmax(scores)
	        # get score by classid
	        score = scores[classID]
	
	        # ignore if score is too low
	        if score >= min_score:
	            box = detection[0:4] * np.array([W, H, W, H])
	            (centerX, centerY, width, height)= box.astype("int")
	
	            x = int(centerX - (width / 2))
	            y = int(centerY - (height / 2))
	            
	            boxes.append([x, y, int(width), int(height)])
	            confidences.append(float(score))
	            classIDs.append(classID)
	
	# run nms using opencv.dnn module
	idxs = cv2.dnn.NMSBoxes(boxes, confidences, 0.2, 0.3)
	
	# render on image
	idxs = array(idxs)
	box_seq = idxs.flatten()
	if len(idxs) > 0:
	    for seq in box_seq:
	        (x, y) = (boxes[seq][0], boxes[seq][1])
	        (w, h) = (boxes[seq][2], boxes[seq][3])
	
	        # draw what you want
	        color = colors[classIDs[seq]]
	        cv2.rectangle(image, (x, y), (x + w, y + h), color, 2)
	        text = "{}: {:.3f}".format(LABELS[classIDs[seq]], confidences[seq])
	        cv2.putText(image, text, (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.3, color, 1)  
	
	    cv2.namedWindow('Image', cv2.WINDOW_NORMAL)
	    cv2.imshow("Image", image)

    # print fps 
    stop = time.time()
    fps = 1/(stop - start)
    print('fps>>> :', fps)

    # normal codes when displaying video
    c = cv2.waitKey(1) & 0xff
    if c == 27:
        cap.release()
        break

cv2.destroyAllWindows()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105

20240215

  • 表哥女儿周岁,气人的一天。人变了,他现在声名鹊起,结交的人多了,但变得很糟心,虽然早挺说他那些事,但我觉得自己今天两顿饭已经很给足面子,老爹都不想去给他敬酒,我代这个做舅舅的去给把酒敬了,他亲家那边招呼也都打了,但人品真太差,尊重父母,至少在外人面前尊重些,哪怕做做样子,现在这哥是真一点脸不给姑妈姑父,人脸上有些东西很难装出来,有些东西也很难抹去。
  • 我也一直想改变父母的一些观念,但至少我们仨还是很和睦,关起门来拌两嘴不过,在外面我也每天给家里打电话。但老妈这次观点很坚定,既不赞成我,也不反对我,你爱咋咋地,只有我能欺负你爸,别人都不行,你也不行。他俩现在统一战线,说到底又不是求他们认可我,我只是希望父母可以更好地理解他们的儿子。心累,反正以后又不是跟他俩过日子。
  • 唯一让我欣慰的事情是晚上吃饭,一堆叔婶和祖辈跟我客气让我多吃些,我一肚子气,就真没跟他们客气,直接光速吃完一整锅炖鸡汤的鸡肉和一整条清蒸鲑鱼,外带各种虾和一堆杂七杂八的东西,吃完还反唇相讥让各位别停筷子。好呀,你们非要跟我客气,那就都别吃了。说实话没吃主食,并不是很撑,吃完顺便在酒店20层楼上下5趟,消食走人。
  • 除昨天活动消耗2400C,除夕到今天初六,每天的活动消耗都在800C以上,身体恢复得很快,今天感觉除小腿酸胀(昨天拉伸小腿时间太短了,果然还是得按摩两下),其他都没有疼痛,依然可以上强度,但没必要,欲速不达,该吃吃,该练练。

彩色图像进行灰度化处理,可以在读取图像文件时直接读取为灰度图像,也可以通过函数 cv.cvtColor() 将彩色图像转换为灰度图像。

import cv2 as cv

img = cv.imread("GrayscaleLena.tif")          
imgGray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
cv.imshow("ImgGray", imgGray)
key = cv.waitKey(0)
# OR
img = cv.imread("GrayscaleLena.tif", cv.IMREAD_GRAYSCALE)
cv.imshow("ImgGray", imgGray)
key = cv.waitKey(0)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

20240216

  • 后悔拖到初八返程,结果今天票还是没候补上,只好先买张镇江到上海的K车兜底(关键现在去镇江也得堵),三个小时,还是无座,有些难绷。其实我只是想今天把阳历生日过了再走,毕竟这么多年(除去年春节特别早,是返沪和凯爹一起过),这一天一般都是在家,也是无奈接受奔三的现实。
  • 昨晚休息得不好,虽然七点半自然醒,但是手表显示身体电量只恢复到83(前六天一觉醒来都有98以上),白天精神很差。静息心率高出平均四五个点,不知为何,是有些紧张,安逸日子过久,不想跳出舒适圈了么?
  • 罢了,放纵一日,光吃不练。年后,事情又将如何?不知晓。
    在这里插入图片描述在这里插入图片描述

简单阈值分割cv2.threshold (src, thresh, maxval, type)
像素值高于阈值时(大部分情况为127),我们给这个像素
赋予一个新值(可能是白色),否则我们给它赋予另外一种颜色(也许是黑色)。

参数:
src 源图片,必须是单通道
thresh 阈值,取值范围0~255
maxval 填充色,取值范围0~255
type 阈值类型,见下表

  • cv2.THRESH_BINARY 二进制阈值化,非黑即白
  • cv2.THRESH_BINARY_INV 反二进制阈值化,非白即黑
  • cv2.THRESH_TRUNC 截断阈值化 ,大于阈值设为阈值
  • cv2.THRESH_TOZERO 阈值化为0 ,小于阈值设为0
  • cv2.THRESH_TOZERO_INV 反阈值化为0 ,大于阈值设为0
import cv2
import numpy as np
from matplotlib import pyplot as plt
img=cv2.imread('image.jpg',0)#必须为灰度图,单通道
ret,thresh1=cv2.threshold(img,127,255,cv2.THRESH_BINARY)
ret,thresh2=cv2.threshold(img,127,255,cv2.THRESH_BINARY_INV)
ret,thresh3=cv2.threshold(img,127,255,cv2.THRESH_TRUNC)
ret,thresh4=cv2.threshold(img,127,255,cv2.THRESH_TOZERO)
ret,thresh5=cv2.threshold(img,127,255,cv2.THRESH_TOZERO_INV)
titles = ['Original Image','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
for i in range(6):
    plt.subplot(2,3,i+1),plt.imshow(images[i],'gray')
    plt.title(titles[i])
    plt.xticks([]),plt.yticks([])
plt.show()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在这里插入图片描述


20240217

  • 零点熄灯前看了眼12306,逮着一班6:26从东站出发的高铁,五点薅起老爹送我上路,一路畅通。睡得很好,不到5小时就补到87身体电量,并不困。
  • 回来先把床单被套洗了,结果洗衣机被人搞得一团糟,一堆衣服塞里头我就忍了,里面还到处湿碎的纸巾,太恶心。酒精、消毒剂、洁厕灵好好把洗衣机和厕所清理一遍。本来胡哥那边的厕所只有D间yjc会搞卫生,他1月离校后就越来越脏。
  • 中午食堂又有5块钱一条的红烧鱼,可惜没年前好吃(也许过年嘴吃叼了,但至少调料确实没年前多),勉为其难干了2条。
  • 大晴天,8~15℃,空气质量优(前两天刚寒潮降雨,而且目前市内车辆相对也少些),而且接下来一周都是阴雨,不能偷懒,三点半下去到操场好好练了一下,均配4’07"的万米(很累,而且节奏很乱)。本想慢跑15km,但现在田径场抬腿就是4分配,根本慢不下来,而且太阳直晒,心率很快升到170bpm以上,最后到10km停止,均配4’07",很累,而且节奏很乱,跑得并不舒服。补30个箭步×8组(+20kg),基本到位。
  • AK下午飞机抵沪,然后六点就出去路跑半马,均配4’02",对他来说只是寻常,但对我已是天花板。AK过年几乎每天高原跑山,状态应是极好,不过这次我的状态也不差,下周等嘉伟回来,趁他虚弱期先拿捏一下[奸笑]。

图像的膨胀与腐蚀:cv2.erode(img_origin, kernel, iterations), cv2.dilate(img_origin, kernel, iterations)
在这里插入图片描述

这里的kernel定义的3×3的纯黑阵用于增强或削弱图像中的黑色线条。通过调节iterations可加大剂量。

import numpy as np
import cv2

if __name__ == '__main__':
    img_origin = cv2.imread('bird.jpeg', cv2.COLOR_BGR2LAB)

    kernel = np.ones((3, 3), np.uint8)                
    img_erosion = cv2.erode(img_origin, kernel, iterations=1)              
    img_dilation = cv2.dilate(img_origin, kernel, iterations=1)
    img_all = np.concatenate((img_origin, img_erosion, img_dilation), axis=1)
    cv2.imshow('img: origin, erosion and dilation', img_all)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

20240218

  • 早上去公司打个卡就溜(反正都没人来),顺道去小姨家把老妈寄的东西全给搬回来。我真是服了,上学期奶粉没够喝到过年,于是老妈给我带了(4桶+3袋)奶粉,外加二十多个苹果,十几个丑橘,还有16只生鸡蛋,生怕饿死在学校。好在这回外婆没再买糖,年前收拾宿舍,发现去年外婆买的一堆大白兔、牛轧糖全都过期,都不知道该扔干垃圾还是湿垃圾里(总不能糖和纸拆下来分开丢吧…)。
  • 天气预报又放假,今儿根本没下雨,还是大晴天——更热,等到四点多下楼遛了会儿,太阳还是晒得不行,春乏,没午睡,而且昨晚休息得不好(起床电量只补到87,明明睡到七点半自然醒,十二点半就熄灯了,可能是床不一样),进场就浑身酸,使不上力气,一共是10km多的渐加速(均配4’09"),差强人意,好天气尽量不偷懒。
  • 最后拉伸时胡哥进场,他昨晚刚到宿舍(所以昨天洗衣机那锅垃圾就是B间好大儿ZZY留下的,过分),见面第一句话问我LXY回来没,他真是比我还关心(我是真不知道,根据过往经验,可能是开学前两天,反正都挺晚,但该来的总会来)。我反问他今年年咋过的,他说依然各过各的,就只去苏州找对象玩了两天,似乎也并不容易(我以为至少得见见家长之类的…)。
  • PS:这么好的天气其实最适合出游,但这两天周末居然没徒步,只有今晚很水的一个两小时citywalk,下周倒有去九峰山拉练和陆羽古道,但气温骤降+阴雨天,不过,雨总会停,春天短暂,也是最好的季节。

先腐蚀再膨胀操作称为“开运算”。如下,被涂鸦了几笔的标牌,开运算无法使得痕迹消除,但闭运算可以:

在这里插入图片描述
在这里插入图片描述

import numpy as np
import cv2

def open_op(img):
    kernel = np.ones((3, 3), np.uint8)
    img1 = cv2.erode(img, kernel, iterations=1)
    img2 = cv2.dilate(img1, kernel, iterations=1)
    return img2

def close_op(img):
    kernel = np.ones((3, 3), np.uint8)
    img1 = cv2.dilate(img, kernel, iterations=1)
    img2 = cv2.erode(img1, kernel, iterations=1)
    return img2

if __name__ == '__main__':
    img_origin = cv2.imread('bird.png', cv2.COLOR_BGR2LAB)
    img_opened = open_op(img_origin)
    img_closed = close_op(img_origin)
    img_all = np.concatenate((img_origin, img_opened, img_closed ), axis=1)
    cv2.imwrite('img_opened_closed.jpeg', img_all)
    cv2.imshow('img: origin, erosion and dilation, dilation and erosion', img_all)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

20240219

2023年:马拉松公园(起点)→滨水路→文昌东路→文昌中路→泰州路→高桥路→万福西路瘦西湖路→平山堂东路→扬子江北路→扬子江中路→三湾公园(终点)
2024年:市民中心广场(起点)→文昌东路→运河北路→万福西路→高桥路→泰州路→文昌中路→汶河北路→盐阜西路→瘦西湖路→平山堂东路→平山堂西路→邗江北路→文昌西路→半程终点

  • 扬马报名启动(3.31),路线依然穿越瘦西湖,起点市民广场,最后一段7km的扬子江路也被腰斩,路线地图尚未公布,可能是3月份瘦西湖路附近一所新建的高中校区要开始动工,较于去年,今年路线难度变大,加了平山堂西路,蜀冈景区的爬升太大,美但不好跑。
  • 其实烟花三月是扬州最美的季节,很想请LXY一起来跑扬马。行程酒店都可以给安排好,结束再回芜湖也不远,但她最近一个月完全对我不理睬,人也迟迟未归,我觉得自己岌岌可危,琢磨不出她的态度,可能当面说机会能大一些,但真好尴尬。唉,我自己都快绷不住了。
  • 下班间歇(原计划等AK一起LSD,但是天气坏起来了),3k@3’42"+2k@3’50"+1k@3’33",间歇3分钟和2分钟。原定改测5000米,但是晚上气温骤降,起风还带些雨点,只穿了件短袖+腿套,冻得有些兴奋,第一个1000米3’37",到3000米就扛不住了,临时改为倒金字塔间歇。近期整体强度偏低,确实也该跑点间歇,LSD太浪费时间了,一个月两次足够了。(昨天下午10km,手表显示要休息60小时,今晚跑完变成67小时,过往一般都是24小时,但最近状态并没有变差,只是前两天没适应突然升高的气温)。

OpenCV 里的连通区域分析可以将具有相同像素值且位置相邻的前景像素点组成的图像区域识别出来。有两种像素相邻的定义:

在这里插入图片描述

通过OpenCV的连通区域分析算法,我们可以将下图的水鸭子的外框框出来:

在这里插入图片描述

框架代码如下:

import cv2
import numpy as np
import matplotlib.pyplot as plt

def close_op(img):
    kernel = np.ones((3, 3), np.uint8)
    img1 = cv2.dilate(img, kernel, iterations=1)
    img2 = cv2.erode(img1, kernel, iterations=1)
    return img2

def show_images(images):
    i = 0
    for title in images:
        plt.subplot(2, 3, i+1), plt.imshow(images[title], 'gray')
        plt.title(title)
        plt.xticks([]), plt.yticks([])
        i += 1
    plt.show()

if __name__ == '__main__':
    duck_origin = cv2.imread('duck.jpeg', -1)
    duck_box = duck_origin.copy()
    duck_gray = cv2.cvtColor(duck_box, cv2.COLOR_BGR2GRAY)
    duck_gray_with_closed = close_op(duck_gray)
    ret, duck_binary = cv2.threshold(duck_gray_with_closed, 127, 255, cv2.THRESH_BINARY)
    # 实现识别鸭子区域并用框出来的代码
	ret, labels, stats, centroid = cv2.connectedComponentsWithStats(duck_binary)
	duck_area = sorted(stats, key=lambda s: s[-1], reverse=False)[-2]
	cv2.rectangle(
	    duck_box,
	    (duck_area[0], duck_area[1]),
	    (duck_area[0] + duck_area[2], duck_area[1] + duck_area[3]),
	    (255, 0, 0),
	    3
	)
    images = {
        'duck_origin': duck_origin,
        'duck_gray': duck_gray,
        'duck_gray_with_closed_op': duck_gray_with_closed,
        'duck_binary': duck_binary,
        'duck_box': duck_box
    }
    show_images(images)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

20240220

  • 乳山路那家菜饭店,昨天中午是年后第一次去吃,老板见我跟见鬼似的,拉了个人最多的桌子让我坐里面,生怕我出来加饭,但我还是干了他四碗。然而今天东昌路的店还是不营业,我只得厚着脸皮继续去乳山路我左脚才踏进门,老板就把我轰出去了,说不做菜饭了,吃四碗,钱都挣不到。我不信,进去看了眼,还真没饭(他家还做饺子、面条和汤圆),好,算你狠。转道找另一家店,生意明显差些,但老板和老板娘很淳朴,其实可以随便吃,但我实在不好意思,不到两碗就回去了。
  • 首先,年前我在乳山路一共吃四次,第一次就提前问能不能加饭,老板很自信,说只要你不浪费,随便你吃;第二天再来时,他脸色就不对了,当时年关将近,附近店面大多关门,他家生意好得不行,没跟我扯皮。其次,要不是东昌路那两家到现在不开门,我才懒得来乳山路东昌路两家门对门,竞争激烈,小菜、主菜质量都更好),我在那儿吃了几个月,老板也没撵过我。最后,我要真想吃回本,就点13元骨头汤,但我点的是25元手撕排,我就不信几碗饭还能给吃赔本。
    在这里插入图片描述在这里插入图片描述
  • 昨晚AK回来冒雨15K@4’10"。我也觉得只跑个倒金字塔间歇量太少,10点半去实验楼B1补3000次单摇,不到22分钟跳完,中间只断一次。现在跳绳真的好轻松呀,身体特别轻,补100次双摇,到位。
  • 今晚回来地面潮湿,操场关闭,想着不下雨还是尽量维持室外训练,计划环校线慢跑6圈(15K@4’30"),但节奏差,心率乱,跑成渐加速10K,均配4’21",很累。应该跑休一天,但总觉得状态很好,舍不得不练,身体机能却并没恢复,一个人又慢不下来。
  • 突然就很怀念和LXY五分摇的时光,那时好轻松,现在很沉重…(AK说两个人有说有笑不会累,特么的他当然不累,哼)
    在这里插入图片描述在这里插入图片描述

没啥大用的库pyautogui,我的评价是不如直接pynput,pymouse,pykeyboard操作键盘鼠标

pip install pyautogui
  • 1

移动光标:

import pyautogui
# Get the size of the monitor.
screenWidth, screenHeight = pyautogui.size()
# Get the coordinates of the center of the screen.
x, y = screenWidth / 2, screenHeight / 2
# Move the mouse to the center of the screen.
pyautogui.moveTo(x, y)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

使用size函数来捕获屏幕尺寸,并使用moveTo函数来移动光标。

单击鼠标:

import pyautogui
# Click the left mouse button.
pyautogui.click()
  • 1
  • 2
  • 3

点击功能将在当前鼠标位置执行鼠标左键单击。

使用键盘输入,下面的代码显示了如何输入“Hello, World!”。

import pyautogui
# Type the string "Hello, World!".
pyautogui.typewrite('Hello, World!')
  • 1
  • 2
  • 3

截图

import pyautogui

# Take a screenshot of the entire screen.
screenshot = pyautogui.screenshot()

# Save the screenshot to a file.
screenshot.save('screenshot.png')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

同样,我们可以使用PyAutoGUI自动化并执行其他一些操作。

import pyautogui 
import time 
# 步骤 1:启动程序
pyautogui.press("win")
time.sleep(1)
pyautogui.typewrite("notepad")
time.sleep(1)
pyautogui.press("enter")
# 步骤 2 : 在程序中输入一些文本
time.sleep(2)
pyautogui.typewrite("Hello, world!\n")
# 步骤 3: 保存文件
time.sleep(2)
pyautogui.hotkey("ctrl", "s")
time.sleep(1)
pyautogui.typewrite("example.txt")
time.sleep(1)
pyautogui.press("enter")
# 第四步:关闭程序
time.sleep(2)
pyautogui.hotkey("alt", "f4")
time.sleep(1)
pyautogui.press("tab")
time.sleep(1)
pyautogui.press("enter")
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

20240221

  • 下周应该是最后一周,凑满二月全勤。今日大雨,我还是没等到交接的林TC,步执下周要来趟上海,第一次面基,有些小紧张。
  • 实话说,上海师生恋的丑闻,女方就算未婚,都很难被原谅。我们这代人其实不太可能逃得过公序良俗约束,等上一代人走了,我们的下一代很可能会颠覆三观,但我是个传统的人,我不觉得颠覆是一件好事,能稳定延续这么多年,自然有它的道理,中国这么大,不乱即为治。
  • 跑休日。昨晚在实验室码到11点半回去都不觉得累,一觉醒来电量恢复到98(静息心率回归到40以下,但做了不好的梦,五点被惊醒,又想太多)。我想一定是下肢肌肉太疲劳,精神状态很好,但需要充分恢复。中午减餐,也找不到地方填饱肚子,晚上去小姨家吃点好的呗。
  • 晚归计划核心训练,这组核心我在2月16日做过一回,难度不算高,单组间休息1分钟有些短,三组做完体感较累。破三就得无短板,任何弱点都意味着提前跑崩。2月14日的30km,老爹给我录像了8段跑姿,我总感觉自己上半身很稳定,但摆臂时腰腹向上还是晃,肩背太差,倒三角大哥跑起来上半身是前后左右一点儿都不会动的,极致的力量感。
    在这里插入图片描述

cv.findContours():二值图像寻找轮廓
cv2.drawContours():绘制轮廓

cv.findContours(image, mode, method[, contours[, hierarchy[, offset]]]	) → contours, hierarchy
  • 1

书画提取轮廓

在这里插入图片描述

import cv2 as cv
img = cv.imread("Contours.jpg", flags=1)
imgGray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(imgGray, 127, 255, cv.THRESH_BINARY_INV)
contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
contourPic = cv.drawContours(img, contours, -1, (0, 0, 255), 2)
cv.imshow("ContourPicture", contourPic)
cv.waitKey(0)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

其中127是二值化的阈值,它的设定需要根据具体图像的亮度情况来确定,一般情况下可以先尝试一些常用的值进行实验,再根据实际情况进行微调。


20240222

  • 中午跟宝玺姐去东昌路433号干饭(这是最新发现的东昌路第三家菜饭店),我没好意思干四碗,两碗多就收手了(东昌路就很多做体力活的人去吃,饭量可不比我小)。回头路上,问宝玺过年有没有相亲,她表示还没过29周岁生日,急啥,自从她爸表哥的女儿被催婚到紫砂,家里就再没人敢催婚。
  • 我觉得总需要相识很久才能建立信任,但宝玺说大多数两口子,认识两年内就结婚了,一人说,咱去领个证吧,另一人附和,这事儿就成了;但只要另一人犹豫,就大概率再也成不了。然后她又给我讲个故事,之前有个同事A,毕业想去创业,他对象说你又没经验去创什么业,然后A说那我们去结婚吧,然后,他们就真去结婚了。???
  • 晚归下地铁遇东哥打完球出去吃饭,汇报了下和AK冬训的成果,东哥都震惊了。冬训肯吃苦,开春猛如虎。可能这是这辈子身体最好的一年,怎么造作都不感冒、不生病,吃得多还长不胖,精神亢奋得从起床到躺下,睡眠质量也够好,以后可能再也不会有这么恐怖的状态了。唉,总有一天会发现,身体是真的老了,怎么也挽救不回来,这也是注定的事情。
  • 但人还是得活在当下,跑休一天后,今天起跑前就知道肯定身体猛得不行。1℃,场地潮湿,还飘细雨,我直接短袖紧身裤,手套和帽子都没有,鞋子也没换,前13km压住心率在150bpm上下(均配4’35"),节奏非常好。结果有个白衣小伙非要在内道逆着我跑,还故意来撞,一怒之下我提到3’45"冲2km到力竭,好呀,不让道就创死你
    -在这里插入图片描述

OpenCV 的颜色空间主要有 BGR、HSV、Lab等,cvtColor 函数可以让图像在不同颜色空间转换。例如通过将花的图像转换到 HSV 颜色空间,在HSV空间内过滤出只含有花瓣颜色的像素,从而提取出花瓣。

import numpy as np
import cv2
flower = cv2.imread('flower.jpeg', -1)                                  
hsv = cv2.cvtColor(flower, cv2.COLOR_BGR2HSV) # cv2读取默认是BGR格式
lower_red = np.array([0, 20, 100])   # 花瓣颜色下界
upper_red = np.array([10, 255, 255]) # 花瓣颜色上界
mask = cv2.inRange(hsv, lower_red, upper_red)
res = cv2.bitwise_and(flower, flower, mask=mask)
images = np.concatenate((flower, res), axis=1)
cv2.imshow('flower', images)
cv2.waitKey()
cv2.destroyAllWindows()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

20240223

  • 开学,大家终于陆陆续续地都回来了,生气渐起,然各种破事渐多。
  • 下班回来冻得不行,比昨天还冷,操场不开,状态尚可,回实验楼计划室内训练(爬楼+跳绳)。但又想到LXY可能已经回来,要不试着叫出来去打会儿球?转念一想,快三周没摸拍子,估计年前练起来的又还给嘉伟,感觉现在就急着找打等于自取其辱,遂独自去羽毛球馆先黑练一把。
  • 抓到一只在创智做安卓开发的码农小哥XZB陪我打(他也跑步,跑过一次半马和全马,半马137,全马350,水平很不错),球技很好,感觉不比嘉伟差,把我遛得上气不接下气,我从7.20跟他打到8.15,四方跑跳,心肺爆炸,鞋子都磨破洞了(穿的昨天跑步的休闲鞋,一个急箭步左脚外侧直接裂开,我也快裂开了,打完膝盖都疼了)。其实我真的很不好意思跟比我强的打,感觉浪费别人时间,只是自己在长球,但XZB很客气,并没有嫌我菜,后来还跟我组双打一直打到九点关门,加了好友约我以后继续练。
  • AK约我明天15K@4’30",估摸着不会诚信(他诚信我也不会诚信),4’30"太慢了,15K建议直接4分配开干,麻烦来点强度,尊重一下我冬训的成果。

通过调整图像的直方图调整图像的整体细节,下图左图是浑水鱼,右边清澈鱼。

在这里插入图片描述
框架代码如下:

import numpy as np
import cv2
if __name__ == '__main__':
    fish = cv2.imread('fish.jpeg', -1)
    # TODO(You): 请正确实现浑水摸鱼代码
    b, g, r = cv2.split(fish)
	bx, gx, rx = cv2.equalizeHist(b), cv2.equalizeHist(g), cv2.equalizeHist(r)                                                                         
	fish_enhance = cv2.merge((bx, gx, rx))
	
    images = np.concatenate((fish, fish_enhance), axis=1)
    cv2.imwrite('fish_enhance.jpeg', images)
    cv2.imshow('fish_enhance', images)
    cv2.waitKey()
    cv2.destroyAllWindows()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

注意:直方图均衡化只能对灰度图使用。

  • fish = cv2.imread('fish.jpeg', -1):-1表示读取的图像原格式不变,得到的图像为三通道
  • b, g, r = cv2.split(fish):通道分离,将三通道分离成单通道,为了直方图均衡化
  • cv2.equalizeHist:直方图均衡化
  • fish_enhance = cv2.merge((bx, gx, rx))合并图像

20240224

  • 人生可以是浮萍,但总会有想要扎根的一天。其实我不是觉得只为自己而活不好,但或许真的到了想为别人而活的那天,却发现自己力不能逮。一撇不成人,高处不胜寒,一个人注定是不容易走到终点的。
  • 0~3℃,雨夹雪,早上下得很大,像是一天都不会停的样子,不开心。年后回来还没跟AK跑过,得让他知道我现在有多猛。我真的特别期待全力跟AK跑一次,差距肯定依然巨大,但我想知道现在的上限在哪里。
  • 下午三点瞅了眼外面,居然操场有人跑步,立即通知AK过来开干。2000米,脱外套只穿短袖干(感觉就算赤膊跑都不可能感冒),7000米,AK到场,他看到我直接震惊了,我表示感觉状态极好,4分配顶完15K不是问题,他就跟着我一直热身到13000米(4分配对他来说确是热身),这一段因为有AK陪着,跑得越来越快,越来越轻松。到10000米时,我摘掉墨镜,心率显著降低。最后2000米,AK上来破风给我拉住配速。最终15000米用时59’34"(均配3’58"),成功跑进1小时,节奏完美(匀渐加速),有被爽到。
  • 后来去蜀地源干饭前还是顺路去羽毛球馆看了一眼,大受刺激。我知道LXY打得很好,但没想到打得这么好,动作很干净,而且力量很足,正手劈长劈对角能底线劈到底线,但是反手确实没有嘉伟强。我跟嘉伟说我觉得你打不过她,嘉伟表示虽然没记过分,但他有把握拿下LXY,那就老老实实再跟嘉伟练一个月,先能跟嘉伟五五开再去找死了。
    在这里插入图片描述在这里插入图片描述在这里插入图片描述

图像滤波是在尽可能保留图像细节特征的条件下对目标图像的噪声进行抑制,是常用的图像预处理操作。

  • 平滑滤波(低通滤波),可以抑制图像中的灰度突变,使图像变得模糊,是低频增强的空间域滤波技术。

  • 盒式核是最简单的可分离低通滤波器核。盒式核结构简单,模板区域中各像素点的系数相同。

函数cv.boxFilter()可以实现直方图均衡化。

函数说明:

cv.boxFilter(src, ddepth, ksize[, dst[, anchor[, normalize[, borderType]]]]) → dst
  • 1

demo:

import cv2 as cv
img = cv.imread("Lena.png")
ksize = (5, 5)
imgBoxFilter = cv.boxFilter(img, -1, ksize=ksize)
cv.imshow("BoxFilter", imgBoxFilter)
key = cv.waitKey(0)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

20240225

  • Black Sunday,我觉得自己好久没有这么颓废,一天没跟活人说几句话。
  • 下午先是与嘉伟练球未遂,嘉伟开始留起小胡子。于是晚饭后,想独自摇会儿消食,勉强当作训练。遇胡哥,一起摇了不到10圈却发现快递刚到站,只好作罢(开学季站点东西太多,想着还是别拖到明天)。
  • 取的时候碰到好久不见的宋某人,他年前一大把东西落在菜鸟没拿。好不容易又捞到个活人能说两句(我是错怪他了,他最近走路确实不方便,但我也不是很懂怎么会足底筋膜炎,力量差小腿拉伤就算了,明明他近半年三天打鱼,两天晒网,跑得也不多,而且跑姿比我要好,可能是鞋的问题?)。结果,走到一半宋某发现自己车落在菜鸟,无语,我只得帮他把几个大件搬回武川。
  • 送走宋某,最后还是去操场把力量补了,重振精神,30箭步×10组(+20kg),现在20kg已经很轻松了,至少能再做两组,但还是罢了,越快,越慢,越容易失败。

在图像处理中,梯度反映了像素值的最大变化率的方向,能够突出和提取边缘,常用于工业检测中产品缺陷检测和自动检测的预处理。

OpenCV 提供了三种梯度算子:SobelScharrLaplacianSobel 梯度算子是高斯平滑和微分求导的联合运算,抗噪声能力强。

Sobel梯度算子很容易通过卷积操作cv.filter2D实现,OpenCV也提供了函数cv.Sobel实现Sobel梯度算子。

函数说明:

cv.Sobel(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]]) → dst
  • 1

Sobel算子从Lena图像提取边缘,效果如下:
在这里插入图片描述

import cv2 as cv

img = cv.imread("lena.png", flags=1)
imgGray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

SobelX = cv.Sobel(imgGray, cv.CV_16S, 1, 0)  # 计算 x 轴方向
SobelY = cv.Sobel(imgGray, cv.CV_16S, 0, 1)  # 计算 y 轴方向
absX = cv.convertScaleAbs(SobelX)  # 转回 uint8
absY = cv.convertScaleAbs(SobelY)  # 转回 uint8
SobelXY = cv.addWeighted(absX, 0.5, absY, 0.5, 0)  # 用绝对值近似平方根
cv.imshow("Sobel gradient", SobelXY)
cv.waitKey(0)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

20240226

  • 晚上吃完饭,老表非要给我秀车技,我能不知道他几斤几两,再三推让,他非要晚高峰送我回学校,结果比我坐地铁慢了一倍时间,我可真是谢谢他了。下车碰到胡哥来跑步,遂陪他摇十几分钟消食,聊了好多高中的事,没想到当年我的同桌是他同学的弟弟,地球可真是个小圈,感觉只要说到高中的好事烂事,男人不管多长多大就依然是少年。
  • 昨天做完力量,很适合间歇(好久不练间歇)。下午趁着有太阳,计划【1000米@3’35"-3’40"】×8组(间歇时间1:1)。第一个1000米想试试全力能跑到多少,结果跑得特别僵硬(可能是没热开),连3’25"都没进,大失所望,动力一下子就没了。补8个【600米快+200米走】的间歇,2’07~2’10",不是很到位。感觉长距离训练对中短距离能力提升很小,现在跟两三月前水平差不太多,反过来想中短距离间歇对全马用处估计也不大,少练1000米以下的间歇。
  • 新食堂现在每天中午都有5块一条的红烧鱼,八年了,终于在学校实现吃鱼自由,深得我心。
    在这里插入图片描述在这里插入图片描述

看到一个有趣的paper(arxiv@2307.04721),有人发现大模型很擅长做行测题(或者说是图形的智商测试题),因为这是个典型的序列预测,就是发现大模型不仅在文本的序列生成上做得很好,在这种非文本的模式(pattern)预测上做得也很好,其实本质上是对embedding的序列预测得好,但是对于带数字的那种找规律就行不通了,然后神奇的事情来了,如果你不把数字当成数字,而是用等价的一些字符去替代(比如你可以用十个不同的汉字去替代0123456789这十个阿拉伯数字),模型就依然保持很高的预测性能。其实本质感觉就是,任何规律都可以被函数拟合,只要参数足够多,样本量足够大,但是这个世界的规律真的是可以被彻底概括的吗?

在这里插入图片描述

仿射变换(矩阵旋转):

cv2.warpAffine(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]]) -> dst
  • 1

demo:

import cv2 as cv
import numpy as np

if __name__ == '__main__':
    img = cv.imread("affine1.jpg")

    pts1 = np.float32([[50, 50], [200, 50], [50, 200]])
    pts2 = np.float32([[50, 100], [200, 50], [100, 250]])
    MA = cv.getAffineTransform(pts1, pts2)
    dst = cv.warpAffine(img, MA, img.shape[:2])

    cv.imshow("Origin image", img)
    cv.imshow("Affine transform", dst)
    cv.waitKey(0)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

20240227

  • 三点接水时扫了眼操场,一眼就看见的显眼的红色。手机都没拿,就径直下楼去,但真不如眼睛瞎了算了。
  • 教练,我不想再懦夫了。

单应性变换:在2D平面实现3D图像视角转换后的模样。

import cv2
import numpy as np

if __name__ == '__main__':
    img_src = cv2.imread('rust_face_src.jpeg')
    pts_src = np.array([[0, 0], [570, 0], [570, 1078], [0, 1078]])
    img_dst = cv2.imread('rust_face_dest.jpeg')
    pts_dst = np.array([[63, 285], [378, 224], [427, 689], [84, 820]])
    dw, dh = img_dst.shape[1], img_dst.shape[0]
    h, status = cv2.findHomography(pts_src, pts_dst)
    img_out = cv2.warpPerspective(img_src, h, (dw, dh))
    images = np.concatenate((img_src[0: dh, 0:dw, 0:3], img_dst, img_out), axis=1)
    cv2.imshow('homography', images)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

20240228

  • 春寒料峭,不适时宜的雨。
  • 但没有什么不开心是疯一场解决不了的。今晚被四个人轮了整整两个小时,水都没喝上一口,不停歇地在练单打,直到看到球都没有再跑去接的力气为止,浑身湿透,筋疲力竭。难得晚上有场地,还有高手不吝虐我,反正体能好得很,当然物尽所用。
  • 第一个中分小哥非常强,校队水平,本来他自己占了个场地在独自练反手,厚着脸皮让他陪我打会儿,动作一致性极好,同样的动作能准确打出钓鱼/吊球/对角,猜球猜到怀疑人生,吃了四五个才能勉强反应过来打回一球,而且反手死角球能轻松以平高打回底线,看起来瘦弱,但反手就是这么有力。然后跟两个不会打球的胖子找虐,最后前端小哥XZB干我到结束,发高远永远被他杀直线,但我就是想试试怎么才能很好地劈回正手低位球,打在这个位置太难受了。技术这个东西对我来说还是太奢侈,放短球什么的太baby了,就该重炮互干,杀死为快。
  • 计划周五跑休(周六强度,周日NRC招募),周三周四应该有丶强度。本来昨天的计划就是慢跑恢复,但一开始跑得实在太慢,后来一个个又莫名飞快,宋某人日常烟雾弹,恢复期顺个5K就顶到19分半,LXY也跟发疯似的像是在跑间歇,无法理解。

Harris特征提取亭子的瓦片特征:

import cv2
import numpy as np
def close_op(img):
    kernel = np.ones((5, 5), np.uint8)
    img1 = cv2.dilate(img, kernel, iterations=1)
    img2 = cv2.erode(img, kernel, iterations=1)
    return img2
def filter_green(img):
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    lower_green = np.array([50, 100, 50])
    upper_green = np.array([70, 255, 255])
    lower_red = np.array([0, 20, 100])
    upper_red = np.array([10, 255, 255])
    mask = cv2.inRange(hsv, lower_red, upper_red)
    res = cv2.bitwise_and(img, img, mask=mask)
    return close_op(res)
if __name__ == '__main__':
    imput_img = 'tower.jpeg'
    ori = cv2.imread(imput_img)
    image = cv2.imread(imput_img)
    no_green = filter_green(image)
    # Harris 特征提取
	gray = cv2.cvtColor(no_green, cv2.COLOR_BGR2GRAY)
	gray = np.float32(gray)
	dst = cv2.cornerHarris(gray, 2, 3, 0.04) 
	dst = cv2.dilate(dst, None) # 这里需要做一次膨胀处理
	image[dst > 0.01*dst.max()] = [0, 0, 255]
    images = np.concatenate((ori, no_green, image), axis=1)
    cv2.imwrite('tower_harris.jpeg', images)
    cv2.imshow('tower_harris', images)
    cv2.waitKey()
    cv2.destroyAllWindows()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

20240229

  • 长雨未歇。晚九点与AK进行室内训练,实验楼上下10组(B1-15F)+3000次跳绳。
  • 上周六不讲武德偷袭了AK,今晚就被狠狠地摁在地上摩擦。从第三组起,6F后我再看不到他车尾灯,每组都能拉我1~2层的距离。虽然爬楼前,我先跳了2000次绳等他过来,但爬完后每次跳不到100下就濒临力竭,一个人爬楼不会累成这样,但节奏被带乱就彻底崩溃了。
  • 农历生日,昨天请宋某今晚吃饭(这顿饭从年前拖到现在),他告诉我今天补考15.15-17.15 + 17.30-21.30,本来想一个人出去吃些好的算了,转念明天又是Last Day,不如延到明天跟那边的人吃顿饭告别,以后大约很久不会再见了罢。
  • PS:最近新食堂的快餐深得我心,菜品真不错(尤其是酸菜猪骨头、酸菜鱼,毛豆系列,以及不常出现的红烧鱼)。但除快餐外,纯抢钱,新园生煎包3元一个(上学期还是2元,我问为什么,阿姨说里面有虾丸,实话说只吃出玉米粒),16年生煎包才1元;以及新食堂的小馄饨,只是换个大碗,从4块卖到6块,上学期专门数了数,从12个变成15个(所以应该是4÷12×15=5元,而且16年新园小馄饨才2.5元,也是12个);更离谱的是,新食堂1楼居然开始卖泡面,就桶装康师傅堆那儿,也不知道怎么制作,但是红烧牛肉面18元,老坛酸菜19元,反正我不吃,爱咋咋地。

亭子和水中的倒影做特征匹配
在这里插入图片描述

import cv2
import numpy as np

if __name__ == '__main__':
    img1 = cv2.imread('tower01.jpg', -1)
    img2 = cv2.imread('tower02.jpg', -1)
	orb = cv2.ORB_create(nfeatures=500)
	kp1, des1 = orb.detectAndCompute(img1, None)
	kp2, des2 = orb.detectAndCompute(img2, None)
	bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
	matches = bf.match(des1, des2)
	matches = sorted(matches, key=lambda x: x.distance)
	match_img = cv2.drawMatches(img1, kp1, img2, kp2, matches[:50], None)	    
    cv2.imwrite('tower_match.jpeg', match_img)
    cv2.waitKey()
    cv2.destroyAllWindows()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

使用OpenCV光流分析,跟踪蚂蚁的轨迹(ant.mp4)

代码框架:

import numpy as np
import cv2

if __name__ == '__main__':
    cap = cv2.VideoCapture('ant.mp4')
    # ShiTomasi 角点检测参数
    feature_params = dict(
        maxCorners=100,
        qualityLevel=0.5,
        minDistance=30,
        blockSize=10
    )
    # Lucas Kanada 光流检测参数
    lk_params = dict(
        winSize=(15, 15),
        maxLevel=2,
        criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03)
    )
    # 获取第一帧并发现角点
    ret, last_current_frame = cap.read()
    last_gray = cv2.cvtColor(last_current_frame, cv2.COLOR_BGR2GRAY)
    p0 = cv2.goodFeaturesToTrack(last_gray, mask=None, **feature_params)
    mask = np.zeros_like(last_current_frame)
    color = np.random.randint(0, 255, (100, 3))
    while (1):
        ret, current_frame = cap.read()
        if not ret:
            break
        current_gray = cv2.cvtColor(current_frame, cv2.COLOR_BGR2GRAY)
        # TODO(You): 请在此编写光流跟踪和绘制代码
		p1, st, err = cv2.calcOpticalFlowPyrLK(
		    last_gray, current_gray, p0, None, **lk_params)	
		good_new = p1[st == 1]
		good_old = p0[st == 1]	
		for i, (new, old) in enumerate(zip(good_new, good_old)):
		    a, b = new.ravel()
		    c, d = old.ravel()
		    a, b = int(a), int(b)
		    c, d = int(c), int(d)
		    mask = cv2.line(mask, (a, b), (c, d), color[i].tolist(), 2)
		    current_frame = cv2.circle(
		        current_frame, (a, b), 5, color[i].tolist(), -1)
        current_img = cv2.add(current_frame, mask)
        cv2.imshow('current_img', current_img)
        k = cv2.waitKey(30) & 0xff
        if k == 27:
            break
        last_gray = current_gray.copy()
        p0 = good_new.reshape(-1, 1, 2)
    cv2.destroyAllWindows()
    cap.release()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

20240301

  • 4.21淮安出签,候补序号很靠前,离家近,全马才3000人,又是网红赛事,口碑好(完赛有龙虾吃,不过咱邵伯龙虾一向不待见盱眙龙虾),首马大概率就在淮安,老天会眷顾我一次好天气吗?不想再输了。
  • 最近三个月,12月185.7km(@4’29");1月231.5km(@4’36");2月141.4km(@4’15")。其实我有氧积累很少,因为费时间,但许多业余高手就是纯靠堆有氧达到极高水平。如南航周某(目前考研到中南),全马PB233(放言3.24锡马不达健将(220)直播自宫,是个狠人),近半年平均月跑达到令人咋舌的1300km,每天5分开外慢摇40~50km,不能说不自律,但确实挺闲…
  • 不过人呢,确实该走得慢些。最后一日,搞定部署和交接。步执又被鸽,估计是有些焦头烂额,但似乎再帮不了他什么。平心而论这六个月,有被认同,有所改变,我很满意,也很喜欢这里的人和事。些许不舍,却也别无选择。
  • 做了一晚上食客和听客,其实我最近还挺健谈,但兴致不高,说不出话来。而且有些高估自己的胃,挑了两条2斤重的黑鱼,小哥很好心地提醒我黑鱼分量足,菜就别多点了,我心想你不必多言,结果吃得叫那个撑。顺心就好,有些事又何所谓呢?随遇而安,即兴而动,未尝不可。
  • 曲高和寡,曲终人散。后会有期。

在视频分析(或视频结构化)应用开发中,多目标跟踪是非常重要的一个环节。它能有效弥补上一个目标检测环节中算法的不足,如检测算法输出坐标不稳定、漏检等。与此同时,跟踪算法输出的目标轨迹(track-id)对于应用下阶段的行为分析环节也有着至关重要的作用。下面是常见视频分析类应用系统结构:

目标检测算法输出单帧检测结果,目标跟踪算法负责将前后2帧中的目标关联起来、给予唯一标识track-id。假设t帧中检测到了M个目标,t+1帧中检测到了N个目标,跟踪算法本质上是M->N的匹配关联过程。

匹配过程中,目标可以分为以下三大类:

  1. matched_tracks,t帧目标出现,t+1帧该目标仍然出现,算法匹配上。
  2. unmatched_tracks,t帧目标出现,t+1帧该目标消失,算法未匹配上。
  3. unmatched_detections,t帧目标不存在,t+1帧该目标出现,新增检测目标。

其中,对于2和3来说,跟踪算法需要考虑:

t帧目标出现,t+1帧目标其实仍然存在,但是检测算法出现短暂漏检,误认为其消失。此时的解决方案是: 某帧未被匹配到的tracks不要立即清除,而是做若干帧的缓存,等待若干帧后检测算法恢复检测
t帧目标不存在,t+1帧该目标仍然不存在,但是检测算法出现短暂误检,误认为其出现。此时的解决方案是:新增的检测目标不要立即生效,而是做若干帧的缓存,等检测算法连续检测超过若干帧、并且都能匹配关联上后再生效

之所以要考虑以上2点,主要原因是对于连续视频帧而言,大部分检测算法基本无法做到100%连续、稳定检测,出现短暂的误检、漏检非常正常。

跟踪代码示例:

# 定义跟踪算法类
class Tracker(object):
  # 初始化参数
  def __init__(self, max_age=1, min_hits=3, iou_threshold=0.3):
    self.max_age = max_age
    self.min_hits = min_hits
    self.iou_threshold = iou_threshold
    self.trackers = []
    self.frame_count = 0

  # 跟踪函数,每帧检测结果返回后,调用一次update
  def update(self, dets=np.empty((0, 5))):
    self.frame_count += 1
    trks = np.zeros((len(self.trackers), 5))
    to_del = []
    ret = []
    for t, trk in enumerate(trks):
      pos = self.trackers[t].predict()[0]
      trk[:] = [pos[0], pos[1], pos[2], pos[3], 0]
      if np.any(np.isnan(pos)):
        to_del.append(t)
    trks = np.ma.compress_rows(np.ma.masked_invalid(trks))
    for t in reversed(to_del):
      self.trackers.pop(t)
    # 匹配关联
    matched, unmatched_dets, unmatched_trks = associate_detections_to_trackers(dets, trks, self.iou_threshold)

    # 后处理逻辑
    # TO-DO your code...
    # 更新matched_tracks
    for m in matched:
      self.trackers[m[1]].update(dets[m[0], :])

    # 初始化unmatched_detections,假设是当前帧新出现的检测目标
    for i in unmatched_dets:
        trk = KalmanBoxTracker(dets[i,:])
        self.trackers.append(trk)
    i = len(self.trackers)
    for trk in reversed(self.trackers):
        d = trk.get_state()[0]
        # 输出满足条件的tracks
        if (trk.time_since_update <= self.max_age) and (trk.hit_streak >= self.min_hits or self.frame_count <= self.min_hits):
          ret.append(np.concatenate((d,[trk.id+1])).reshape(1,-1))    
        i -= 1
        # 移除超过self.max_age次的漏检目标
        if(trk.time_since_update > self.max_age):
          self.trackers.pop(i)  
    # 返回跟踪结果 [[left, top, right, bottom, track-id]...]
    if(len(ret) > 0):
      return np.concatenate(ret)
    return np.empty((0, 5))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

其中:

self.max_age代表跟踪算法允许出现的最大漏检帧数
self.min_hints代表跟踪算法要求的最低连续匹配帧数
self.trackers代表跟踪算法维持的目标集合(已生成track-id)
update(self, dets)代表跟踪函数,其中参数dets代表t+1帧中目标检测结果
list[[left, top, right, bottom, score]...],即t+1帧中待匹配的detections
associate_detections_to_trackers(...) 代表IOU+卡尔曼滤波匹配算法,返回上面提到的matched_tracks, unmatched_tracks, unmatched_detections三个值
time_since_update代表目标当前漏检帧数
hit_streak代表目标当前连续匹配帧数


20240302

  • 下午三点训练,停跑3天,找不到节奏。虽然三四五
    声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/582569
推荐阅读
相关标签