002.叶欣机器视觉学习笔记_第二课_图像的预处理部分
图像预处理
1.图像显示与存储原理
a.RGB颜色空间
b.CMY(K)颜色空间
c.HSV颜色空间(人眼)
-
第二课 00:06:38
-
人眼视觉概念
- H/Hue:色调,颜色种类
- S/Saturation:饱和度,颜色的纯度
- V/Value:明度,颜色的明亮度
-
一个像素用(h,s,v)表示,并且可以转化为RGB
d.CIE-XYZ最精确的颜色空间
- 第二课 00:07:28
f.RGB三通道转灰度图单通道公式
- Gray = R*0.3 + G*0.59 + B*0.11
2.图像增强的目标
a.目的:实现锐化,平滑,去噪,对比度增强
b.图像处理
i:空间域处理
一. 点运算(HE,AHE,CLAHE)
-
基于直方图(Histogram)统计,直方图越宽胖,肉眼可见暗部细节就越多
-
x轴:像素区间, y轴:落在该区间的像素的个数 (第二课 00:16:13)
-
直方图均衡化(HE)可以调整图像对比度
-
就是把"瘦高的直方图"拉成"胖矮直方图"(第二课 00:19:38)
-
比如像素和像素靠的太近,肉眼无法分辨出来,就需要一个函数把变成,把变成,这样肉眼就可以分辨出来了,当然,要求原本比暗的像素,在变换后依然比暗(原本比亮的像素,在变换后依然比亮),才不至于黑白颠倒
-
直方图均衡化灰度转换函数推导博客https://blog.csdn.net/superjunenaruto/article/details/80037777
-
使用累积分布函数 作为映射函数,使得图像映射后更分散
-
累积分布函数性质
-
单调递增(可以保证比亮的像素,映射后还是比亮
-
值域为到,(控制像素变换后依然在范围内)
-
累积分布函数公式为:
注意:每一个像素都对应一个不同的
-
-
某个像素的计算公式:
波峰和前后两个像素 像素出现的概率 累积概率 映射后 像素 像素 像素
-
-
优缺点:
-
优点:
- 无参数的优化对比度算法,免去了针对不同图像进行调整的过程
- 处理后像素值几乎均匀分布
-
缺点:
- 缺点1:如果图像中有明显亮(或明显暗)的区域,则作用有限,变得更亮(或更暗)的地方可能会丢失细节
- 缺点2:噪点会被放大
-
-
问题:
- 离散型由于取整,导致出现多对一的映射关系。比如把像素128和像素130映射后得到
T(120)=131.25
,T(125)=131.94
,由于我们取整,得到两个都是131
,这就使得图像不平坦。 - 直方图均衡只能使用一次,多了也没什么效果
- 直方图均衡可以通过直方图匹配(直方图规定化)来实现,matlab里就是这样的。
- 离散型由于取整,导致出现多对一的映射关系。比如把像素128和像素130映射后得到
-
-
-
自适应直方图均衡(AHE Adaptive)
-
就是把图片横着切几刀,竖着切几刀,再对每个子块做HE,最后拼起来
-
缺点:图片会产生明显的分割线
-
-
限制对比度自适应直方图均衡化(CLAHE)
-
直方图等面积变换:可以让对比度更自然
-
什么是双线性插值
-
百度百科:如图,已知4个红点Q,如何求绿点P的坐标?
先在两条横线中插入,再对竖线中插入
-
-
缺点:
- 缺点1:本来比较均匀的区域,会把噪声放大了
-
应用:图像去雾
-
第二课 00:21:32截图
-
-
opencv中使用clahe
void opencvEqualizeHist_CLAHE() { using namespace cv; Mat img, img_gray, result; Mat clahe_result; img = imread("/media/majiao/Elements/majiao/尼康1j1/2022_09_10中秋学校拍的/DSC_0308.JPG"); int wid = img.cols*0.2, hei = img.rows*0.2; resize(img, img, cv::Size(wid, hei)); cvtColor(img, img_gray, cv::COLOR_BGR2GRAY); equalizeHist(img_gray, result); cv::Ptr<CLAHE> clahe = cv::createCLAHE(); clahe->setClipLimit(4); clahe->setTilesGridSize(cv::Size(10, 10)); clahe->apply(img_gray, clahe_result); imshow("src", img_gray); imshow("result", result); imshow("clahe_result", clahe_result); waitKey(); }
二.形态学运算(膨胀,腐蚀)第二课 29:57:00
-
膨胀(dilate达累特)
-
目的:消除噪声,
连接图像中相邻的元素
-
膨胀就是局部求最大值,公式为
原图中间不连续(T的连接处断开了) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 255, 255, 255, 255, 255, 255, 255, 255, 255, 255; 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 255, 0, 0, 0, 0; 0, 0, 0, 0, 0, 255, 0, 0, 0, 0; 0, 0, 0, 0, 0, 255, 0, 0, 0, 0; 0, 0, 0, 0, 0, 255, 0, 0, 0, 0; 0, 0, 0, 0, 0, 255, 0, 0, 0, 0] 经过cv::dilate膨胀后,就相连了 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 0, 0, 0, 0, 0, 0, 0, 0, 0, 0; 255, 255, 255, 255, 255, 255, 255, 255, 255, 255; 255, 255, 255, 255, 255, 255, 255, 255, 255, 255; 255, 255, 255, 255, 255, 255, 255, 255, 255, 255; 0, 0, 0, 0, 255, 255, 255, 0, 0, 0; 0, 0, 0, 0, 255, 255, 255, 0, 0, 0; 0, 0, 0, 0, 255, 255, 255, 0, 0, 0; 0, 0, 0, 0, 255, 255, 255, 0, 0, 0; 0, 0, 0, 0, 255, 255, 255, 0, 0, 0]
-
-
腐蚀(erode衣肉的)
割出图像中独立的元素
- 腐蚀:就是局部求最小值,公式
-
在opencv中使用腐蚀膨胀函数
void opencvErodeDilateTest() { using namespace cv; Mat img, img_gray, result; Mat clahe_result; img = imread("/home/majiao/图片/p2235240978.jpg"); int wid = img.cols*0.2, hei = img.rows*0.2; resize(img, img, cv::Size(wid, hei)); cvtColor(img, img_gray, cv::COLOR_BGR2GRAY); cv::Mat img_erode, kenel = Mat::ones(3, 3, img_gray.type()); /** * kenel为3x3的全1矩阵 * 1 1 1 * 1 1 1 * 1 1 1 */ cv::erode(img_gray, img_erode, kenel); // 腐蚀 cv::Mat img_dilate; cv::dilate(img_gray, img_dilate, kenel); // 膨胀 cv::Mat kenel2 = getStructuringElement( /* 生成核 */ cv::MORPH_RECT, /* 可选 cv::MORPH_CROSS 十字型 cv::MORPH_OPEN 开运算 cv::MORPH_CLOSE 闭运算 cv::MORPH_GRADIENT 形态学梯度 cv::MORPH_TOPHAT 顶帽 cv::MORPH_BLACKHAT 黑帽 cv::MORPH_ERODE 腐蚀 cv::MORPH_DELATE 膨胀 */ cv::Size(3, 3)); cv::Mat img_dilate_morph_rect; cv::dilate(img_gray, img_dilate_morph_rect, kenel2); imshow("img_gray", img_gray); imshow("erode_3x3", img_erode); imshow("dilate_3x3", img_dilate); imshow("img_dilate_morph_rect", img_dilate_morph_rect); std::cout << kenel << std::endl; std::cout << kenel2 << std::endl; waitKey(); }
开运算:先腐蚀再膨胀,可以去掉目标外的孤立点
闭运算:先膨胀再腐蚀,可以去掉目标的孔洞
三.临域运算(卷积,金字塔)
1.卷积(也叫滤波)
a. 卷积定义:对应位置相乘再相加
- 冈萨雷斯书上的卷积公式:
中科院王伟强老师说这个不应该叫卷积,应该叫相关 - 需要理解什么是线性移不变系统
1. 线性,只有加法和乘法
b. Padding(边界填充):
- 卷积完,新图可能会小一点,为了使得新图和原图一样大,所以一般用0填充边界后再卷积,卷积核越大则填充越多
- 尽量不要用大卷积核去卷小图,因为用0来padding会影响新图的结果
d. 卷积分解特性(级联高斯)
-
结论:如果一个卷积核可以表达成
水平核H
和竖值核V
相乘,则这个卷积核是可分离的 -
对称
2D卷积
拆分成2个相同的1D
卷积(行与列),注意要对称-
拆分后计算量下降:2D卷积K*K次计算,1D卷积只要2K次计算
-
第二课00:48:00截图
-
TEST_CASE("test_opencv_卷积操作_002_filter2D卷积核2D分解为1D降低计算量", "[test_opencv_convolution]") { Mat img, img_gray, result; Mat clahe_result; img = imread(test_opencv_convolution_kenels::imagePath); int wid = img.cols*0.2, hei = img.rows*0.2; resize(img, img, cv::Size(wid, hei)); cvtColor(img, img_gray, cv::COLOR_BGR2GRAY); cv::Mat kernel2D = (Mat_<uchar>(3,3) << 1, 2, 1, // 2D核心:可以写成行列相乘的形式 2, 4, 2, 1, 2, 1); cv::Mat kernel2DRow = (Mat_<uchar>(1,3) << 1, 2, 1); // 1D核心行 cv::Mat kernel2DCol = (Mat_<uchar>(3,1) << 1, 2, 1); // 1D核心列 cv::Mat src = (Mat_<uchar>(5,5) << 2,3,4,5,6, 3,4,5,6,7, 3,4,5,6,7, 3,4,5,6,7, 3,4,5,6,7); cv::Mat dst, dst2; cv::filter2D(src, dst, 0, kernel2D); /*直接卷2D核心*/ { cv::filter2D(src, dst2, 0, kernel2DRow); /*先卷行*/ cv::filter2D(dst2, dst2, 0, kernel2DCol); /*再卷列*/} }
-
f. 卷积的性质:
- 交换律:
- 结合律:
- 分配率:
- 数乘:
g.卷积convolution的由来
h. opencv里的卷积函数cv::filter2D(src, dst, ddepth, kernel)
i. 低通滤波器与高通滤波区别
- 人眼对高频信息更敏感,(白纸上有一行字,我们肯定直接聚焦在这行字上,而不是白纸上)
- 图像上,变化剧烈的地方是高频信号,变化平缓的地方是低频信号
= 常用卷积核
-
001.平滑均值滤波: 参数和为1,用来去除噪声,但效果不好(破坏了细节,无法去除椒盐噪声)没什么人用
优点:平滑图像,降低锐度,模糊图像并去除噪声
缺点:对椒盐噪声基本无能为力,
TEST_CASE("test_opencv_卷积核测试_001_均值滤波器", "[test_opencv_convolution_kenels]") { Mat img, img_gray, result; Mat clahe_result; img = imread(test_opencv_convolution_kenels::imagePath); int wid = img.cols*0.2, hei = img.rows*0.2; resize(img, img, cv::Size(wid, hei)); cvtColor(img, img_gray, cv::COLOR_BGR2GRAY); cv::Mat img_bulr; cv::blur(img_gray, img_bulr, cv::Size(3,3)); /** bulr 平滑均值滤波 作用 : 1.平滑图像,降低锐度 2.模糊图像并去除噪声 缺点 : 去噪效果差,破坏了细节, 椒盐噪声几乎没法去除 1 1 1 1/9 x 1 1 1 1 1 1 */ imshow("灰度图src", img_gray); imshow("经过bulr均值滤波后", img_bulr); waitKey(); }
- 问题:
- 均值滤波后,如何消除零填充带来暗色边框伪影?冈萨雷斯第四版109页,pdf第124页.
- 为什么说图像均值处理与积分类似?冈萨雷斯第四版117页,pdf第132页
答:https://blog.csdn.net/LaoYuanPython/article/details/123167634
- 问题:
-
002.平滑中值滤波:卷积域内的像素值排序后取中间值输出
- 优点:
- 有效去除椒盐噪声(椒盐就是图像上的白点或黑点)
- 缺点:
- 可能会删除图像中非线性的部分,冈萨雷斯第四版131页,pdf第146页,医学影像最好别用。
TEST_CASE("test_opencv_卷积核测试_002_中值滤波MedianFilter", "[test_opencv_convolution_kenels]") { Mat img, img_gray, result; Mat clahe_result; img = imread(test_opencv_convolution_kenels::imagePath); int wid = img.cols*0.2, hei = img.rows*0.2; resize(img, img, cv::Size(wid, hei)); cvtColor(img, img_gray, cv::COLOR_BGR2GRAY); for (int i=0; i+10<img_gray.cols; i+=10) { // 撒上椒盐噪声 for (int j=0; j+10<img_gray.rows; j+=10) { if (i < img_gray.rows && j < img_gray.cols) img_gray.at<uchar>(i, j) = 0; } } cv::Mat img_median; /* 中值滤波 : 卷积域的像素值排序后取中间值输出为新像素 优点 : 有效去除椒盐噪声 */ cv::medianBlur(img_gray, img_median, 3/*模糊核的线性大小3,5,7...*/); imshow("灰度图src", img_gray); imshow("经过medianBlur中值滤波后", img_median); waitKey(); }
- 优点:
-
003.高斯滤波(重要):模拟人眼关注中心区域,应用于
高斯金字塔
-
第二课:44:28
-
高斯分布公式:,入参标准差,标准差增大,图像变矮
如图:卷积后会突出中心点的像素,中心点附近的像素也会或多或少算进去
-
且高斯核具有可分离性,2D可以分解为2个1D的核
TEST_CASE("test_opencv_卷积核测试_003_高斯滤波GuassianFilter", "[test_opencv_convolution_kenels]") { Mat img, img_gray, result; Mat clahe_result; img = imread(test_opencv_convolution_kenels::imagePath); int wid = img.cols*0.2, hei = img.rows*0.2; resize(img, img, cv::Size(wid, hei)); cvtColor(img, img_gray, cv::COLOR_BGR2GRAY); /** 高斯滤波:模拟人眼,突出中间点,图像是正态分布的3维版本 */ cv::Mat img_gaussian_sigma_0_1, img_gaussian_sigma_1, img_gaussian_sigma_3, img_gaussian_sigma_7; cv::GaussianBlur(img_gray, img_gaussian_sigma_0_1, cv::Size(7,7), 0.1); cv::GaussianBlur(img_gray, img_gaussian_sigma_1, cv::Size(7,7), 1); cv::GaussianBlur(img_gray, img_gaussian_sigma_3, cv::Size(7,7), 3); cv::GaussianBlur(img_gray, img_gaussian_sigma_7, cv::Size(7,7), 7/*sigmaX越大越模糊*/); }
-
应用
- 高斯滤波消除图像中的阴影(阴影校正): 在冈萨雷斯第四版115页,pdf第130页
- 高斯滤波消除图像中的阴影(阴影校正): 在冈萨雷斯第四版115页,pdf第130页
-
-
004.prewitt水平或垂直边缘滤波器(常用改进后的sobel算子)
-
水平边缘 竖值边缘滤波 1 1 1 -1 0 1 -1 -1 0 0 1 1 0 0 0 -1 0 1 -1 0 1 -1 0 1 -1 -1 -1 -1 0 1 0 1 1 -1 -1 0 可分解为行(1,1,1)和列(1,0,-1)
-
TEST_CASE("test_opencv_卷积核测试_004_Prewitt水平或垂直滤波器01", "[test_opencv_convolution_kenels]") { Mat img, img_gray, result; Mat clahe_result; img = imread(test_opencv_convolution_kenels::imagePath); int wid = img.cols*0.2, hei = img.rows*0.2; resize(img, img, cv::Size(wid, hei)); cvtColor(img, img_gray, cv::COLOR_BGR2GRAY); cv::Mat kernelV = (Mat_<float>(3,3) << -1, 0, 1, -1, 0, 1, -1, 0, 1 ); // -1 0 1 [水平梯度/垂直边缘] // ------------- 可分解成水平(-1,0,1) // 1 | -1, 0, 1 和竖直(1,1,1) // 1 | -1, 0, 1 // 1 | -1, 0, 1 cv::Mat kernelH = (Mat_<float>(3,3) << -1, -1, -1, 0, 0, 0, 1, 1, 1); // 1 1 1 [垂直梯度/水平边缘] // ------------- 可分解成水平(1,1,1) // -1 | -1, -1, -1 和竖直(-1,0,1) // 0 | 0, 0, 0 // 1 | 1, 1, 1 cv::Mat after_prewittV, after_prewittH; cv::filter2D(img_gray, after_prewittV, -1, kernelV); cv::filter2D(img_gray, after_prewittH, -1, kernelH); Mat prewitt_HV = after_prewittH + after_prewittV; imshow("灰度图src", img_gray); imshow("prewitt垂直边缘算子后", after_prewittV); imshow("prewitt水平边缘算子后", after_prewittH); imshow("prewitt横竖相加", prewitt_HV); waitKey(); }
-
-
-
005.Sobel边缘过滤算子(分为x方向和y方向)
-
与
梯度
密不可分,本质上是梯度运算,可以和杨辉三角对应上 -
对图像中弱边缘提取效果差(可用Scharr算子替代)
-
产生梯度的情况
-
中心点p5的梯度如何求
-
sobel的本质是一阶导数https://blog.csdn.net/lz0499/article/details/118003220
-
Roberts算子只考虑了差分,prewitt也是一阶差分且引入了均值,
sobel兼顾了差分和类似高斯的平滑处理
,[1,0,-1]及其转置分别代表水平差分和垂直差分,[1,2,1]则类似高通平滑过滤噪声
-
Sobel的构造方法
- opencv里的
getSobelKernels
函数会返回x方向和y方向的1x3Sobel算子 (3x3分解后的) - 行列的Sobel类似高斯平滑的部分可以用
杨辉三角的第N行构造
,然后差分出下一行或下一列(减1或减2)
- opencv里的
-
TEST_CASE("test_opencv_卷积核测试_005_Sobel边缘过滤算子_01水平和竖直", "[test_opencv_convolution_kenels]") { Mat img, img_gray, resultX, resultY, cvResultX, cvResultY; img = imread(test_opencv_convolution_kenels::imagePath); int wid = img.cols*0.2, hei = img.rows*0.2; resize(img, img, cv::Size(wid, hei)); cvtColor(img, img_gray, cv::COLOR_BGR2GRAY); cv::Mat kernelX = (Mat_<float>(3,3) << -1, -2, 1, 0, 0, 0, 1, 2, 1 ); cv::Mat kernelY = (Mat_<float>(3,3) << -1, 0, 1, -2, 0, 2, -1, 0, 1 ); cv::filter2D(img_gray, resultX, -1, kernelX); cv::filter2D(img_gray, resultY, -1, kernelY); cv::Sobel(img_gray, cvResultX, -1, 2, 0, 3); // 突出了竖线 cv::Sobel(img_gray, cvResultY, -1, 0, 2, 3); // 突出了横线 cv::getDerivKernels(kernelX, kernalY, 1, 0, FILTER_SCHARR=-1); // ksize=-1时生成scharr核,否则都生成sobel核(可分离的行列核心) waitKey(); }
-
应用
- 缺陷检测时,用来消除几乎没什么变化的背景,并且增强待检测物体的边缘。冈萨雷斯第四版125页,pdf第140页有一个检测眼镜片边缘磨损的例子。
- 用来突出肉眼很难看到的细小物体,如保护液中的气泡,划痕
-
问题:
-
目标像素运算结果不在0到255区间内怎么办?
- opencv里是直接截断,负数取0,大于255取255
- 实际负数取绝对值,大于255取255,会比较合理
-
总的梯度如何求?
-
开根号[ (x方向梯度平方) + (y方向梯度平方) ]
或者用简化的梯度
-
-
冈萨雷斯第四版书中124页,pdf第139页中说:Sobel算子强调中心重要程度实现某种平滑,这里的
某种平滑
指的是什么? -
医学影像常用的图像处理策略有哪些?
- 如果噪声多,不建议用中值滤波,建议先使用拉普拉斯争强细节,再用sobel突出边缘。冈萨雷斯第四版129页,pdf第144页
-
-
005.Scharr算子和sobel差不多(梯度检测中常用)
-
3 10 3 其他方向都和sobel差不多 0 0 0 只不过改成了3 10 3 -3 -10 -3 cv::getDerivKernels(kernelX, kernalY, 1, 0, FILTER_SCHARR=-1); // ksize=-1时生成scharr核,否则都生成sobel核(可分离的行列核心)
-
-
006.Laplacian算子过滤边缘(具有旋转不变性的算子之一)
- 在冈萨雷斯第四版119页,pdf第134页
拉普拉斯算子对边缘敏感
-
注意看算子加和等于零
-
拉普拉斯算子是二阶导:
1. f(x) = f(x-1)+f(x+1) - 2f(x) 左右之和减去2倍的中间像素
2. f(y) = f(y-1)+f(y+1) - 2f(y) 上下之和减去2倍的中间像素
3. 合并起来就是f(x,y) = f(x-1,y)+f(x+1,y)+f(x,y-1)+f(x,y+1) - 4*f(x,y)
-
-
增强对比度的方法:新图S = (原图A) 减 (laplacian卷一遍后的图B)
-
opencv中的cv::Laplacian竟然是用Sobel实现的
- 如果卷积核用1x1或3x3就用上面的卷积,但是卷积核大于等于5x5就会使用sobel
- 内部用了CV_OCL_RUN(ocl_Laplacian5(,,,)),
优先使用openCL硬件和GPU运算
-
计算图像模糊度,具体操作为:用图片的1个通道用以下3x3的核进行卷积,然后计算输出的方差,如果方差cv::meanStdDev小于一定值则图片视为模糊
-
细节参考"./维基百科上对laplace的讲解Discrete_Laplace_operator_Wikipedia.pdf"
-
问题
- 如何用opencv创建一个log算子,(类似于matlab的fspecial函数)?
- 在冈萨雷斯第四版119页,pdf第134页
ii:频率域处理
一.傅里叶变换
二.小波变换
iii:空间域要映射到频率域
- 好处:可以快速计算卷积
- 空间域上的卷积等于频率域上面的乘
3.点运算:基于直方图的对比度增强
4.形态学处理
5.空间域处理:卷积
6.卷积的应用:平滑,边缘检测,锐化等
锐化
锐化:补偿图像的轮廓,增强图像的边缘及灰度跳变的部分,使图像变得清晰的方法,也叫边缘增强
- 一个像素
r
,如果左边的像素减,右边的像素加,那对比度是不是就上来了!
1. 用钝化(平滑后的)图像来锐化
- 冈萨雷斯第四版122页,pdf138页,书上说20世纪30年代就有这种做法了(效果较差,现在用的少)
- 做法分3步:
1. 先模糊原图,得到模糊图imgBlur
2. 再用原图减去模糊图,得到模板图,即:模板图
=src
-imgBlur
3. 最后再把原图与模板图相加得到锐化图,即:img锐化
=src
+模板图
2. 用拉普拉斯滤波器进行锐化,
- 拉普拉斯算子是x和y两个方向上都是二阶导
- 就是
锐化好的新图
=原图
-拉普拉斯卷积后的图
,(原图减去拉普拉斯卷积后的图像)| 0 1 0 | src - (src 卷积 | 1 -4 1 | ) | 0 1 0 | 锐化可以优化成下面这样 (直接拿原图卷这个核,省去了一个减法过程) | 0 1 0 | src 卷积 | 1 -5 1 | | 0 1 0 |
7.频率域处理:傅里叶变换,小波变换
8.应用案例
a.平滑,边缘检测,CLAHE等
补充
1. 卷积convolution的由来
a. 卷积公式:
- 连续型:
- 离散型:
b. 卷积由来
-
问题1:考虑两个骰子A和B,摇一次得到2个值A1和B1,定义sum1=A1+B1,问sum1=X的概率是多少?
例如:sum1=A1+B1=5的概率是多少?
- 解法:
-
问题2:知乎进食问题 , 人吃饭,函数表示一口一口吃,每口质量能不一样(多一点或少一点),
表示这一口吃下去会被消化,经过时间后还剩克, 现在问:你不断不断的吃,到了t时刻,肚子里有多少食物待消化?
-
当t=9时,
-
问题3:pid控制算法, (比例-积分-微分控制器)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构