【NCC】之三:FFT(DFT)加速协方差的计算

news2025/2/28 3:03:48

FFT加速计算两个图的协方差

文章目录

  • <center> FFT加速计算两个图的协方差
    • 1. 傅里叶变换和卷积
      • 1.1 卷积定理
      • 1.2 空域卷积和频域乘积的复杂度
    • 2. opencv中的DFT
    • 3. FFT用于NCC
    • 4. 测试结果
    • 部分代码

1. 傅里叶变换和卷积

1.1 卷积定理

在这里插入图片描述

图片来源
在空域上的卷积就是上面的动图所展示的过程,把两个图重叠的部分相乘再相加,不断滑动一个图到所有可重叠的地方,计算完后得到卷积的结果。
根据卷积定理:两个函数的卷积等于两个函数在频率域相乘。用式子表示如下:
f ( x ) ∗ g ( x ) = F ( ω ) ⋅ G ( ω ) f(x)*g(x)=F(\omega)\cdot G(\omega) f(x)g(x)=F(ω)G(ω)
也就是说如果我们把上面的两个图像转换到频率域,上述的卷积过程就可以变成两个频率域图像对应点的相乘(频率域的两个图大小相同)。转到频域再相乘的方式主要的耗时都在傅里叶变换和逆变换那里,因为最后的频域图相乘那里复杂度只有平方项,而傅里叶变换和逆变换的的复杂度的阶数高于2,傅里叶变换这一步有非常成熟的快速傅里叶变换(FFT)可以使用来加速这一个过程。

1.2 空域卷积和频域乘积的复杂度

假设两个图像分别为 A N × N , B M × M , M ≤ N A_{N\times N},B_{M\times M},M\leq N AN×N,BM×M,MN,则在空域的卷积的时间复杂度是 O ( N 2 ∗ M 2 ) O(N^2*M^2) O(N2M2)

FFT的时间复杂度 O ( N 2 log ⁡ N ) O(N^2\log N) O(N2logN)
注意这里的复杂度没有错,有人可能会觉得应该是 O ( N 2 log ⁡ N 2 ) O(N^2\log N^2) O(N2logN2),但是在表示复杂度的 O ( ) O() O()表示法中可以忽略系数只关注最高阶的项数, O ( N 2 log ⁡ N 2 ) = O ( 2 ∗ N 2 log ⁡ N ) = O ( N 2 log ⁡ N ) O(N^2\log N^2)=O(2*N^2\log N)=O(N^2\log N) O(N2logN2)=O(2N2logN)=O(N2logN)

在这里插入图片描述

上图来源

复杂度n=10n=20
log ⁡ n \log n logn12.996
n n n1020
n log ⁡ n n\log n nlogn1059.9
n 2 n^2 n2100400
2 n 2^n 2n10241048576
n ! n! n!36288002,432,902,008,176,640,000

上面两种方式的复杂度可以看成是 n 2 , n log ⁡ n n^2 ,n\log n n2,nlogn的差别,可以看到当两个要卷积图像都比较大时采用FFT加速的方式还是可以获得非常可观的效果。

2. opencv中的DFT

opencv dft 官网介绍

void cv::dft	(	InputArray 	src,
					OutputArray 	dst,
					int 	flags = 0,
					int 	nonzeroRows = 0 
				)	
  • src input array that could be real or complex.
  • dst output array whose size and type depends on the flags .
  • flags transformation flags, representing a combination of the DftFlags
  • nonzeroRows when the parameter is not zero, the function assumes that only the first nonzeroRows rows of the input array (DFT_INVERSE is not set) or only the first nonzeroRows of the output array (DFT_INVERSE is set) contain non-zeros, thus, the function can handle the rest of the rows more efficiently and save some time; this technique is very useful for calculating array cross-correlation or convolution using DFT.

根据官网的示例和learning opencv3中的示例实现的卷积如下


/**
 * @brief 用FFT实现两个图像的卷积,src: H*W ,kernel: h*w,
 * 把卷积的过程想象成kernel在src上滑动,记在src上和kernel对应的子图像块为Sxy,
 * conv(x,y) = \sigma \sigma Sxy(i,j)*kernel(i,j),i in [0,h) ,j in [0,w)
 * output: (H-h+1)*(W-w+1)
 * 
 * @param src  : CV_8UC1的图像
 * @param kernel  : CV_8UC1的图像
 * @param output  : CV_32FC1的图像
 * @return int 
 */
int convFFT(const cv::Mat &src, const cv::Mat &kernel, cv::Mat& output)
{
	if (src.empty() || kernel.empty())
	{
        MYCV_ERROR(kImageEmpty,"input is empty");
		return kImageEmpty;
	}
	int dft_h = cv::getOptimalDFTSize(src.rows + kernel.rows - 1);
	int dft_w = cv::getOptimalDFTSize(src.cols + kernel.cols - 1);

	cv::Mat dft_src = cv::Mat::zeros(dft_h, dft_w, CV_32F);
	cv::Mat dft_kernel = cv::Mat::zeros(dft_h, dft_w, CV_32F);

	cv::Mat dft_src_part = dft_src(cv::Rect(0, 0, src.cols, src.rows));
	cv::Mat dft_kernel_part = dft_kernel(cv::Rect(0, 0, kernel.cols, kernel.rows));

	//把src,kernel分别拷贝放到dft_src,dft_kernel左上角
	src.convertTo(dft_src_part, dft_src_part.type());
	kernel.convertTo(dft_kernel_part, dft_kernel_part.type());

	cv::dft(dft_src, dft_src, 0, dft_src.rows);
	cv::dft(dft_kernel, dft_kernel, 0, dft_kernel.rows);

	cv::mulSpectrums(dft_src, dft_kernel, dft_src, 0, true);
	
	int output_h = abs(src.rows - kernel.rows) + 1;
	int output_w = abs(src.cols - kernel.cols) + 1;
	cv::dft(dft_src, dft_src, cv::DFT_INVERSE + cv::DFT_SCALE, output_h);;

	cv::Mat corr = dft_src(cv::Rect(0, 0, output_w,output_h));

	output = corr;

    return kSuccess;
}

3. FFT用于NCC

NCC前情提要
记 T m × n 为目标图 ( t a r g e t ) 记T_{m\times n}为目标图(target) Tm×n为目标图(target), S M × N S_{M\times N} SM×N为源搜索图(source), S x , y S_{x,y} Sx,y为S中以点 ( x , y ) (x,y) (x,y)为左上角的和T大小相同的子图, R ( M − m + 1 ) × ( N − n + 1 ) R_{(M-m+1)\times (N-n+1)} R(Mm+1)×(Nn+1)为匹配的结果图,则 R ( x , y ) = c o v ( S x , y , T ) σ ( S x , y ) σ ( T ) R(x,y)=\frac{cov(S_{x,y},T)}{\sigma(S_{x,y})\sigma(T)} R(x,y)=σ(Sx,y)σ(T)cov(Sx,y,T)

其中 c o v ( S x , y , T ) = E ( S x , y T ) − E ( S x , y ) E ( T ) = Σ i = 1 m Σ j = 1 n S x , y ( i , j ) T ( i , j ) m n − S x , y ˉ T ˉ \begin{aligned} cov(S_{x,y},T) &=E(S_{x,y}T)-E(S_{x,y})E(T)\\ &=\frac{\Sigma_{i=1}^{m}\Sigma_{j=1}^{n}S_{x,y}(i,j)T(i,j)}{mn} - \bar{S_{x,y}}\bar{T} \end{aligned} cov(Sx,y,T)=E(Sx,yT)E(Sx,y)E(T)=mnΣi=1mΣj=1nSx,y(i,j)T(i,j)Sx,yˉTˉ
图像块 S x , y S_{x,y} Sx,y的均值计算过程已经用积分图加速过了,NCC的速度从opencv的八百分之一提升到三百分之一。在协方差的计算过程中一直会用到模板图和 S x , y S_{x,y} Sx,y的逐像素相乘再求和的结果,就是下式
Σ i = 1 m Σ j = 1 n S x , y ( i , j ) T ( i , j ) \Sigma_{i=1}^{m}\Sigma_{j=1}^{n}S_{x,y}(i,j)T(i,j) Σi=1mΣj=1nSx,y(i,j)T(i,j)
从整个图像的计算过程来看这就是卷积!而卷积天然就适合用FFT来加速。用一个矩阵来存储模板和源图的卷积结果,上面的计算式就变为访问卷积结果某个位置的值。

4. 测试结果

source image size w,h = (500,500)
target image size w,h = (100,100)
my NCC run 10 times,average use 12.000000 ms
min_value=-0.045359 , min_loc(x,y)=(316,121),    max_value=0.044341,max_loc(x,y)=(213,264)
opencv NCC run 10 times,average use 4.000000 ms
min_value=-0.045360 , min_loc(x,y)=(316,121),    max_value=0.044340,max_loc(x,y)=(213,264)
source image size w,h = (600,600)
target image size w,h = (100,100)
my NCC run 10 times,average use 16.000000 ms
min_value=-0.046514 , min_loc(x,y)=(186,308),    max_value=0.044515,max_loc(x,y)=(444,178)
opencv NCC run 10 times,average use 7.000000 ms
min_value=-0.046515 , min_loc(x,y)=(186,308),    max_value=0.044514,max_loc(x,y)=(444,178)
source image size w,h = (700,700)
target image size w,h = (100,100)
my NCC run 10 times,average use 24.000000 ms
min_value=-0.044219 , min_loc(x,y)=(428,192),    max_value=0.042701,max_loc(x,y)=(347,262)
opencv NCC run 10 times,average use 8.000000 ms
min_value=-0.044219 , min_loc(x,y)=(428,192),    max_value=0.042701,max_loc(x,y)=(347,262)
source image size w,h = (800,800)
target image size w,h = (100,100)
my NCC run 10 times,average use 30.000000 ms
min_value=-0.045473 , min_loc(x,y)=(452,292),    max_value=0.044728,max_loc(x,y)=(298,175)
opencv NCC run 10 times,average use 10.000000 ms
min_value=-0.045473 , min_loc(x,y)=(452,292),    max_value=0.044728,max_loc(x,y)=(298,175)
source image size w,h = (900,900)
target image size w,h = (100,100)
my NCC run 10 times,average use 39.000000 ms
min_value=-0.046390 , min_loc(x,y)=(716,347),    max_value=0.043345,max_loc(x,y)=(591,252)
opencv NCC run 10 times,average use 13.000000 ms
min_value=-0.046390 , min_loc(x,y)=(716,347),    max_value=0.043345,max_loc(x,y)=(591,252)
source image size w,h = (1000,1000)
target image size w,h = (100,100)
my NCC run 10 times,average use 52.000000 ms
min_value=-0.046292 , min_loc(x,y)=(727,726),    max_value=0.048027,max_loc(x,y)=(664,237)
opencv NCC run 10 times,average use 18.000000 ms
min_value=-0.046293 , min_loc(x,y)=(727,726),    max_value=0.048028,max_loc(x,y)=(664,237)
source image size w,h = (1100,1100)
target image size w,h = (100,100)
my NCC run 10 times,average use 57.000000 ms
min_value=-0.047824 , min_loc(x,y)=(116,699),    max_value=0.049075,max_loc(x,y)=(319,546)
opencv NCC run 10 times,average use 21.000000 ms
min_value=-0.047824 , min_loc(x,y)=(116,699),    max_value=0.049075,max_loc(x,y)=(319,546)
source image size w,h = (1200,1200)
target image size w,h = (100,100)
my NCC run 10 times,average use 75.000000 ms
min_value=-0.048051 , min_loc(x,y)=(1095,47),    max_value=0.051388,max_loc(x,y)=(95,634)
opencv NCC run 10 times,average use 25.000000 ms
min_value=-0.048050 , min_loc(x,y)=(1095,47),    max_value=0.051388,max_loc(x,y)=(95,634)
请按任意键继续. . .

我实现的NCC耗时从opencv的800多倍,用积分图降到300多倍,用FFT降到现在的2-3倍。没想到FFT加速这么多!FFT YYDS!

部分代码


/**
 * @brief 模板匹配,归一化交叉相关算法。衡量模板和待匹配图像的相似性时
 * 用(Pearson)相关系数来度量。
 * r=cov(X,Y)/(sigma(X) * sigma(Y))
 * 其中cov(X,Y): 表示两个变量的协方差
 * cov(X,Y) = E[(X-E(x)) * (Y-E(Y))] = E(XY) - E(x)E(Y)
 * sigma(X): 表示X变量的标准差
 * sigma(Y): 表示Y变量的标准差
 *
 * @param source : 搜索图CV_8UC1格式
 * @param target :模板图CV_8UC1格式
 * @param result : 匹配结果的map图
 * @return int : 程序运行的状态码
 */
int NormalizedCrossCorrelationFFT(
	const cv::Mat &source,
	const cv::Mat &target,
	cv::Mat &result
)
{
	if (source.empty() || target.empty())
	{
		MYCV_ERROR(kImageEmpty, "NCC empty input image");
		return kImageEmpty;
	}
	int H = source.rows;
	int W = source.cols;
	int t_h = target.rows;
	int t_w = target.cols;
	if (t_h > H || t_w > W)
	{
		MYCV_ERROR(kBadSize, "NCC source image size should larger than targe image");
		return kBadSize;
	}

	//r = cov(X,Y)/(sigma(X) * sigma(Y))
	//sigma(X) = sqrt(var(X))
	int r_h = H - t_h + 1; //结果图的高度
	int r_w = W - t_w + 1;
	cv::Mat integral_image;//source的积分图
	cv::Mat sq_integral;//source 的像素平方的积分图
	integral(source, integral_image, sq_integral);
	//cv::integral(source, integral_image, sq_integral, CV_64FC1, CV_64FC1);

	//计算模板图在source上的卷积
	cv::Mat conv;
	convFFT(source, target, conv);

	const double target_size = t_h * t_w;

	double target_mean = calculateMean(target);
	double target_var = calculateVariance(target, target_mean);
	double target_std_var = std::sqrt(target_var);
	result = cv::Mat::zeros(cv::Size(r_w, r_h), CV_32FC1);

	double region_sum = 0;
	double region_sq_sum = 0;
	for (int row = 0; row < r_h; row++)
	{
		float * p = result.ptr<float>(row);
		float * convp = conv.ptr<float>(row);
		for (int col = 0; col < r_w; col++)
		{
			cv::Rect ROI(col, row, t_w, t_h);//source上和目标图匹配的子图
			cv::Mat temp = source(ROI);
			//计算source中对应子块的均值
			getRegionSumFromIntegralImage(integral_image, col, row, col + t_w - 1, row + t_h - 1, region_sum);
			double temp_mean = region_sum / target_size;

			//计算两个图的协方差
			//cov(X,Y) = E(X*Y) - E(X)*E(Y)
			double cov = (*(convp + col)) / target_size - temp_mean * target_mean;
			//double cov = calculateCovariance(temp, target, temp_mean, target_mean);

			//计算source中对应子块的方差
			getRegionSumFromIntegralImage(sq_integral, col, row, col + t_w - 1, row + t_h - 1, region_sq_sum);

			double temp_var = (region_sq_sum - temp_mean * region_sum) / target_size;
			double temp_std_var = std::sqrt(temp_var);
			p[col] = cov / ((temp_std_var + 0.0000001) * (target_std_var + 0.0000001));
		}
	}


	return kSuccess;
}

void test_NCC_speed()
{
    const int TIMES = 10;
    std::string src_path = "data\\source.jfif";
    std::string target_path = "data\\target.jfif";
    std::string log_path = "ncc_speed.txt";
    cv::Mat source, target, result;
    //source = cv::imread(src_path, cv::IMREAD_GRAYSCALE);
    //target = cv::imread(target_path, cv::IMREAD_GRAYSCALE);
    std::chrono::steady_clock::time_point start_time,end_time;
    double myncc_runtime = 0, opencv_runtime = 0;

    auto logger = spdlog::basic_logger_mt("NCC", log_path);
    logger->set_level(spdlog::level::critical);
    // location
    double min_value, max_value;
    cv::Point min_loc, max_loc;
    for (int src_size = 500; src_size <= 1200; src_size += 100)
    {
        source = cv::Mat(cv::Size(src_size, src_size), CV_8UC1);
        target = cv::Mat(cv::Size(100, 100), CV_8UC1);
        cv::randu(source,cv::Scalar(0),cv::Scalar(255));
        cv::randu(target,cv::Scalar(0),cv::Scalar(255));
        logger->info("src_size:(h,w)=({0},{1}), target_size:(h,w)=({2},{3})",
            source.rows,source.cols,target.rows,target.cols);
        // my NCC test
        printf("source image size w,h = (%d,%d) \n", source.cols, source.rows);
        printf("target image size w,h = (%d,%d) \n", target.cols, target.rows);

        //warm up
        //mycv::NormalizedCrossCorrelation(source, target, result);
        mycv::NormalizedCrossCorrelationFFT(source, target, result);

        start_time = std::chrono::steady_clock::now();;
        for (int n = 0; n < TIMES; n++)
        {
            //mycv::NormalizedCrossCorrelation(source, target, result);
            mycv::NormalizedCrossCorrelationFFT(source, target, result);
        }
        end_time = std::chrono::steady_clock::now();;
        myncc_runtime = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count() / TIMES;
        printf("my NCC run %d times,average use %f ms \n", TIMES, myncc_runtime);

        cv::minMaxLoc(result, &min_value, &max_value, &min_loc, &max_loc);
        printf("min_value=%f , min_loc(x,y)=(%d,%d), \t max_value=%f,max_loc(x,y)=(%d,%d)\n",
            min_value, min_loc.x, min_loc.y, max_value, max_loc.x, max_loc.y);

        logger->info("my NCC run {0} times,average use {1} ms \n", TIMES, myncc_runtime);
        logger->info("my NCC min_value = {0}, min_loc(x, y) = ({1}, {2}), \t max_value = {3}, max_loc(x, y) = ({4}, {5})\n",
            min_value, min_loc.x, min_loc.y, max_value, max_loc.x, max_loc.y);

        //warm up
        cv::matchTemplate(source, target, result, cv::TM_CCOEFF_NORMED);

        // opencv NCC test
        start_time = std::chrono::steady_clock::now();;
        for (int n = 0; n < TIMES; n++)
        {
            cv::matchTemplate(source, target, result, cv::TM_CCOEFF_NORMED);
        }
        end_time = std::chrono::steady_clock::now();;
        opencv_runtime = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count() / TIMES;
        printf("opencv NCC run %d times,average use %f ms \n", TIMES, opencv_runtime);
        cv::minMaxLoc(result, &min_value, &max_value, &min_loc, &max_loc);
        printf("min_value=%f , min_loc(x,y)=(%d,%d), \t max_value=%f,max_loc(x,y)=(%d,%d)\n",
            min_value, min_loc.x, min_loc.y, max_value, max_loc.x, max_loc.y);


        logger->info("opencv NCC run {0} times,average use {1} ms \n", TIMES, opencv_runtime);
        logger->info("opencv NCC min_value = {0}, min_loc(x, y) = ({1}, {2}), \t max_value = {3}, max_loc(x, y) = ({4}, {5})\n",
            min_value, min_loc.x, min_loc.y, max_value, max_loc.x, max_loc.y);
        logger->info("speed : myncc_runtime / opencv_runtime = {}", (int)(myncc_runtime / opencv_runtime));
    }
  

}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/146154.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

再学C语言27:输入和输出——缓冲区

I/O函数&#xff1a;输入/输出函数 I/O函数将信息传输至程序并从程序中传出信息&#xff0c;如printf()、scanf()、getchar()、putchar()等函数 getchar()和putchar()每次输入/输出一个字符 示例代码&#xff1a; #include <stdio.h> int main(void) {char c;// 输入回…

Vivado综合设置之-resource_sharing

-​resource_sharing用于对算数运算&#xff08;加法、减法和乘法&#xff09;实现资源共享&#xff0c;以节约LUT资源&#xff0c;有3个值&#xff1a;auto、off和on&#xff0c;默认是auto。 默认情况下&#xff0c;将resource_sharing设置为auto即可。 本文验证-resource_…

[Leetcode] 将二叉搜索树变平衡

将二叉搜索树变平衡&#xff1a;https://leetcode.cn/problems/balance-a-binary-search-tree/给你一棵二叉搜索树&#xff0c;请你返回一棵 平衡后的二叉搜索树&#xff0c;新生成的树应该与原来的树有着相同的节点值。如果有多种构造方法&#xff0c;请你返回任意一种。如果一…

Codeforces Round #842 (Div. 2)-C. Elemental Decompress

题目&#xff1a; 题目大意&#xff1a; 给定一个数列t&#xff0c;你构造两个数列a和b&#xff0c;使得max(a[i],b[i])t[i] 核心思想&#xff1a; 1、先根据给出的数组进行放置&#xff0c;优先放到a数组中&#xff0c;如果这个数已经在a数组中出现了&#xff0c;再去放到b数…

sentinel的使用

一、sentinel控制台的使用1、sentinel控制台jar包地址&#xff1a;Releases alibaba/Sentinel GitHub账号密码都为sentinel控制台访问地址&#xff1a;http://localhost:80802、sentinel的maven坐标<dependency><groupId>com.alibaba.cloud</groupId><a…

Android基础入门教程——2.2 LinearLayout(线性布局)

总结图片2. weight权重白色占总数量的2份&#xff0c;绿色占总数的1份。<?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http://schemas.android.co…

树莓派最新版系统烧写和网络配置

树莓派笔记1.树莓派烧写篇2.树莓派WIFI配置篇3.树莓派ping外网4.树莓派git篇5.参考1.树莓派烧写篇 目前烧写最新版本32位系统(2022-09-22发布的)&#xff0c;默认移除了pi用户&#xff0c;没有用户就无法SSH登录(无屏幕)&#xff0c;建议使用raspberry pi imager烧写工具&…

通过USB转TTL串口下载stm32程序

文章目录前言一、硬件及其接线二、使用步骤1.主芯片STM32F103C8T6开发板2.转串口模块接线3.CH340驱动及安装方法4.CH340驱动及安装方法4.下载测试5.0.91寸OLED 接口演示例程前言 前期我们下载程序都是使用STlink进行下载的&#xff0c;现在给大家提供一种新的程序下载方法&…

5G小基站国产化超五成,美国芯片仅占1%,难怪美国芯片难卖了

日前日媒拆解中国某科技企业的5G小基站&#xff0c;发现它的中国零部件占比达到55%&#xff0c;而来自美国的零部件占比仅为1%&#xff0c;显示出这家企业在去美化取得了重大进展&#xff0c;如此也就能理解为何如今美国芯片难卖了。日媒指出该科技企业的5G小基站国产化零部件占…

动态规划 完全背包问题

目录 LintCode 炼码完全背包问题 【解法一】 【解法二】 完全背包问题 【解法一】 解释&#xff1a; 第一个for循环表示从第一个物品开始遍历 第二个for循环表示逆向 从背包容量为m时开始处理&#xff08;滚动数组&#xff09; 第三个for循环表示装入k个该物品&#xff0c;装…

【前端】Vue项目:旅游App-(9)city:固定tab栏、内容中显示数据

文章目录目标过程与代码Tab一直显示的两种方法方法1&#xff1a;fixed定位方法2&#xff1a;设置height和overflow-y&#xff08;效果不好&#xff09;content显示数据效果总代码city.vue相关参考目标 上一篇获取了服务器中的数据&#xff1a;【前端】Vue项目&#xff1a;旅游…

【数据结构】完全二叉树——啊堆堆堆

一、树概念及结构树的概念树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n > 0&#xff09;个有限节点组成的一个具有层次关系的集合。把它叫做树是因为他看起来像是一颗倒挂起来的树&#xff0c;也就是说它是根朝上&#xff0c;而叶子朝下的。-> 有一个特殊的…

Find My产品|Ember发布支持苹果Find My温控马克杯

在 CES2023 展会上&#xff0c;温控马克杯制造商 Ember 表示&#xff0c;计划在 2023 年 Q2 上市一款支持苹果 Find My 应用的全新 Travel Mug 2 马克杯。 这项新功能将使用户能够在 Find My 应用中追踪 Travel Mug 2 的位置&#xff0c;就像追踪iPhone、iPad、AirPods 或其他…

埃科光电在科创板IPO过会:拟募资11亿元,董宁为实际控制人

近日&#xff0c;上海证券交易所披露的信息显示&#xff0c;合肥埃科光电科技股份有限公司&#xff08;下称“埃科光电”获得科创板上市委会议通过。据贝多财经了解&#xff0c;埃科光电于2022年6月22日在科创板提交招股书&#xff0c;并于12月29日递交招股书&#xff08;上会稿…

图像分割笔记

图像分割笔记 目标&#xff1a; 实现图像中多个物体的分割&#xff0c;多个物体的标注方式为0,1,2,3,0表示背景&#xff0c;1表示一种物体&#xff0c;2表示另一种物体&#xff0c;假设我们现在的分割任务里面有5个目标需要&#xff0c;如肺叶分割&#xff0c;5个肺叶的标注方…

飞书开放平台-全新消息卡片搭建工具

前言你还在为需要手撕 JSON 代码而烦恼吗&#xff1f;消息卡片搭建工具全新升级&#xff0c;为你带来更优雅的卡片创作体验&#xff1a;&#x1f31f; 卡片编辑&#xff1a;纯可视化操作编辑消息卡片&#xff0c;再也不用碰代码&#x1f31f; 我的卡片&#xff1a;一键保存卡片…

Vue3 Composition API

文章目录p15 Vue3 Composition APIMixin全局混入options API的弊端认识Componsition APIsetup函数的参数setup不可以使用thisReactive APIrefreadonlyp16 Composition API(二)toRefscomputedwatchEffect在setup中使用refwatchp15 Vue3 Composition API Mixin 全局混入 、 opti…

使用正则表达式删除注释

以下摘自某网友来信&#xff1a; 难点 Javascript 不支持点号匹配换行符, 因此无法直接进行多行匹配处理前面没有 http: 的 //, 当然要用否定前瞻( negative lookbehine)了:(?<!http:)\/\/. 可惜 javascript 不支持 思路 关于多行匹配 这个问题, 之前我已经说过, 要点…

多线程初阶(四)定时器及线程池

目录 前言&#xff1a; 定时器 使用标准库中定时器 模拟实现定时器 线程池 使用标准库中的线程池 代码实现 ThreadPoolExecutor类介绍 构造方法参数介绍 拒绝策略介绍 模拟实现线程池 代码实现 提出问题 小结&#xff1a; 前言&#xff1a; 这篇文章同上一篇文章…

简单实现Java定时器

✨✨hello&#xff0c;愿意点进来的小伙伴们&#xff0c;你们好呐&#xff01; &#x1f43b;&#x1f43b;系列专栏&#xff1a;【JavaEE】 &#x1f432;&#x1f432;本篇内容&#xff1a;自己实现Java定时器 &#x1f42f;&#x1f42f;作者简介:一名现大二的三非编程小白&…