2022年9月28日 星期三

在 Linux 上建立 RAM Disk

 在 Linux 上已經有內建一些方法,可以很方便地將記憶體分配來當作檔案目錄來使用。

要在 Linux 上建立 RAM Disk 內建有三種方式:ramdiskramfstmpfs,那我們一一來介紹,我的硬體環境是 GCP 上 N1-Standard-1 的 VM。

ramdisk

ramdisk 在一般常見的 Linux 發行版本大都是沒有的,必須要自己建置 Linux 的 Kernel 把 ramdisk 加進來才有得用,它預設會在 /dev 底下掛載 16 個 RAM Disk,使用者可以自行挑選要使用哪一個 RAM Disk,而且使用前還得先進行格式化,預設的 RAM Disk 大小都不大,想要調整的話,可以透過修改 GRUB 來設定。

由於 ramdisk 不是那麼方便使用,所以我們不在它上面著墨,把重點放在後面的 ramfs 及 tmpfs。

ramfs

ramfs 與 ramdisk 不同的是,ramfs 是虛擬的檔案系統,掛載起來就可以直接使用,不像 ramdisk 還要先格式化,建立的方式也很簡單,首先建立一個目錄,在哪都無所謂,一般都是建在 /mnt 底下。

sudo mkdir /mnt/ramfs

接著將 ramfs 掛載到剛剛建的目錄

sudo mount -t ramfs -o mode=0777 ramfs /mnt/ramfs
  • -t ramfs:指定 vfstype 為 ramfs
  • -o mode=0777:設定權限為 0777
  • ramfs:指定 device 為 ramfs

掛載好了之後,就可以開始使用了,我用 sysbench 對 ramfs 進行隨機讀寫的測試,效能提升了 10 倍左右。

有兩件很重要的事要注意:

  1. ramfs 的大小是無法限制的,記憶體吃到滿為止。
  2. ramfs 在 df 指令中是看不到的。

tmpfs

由於 ramfs 存在著一些缺點,所以要在 Linux 上建立 RAM Disk 大都推薦使用 tmpfs,tmpfs 跟 ramfs 一樣是虛擬的檔案系統,但是它可以限制大小,而且它會使用到 SWAP 的空間,就不用太擔心 tmpfs 佔用的空間太大而吃滿記憶體,還有用 df 指令是可以看得到 tmpfs 的。

用 tmpfs 建立 RAM Disk 的過程跟 ramfs 是一樣的,一樣先建立目錄。

sudo mkdir /mnt/tmpfs

再來就是將 tmpfs 掛載到剛剛建立的目錄

sudo mount -t tmpfs -o size=300M,mode=0777 tmpfs /mnt/tmpfs

一樣用 sysbench 對 tmpfs 做隨機讀寫的測試,可以看到效能是差不多的。

用 df 指令也能夠看得到 tmpfs 的資訊

而且當空間不足以寫入檔案時,會出現 No space left on device 訊息。

開機時自動掛載 RAM Disk

掛載的 RAM Disk 預設都會在重新開機時消失,所以我們要修改 /etc/fstab 檔案,在開機時將 RAM Disk 掛載起來。

tmpfs /mnt/tmpfs tmpfs size=300M,mode=0777 0 0

這樣我們就保證都存取得到 RAM Disk,以上就提供給有需要在 Linux 建立 RAM Disk 的朋友參考。

參考資料

資料來源: https://dotblogs.com.tw/supershowwei/2020/09/28/091619

2022年9月19日 星期一

C# Excel to Database

使用 MiniExcel,讀取Excel資料到IEnumerable或是DataTable,再轉成SQL轉到資料庫

Github : https://github.com/shps951023/MiniExcel

例子:

1. SQLite & Dapper 讀取大數據新增到資料庫

Note : 請不要呼叫 call ToList/ToArray 等方法,這會將所有資料讀到記憶體內

using (var connection = new SQLiteConnection(connectionString))
{
    connection.Open();
    using (var transaction = connection.BeginTransaction())
    {
	   var rows = MiniExcel.Query(path);
	   foreach (var row in rows)
			 connection.Execute("insert into T (A,B) values (@A,@B)", new { row.A, row.B }, transaction: transaction);
	   transaction.Commit();
    }
}

讀取excel插入資料庫1千萬筆數據花費30秒,最大26MB記憶體

image 

資料來源: https://ithelp.ithome.com.tw/questions/10204298

2022年9月7日 星期三

Emgucv 常用函數

 Emgucv常用函数总结:

读取图片
Mat SCr = new Mat(Form1.Path, Emgu.CV.CvEnum.LoadImageType.AnyColor);
//根据路径创建指定的灰度图片
Mat scr = new Mat(Form1.Path, Emgu.CV.CvEnum.LoadImageType.Grayscale);
获取灰度    //图像类型转换, bgr 转成 gray 类型。MAT Bw = New MAT
CvInvoke.CvtColor(SCr, bw, Emgu.CV.CvEnum.ColorConversion.Bgr2Gray);
//相当于二值化图 --黑白 根据大小10判断为0还是255
CvInvoke.Threshold(bw,bw,10,255,Emgu.CV.CvEnum.ThresholdType.BinaryInv);
//获取指定区域图片 SCr为mat类型
Rectangle rectangle = new Rectangle(10,10,10,10);
SCr = SCr.ToImage().GetSubRect(rectangle).Mat;
//将Mat类型转换为Image类型
Image Su = SCr.ToImage();
Image Img = new Image(new Bitmap(""));//路径声明
Image Sub = SCr.ToImage().GetSubRect(rectangle);//指定范围
//指定参数获得结构元素
Mat Struct_element = CvInvoke.GetStructuringElement(Emgu.CV.CvEnum.ElementShape.Cross, new Size(3, 3), new Point(-1, -1));
//膨胀
CvInvoke.Dilate(bw, bw, Struct_element, new Point(1,1),3,Emgu.CV.CvEnum.BorderType.Default, new MCvScalar(0, 0, 0));
//腐蚀 当Struct_element模型创建不合理或者膨胀腐蚀次数较大时可能图像会发生偏移
CvInvoke.Erode(bw, bw, Struct_element, new Point(-1, -1), 3,Emgu.CV.CvEnum.BorderType.Default, new MCvScalar(0, 0, 0));
//轮廓提取
VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
//筛选后
CvInvoke.FindContours(bw, contours, null, Emgu.CV.CvEnum.RetrType.List, Emgu.CV.CvEnum.ChainApproxMethod.ChainApproxSimple);
int ksize = contours.Size;//获取连通区域的个数。     
VectorOfPoint contour = contours[i];//获取独立的连通轮廓   
Rectangle rect = CvInvoke.BoundingRectangle(contour);//提取最外部矩形。
double Length = CvInvoke.ArcLength(contour, false);//计算连通轮廓的周长。
//画出轮廓
Mat mask = bw.ToImage().CopyBlank().Mat;
//获取一张背景为黑色的图像, 大小与 scr 的大小一样, 类型为 Bgr。
CvInvoke.DrawContours(mask, contours, -1, new MCvScalar(0, 0, 255));
Image ycc_img = bgr_img.Convert();//把 bgr颜色图片转成ycbcr类型。
Ycc min = new Ycc(152, 38, 118);//最小值的颜色。
Ycc max = new Ycc(94, 43, 118);//最大值得颜色。
Image result = ycc_img.InRange(min, max);//进行颜色提取。
Image bgr_img = Ma.ToImage();//载入一张 Bgr 类型的图片。
Bgr min = new Bgr(255, 255, 255);//白色的最小值, 允许一定154的误差。
Bgr max = new Bgr(255, 255, 255);//白色的最大值, 允许一定的误差。
Image result = bgr_img.InRange(min, max);//进行颜色提取。
Image imageSource = new Image(SCr.Bitmap);
Image imageHsv = imageSource.Convert();//将色彩空间从BGR转换到HSV
Image[] imagesHsv = imageHsv.Split();//分解成H、S、V三部分
CvInvoke.AbsDiff(Ma1, Ma2, Ma); // 返回两幅图片或此图与某个yanse像素的差的绝对值的图片
CvInvoke.Add(Ma1, Ma2, Ma); // 返回这张图片与图片或颜色直接相加的图片(矩阵加法)  (适应两种效果)
//CvInvoke.HConcat(Ma1, Ma2, Ma); //返回与另一张图片横向链接的图片
//CvInvoke.VConcat(Ma1, Ma2, Ma);//返回与另一张图片纵向链接的图片
//清除小于平均顶点10的二值图
Point[] po = { new Point(0, 0), new Point(res.Width, 0), new Point(res.Width, minAvg - Gets.Fges[1] + 52), new Point(0, minAvg - Gets.Fges[1] + 52) };
VectorOfPoint vp = new VectorOfPoint(po);
//CvInvoke.DrawContours(res, vp, -1, new MCvScalar(0, 0, 255));
CvInvoke.FillConvexPoly(res,vp,new MCvScalar(0),LineType.EightConnected);//填充指定区域
 
/// 
/// 灰度直方图计算  手动计算、/获取百分比的阀值  0.95
/// 
public static void GetDenseHistogram95(ref int huidu, Mat ma)
{
            DenseHistogram dense = new DenseHistogram(256, new RangeF(0, 255));
            dense.Calculate(new Image[] { ma.ToImage() }, true, null);
            //计算直方图数据。
            float[] data = dense.GetBinValues();
            float[] data2 = dense.GetBinValues();
            //获得直方图数据。
            /*** 进行数据归一化到[0,256]区域内并且绘制直方图***/
            float max = data[0]; //最大值
            for (int j = 1; j < data.Length; j++)
            {
                if (data[j] > max)
                {
                    max = data[j];
                }
            }
            float Sum = data2.ToList().Sum();
            float FloCount = 0;
            for (int k = 0; k < data.Length; k++)
            {
                data[k] = data[k] * 256 / max;
                FloCount += data2[k];
                if (FloCount / Sum >= 0.95)
                {
                    huidu = k;
                    break;
                }
            }}
//各种颜色空间 Hsv/Rgb/Hls/Xyz/Ycc/Gray
public static Image imageHsv=new Image(mat.Bitmap);
public static Image Rgbimg = new Image(mat.Bitmap);
public static Image Hlsimg = new Image(mat.Bitmap);
public static Image Xyzimg = new Image(mat.Bitmap);
public static Image Yccimg = new Image(mat.Bitmap);
public static Image Grayimg = new Image(mat.Bitmap);
Image[] imagesHsvs = imageHsv.Split();//分解成H、S、V三部分其他相同
//高斯滤波实现
CvInvoke.GaussianBlur(ma, ma, new Size(5, 5), 4);
//形态学闭运算,先膨胀后腐蚀  Others.matWithPhi(by)自定义模型
CvInvoke.MorphologyEx(ma, ma, Emgu.CV.CvEnum.MorphOp.Close, Others.matWithPhi(by), new Point(-1, -1), 3, Emgu.CV.CvEnum.BorderType.Default, new MCvScalar(0, 0, 0));
CvInvoke.MedianBlur(ma, ma, 5);//中值滤波实现
CvInvoke.PutText(ma05, "G num: 1", new Point(10, 100), FontFace.HersheyComplex, 0.5, new MCvScalar(255)); //指定坐标(10, 100)显示文字,中文乱码,
VectorOfPoint vp = new VectorOfPoint();
CvInvoke.ConvexHull(pointof, vp);////查找最小外接矩形cvInpaint
double dou = CvInvoke.ContourArea(vp, false);  //计算面积
VectorOfPoint vect = new VectorOfPoint();
CvInvoke.FindNonZero(ma, vect); //获取非0的点
Mat maSave1 = ma5.Clone();//备份 保留原有图片
CvInvoke.AdaptiveThreshold(ma, mas, 255, AdaptiveThresholdType.GaussianC, Emgu.CV.CvEnum.ThresholdType.Binary, 3, 0);//查找最适合二值图