工業検査におけるyolov3ターゲット認識の適用

このブログ投稿は、産業分野におけるディープラーニングアプリケーションプロジェクトを記録しています。視野内の部品の総数を検出する機能で、設定された基準数を下回ると警報が発せられますが、異なる種類の部品を配置した場合も警報が必要です。従来の方法では、従来の画像処理テンプレートマッチングを使用しますが、halconとopencvを使用したテンプレートマッチングの効果は良くないため、ディープラーニングターゲット検出を使用してみてください。

その効果を下図に示します。認識率は99%を超えています(ターゲットの数、カテゴリ、確率、長方形のフレーム位置を取得でき、照明、角度、サイズの特定の変化に適応できます)。

 

ディレクトリ:

1. darknet yolov3環境をインストールする

2.データセットを収集して作成し、yolov3でトレーニングする

3. opencv3.4バージョンでトレーニング済みモデルを使用します。

4. vsでソフトウェアを書く

テキスト:

1. https://blog.csdn.net/sinat_41852207/article/details/90906309については、別のブログを参照してください

私のコンパイル環境はyolov3 win10 cuda8.0 vs2017です。

2.データセットを収集して作成し、yolov3でトレーニングする

2.1データセットの作成方法とエンジニアリング経験

参照:データセット構築プロセスhttps://blog.csdn.net/u011574296/article/details/78953681

ステップ1:vocデータセットを理解し、空のフォルダーを作成する

1)JPEGImages フォルダー

このフォルダーには、トレーニング画像とテスト画像が混在しています。

2)Annatations フォルダー

フォルダーにはタグファイルがxml形式で格納され、各xmlファイルはJPEGImagesフォルダー内の同じ名前の画像に対応します

3)ImageSets フォルダー

Actionは、当面は使用しない人間のアクションを格納します。Layoutは、人間の体の部分に関するデータを格納します。今のところ使わない

Mainには、画像オブジェクト認識のデータが20のカテゴリに分けて格納されています。もちろん、独自に作成する必要はありません。mainには、test.txt、train.txt、val.txt、trainval.txtがあります。生成する

セグメンテーションは、セグメンテーションに使用できるデータを保存します

4)他のフォルダは説明されていません。XXXなどを分割するために使用されます。

このフォルダ形式を真似て、独自の空のフォルダを作成します。

手順2:JPEGSImagesフォルダーを取得する

1)画像をJPEGSImagesに配置します。VOC2007では、他の人の画像のファイル名はすべて000001.jpgのようになっています。また、統一された形式になっており、画像名を次のように変更しています。詳細については、別の記事を参照してくださいhttp://blog.csdn.net/gaohuazhao/article/details/60324715ファイル名を一括変更できます

手順3:アナレーションフォルダーを取得する

インターネット上にはたくさんのチュートリアルがありますが、とても面倒だと思いますウィザードアノテーションアシスタントをダウンロードして、手動でアノテーションを付け、画像情報のxmlファイルを自動生成できます

1)1つずつゆっくりとフレームを作成します。数時間後、次のステップに進むことができます

2)保存されたパスは、アナレーションフォルダーです。他の場所には保存しないでください。

 

4番目のステップ:ImageSetsフォルダーのメインフォルダーに4つのファイルを取得します(4つのtxtファイルは何ですか?名前を見て、トレーニングに使用されている画像の数とテストに使用されている画像の数を確認してください)。

trianvalはtrainとvalの合計です

前のコードは直接あなたに与える:

オリジナル:https://blog.csdn.net/gaohuazhao/article/details/60871886

このpythonスクリプトを使用して、ImageSets / Mainに4つのtxtファイル(ランダムに割り当てられたトレーニングセット、検証セット、テストセット)を生成します。

経験の要約:業界で収集された写真は非常に単一であるため、パーツは常に同じ背景に配置され、単一のデータはトレーニングされたモデルを非常に単純化し、誤認しやすくします。たとえば、猫を訓練するときは、常に緑の芝生の上に猫を置きます。モデルは、芝生の上に何かがある限り、猫であると考えるかもしれません。このトレーニングの結果、犬を芝生に乗せることができ、モデルは猫と見なされることもあります。したがって、トレーニングの際は、トレーニングセットが単一であってはなりません。トレーニング画像のパーツに加えて、一部の干渉オブジェクトを追加するなど、パーツの背景も変更して、トレーニング済みモデルの誤認識率を低減する必要があります。

 

2.2 yolov3を使用して独自のデータセットをトレーニングし、テストする

この部分については、このブログを参照してください:https : //mp.csdn.net/postedit

約300枚の画像を使用してトレーニングを行い、合計20,000回繰り返し、バッチサイズは8でした。約10,000回の反復後にほぼ収束し、20,000回の反復後、損失値は約0.2に収束しました。

        収束が困難な場合は、(1)ダーティデータ(誤ってラベル付けされたデータ)が多すぎるかどうかを検討します(2)学習率を下げるようにします。 

        トレーニングが収束しても実際のテストが間違っている場合は、トレーニング中に干渉を追加し、オブジェクトの周囲に関連のないさまざまなオブジェクトを追加することを検討してください。

        トレーニングは収束したが、実際のテスト中に認識が欠落している場合は、(1)トレーニング画像の数を増やすことを検討してください。トレーニングセットには、照明、角度、位置の変更を含めて、汎化パフォーマンスを向上させる必要があります。(2)トレーニング中、損失曲線は0に近づきすぎずに収束しますか?やっと0.2くらいになりましたが、大きすぎると収束が足りないので、ボトルネックになったら学習率を下げる必要があります。

 

3ターゲット認識のためにopencvでトレーニング済みモデルを呼び出す

内部のパスを自分のパスに変更するだけです

//在debug模式下没有优化,要在release下运行 速度快
#include <fstream>
#include <sstream>
#include <iostream>
#include <opencv2/dnn.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include<vector>


using namespace std;
using namespace cv;
using namespace dnn;

vector<string> classes;
vector<String> getOutputsNames(Net&net);
void drawPred(int classId, float conf, int left, int top, int right, int bottom, Mat& frame);
void postprocess(Mat& frame, const vector<Mat>& outs, float confThreshold, float nmsThreshold);


int main()
{
	string names_file = "D:/software_engineer/darknet/darknet/cfg/voc.name";
	String model_def = "D:/software_engineer/darknet/darknet/cfg/yolov3.cfg";
	String weights = "D:/software_engineer/darknet/darknet/backup/yolov3_last.weights";

	int in_w, in_h;
	double thresh = 0.5;//阈值
	double nms_thresh = 0.25;
	in_w = in_h = 416;

	//read names
	ifstream ifs(names_file.c_str());
	string line;
	while (getline(ifs, line)) 
		classes.push_back(line);

	//init model
	Net net1 = readNetFromDarknet(model_def, weights);
	net1.setPreferableBackend(DNN_BACKEND_DEFAULT);
	net1.setPreferableTarget(DNN_TARGET_CPU);
	Net net2 = readNetFromDarknet(model_def, weights);
	net2.setPreferableBackend(DNN_BACKEND_DEFAULT);
	net2.setPreferableTarget(DNN_TARGET_CPU);
	//read image and forward

		Mat inputImg, blob;
		inputImg = imread("D:/测试3/1.jpg");//待检图片
		if (inputImg.empty())
		{
			cout << "can't find image" << endl;
			waitKey(0);
		}
		//capture >> inputImg;


		blobFromImage(inputImg, blob, 1 / 255.0, Size(in_w, in_h), Scalar(), true, false);

		vector<Mat> mat_blob;
		imagesFromBlob(blob, mat_blob);

		//Sets the input to the network
		net1.setInput(blob);

		// Runs the forward pass to get output of the output layers
		vector<Mat> outs;
		net1.forward(outs, getOutputsNames(net1));

		postprocess(inputImg, outs, thresh, nms_thresh);

		vector<double> layersTimes;
		double freq = getTickFrequency() / 1000;
		double t = net1.getPerfProfile(layersTimes) / freq;
		string label = format("Inference time for a frame : %.2f ms", t);
		putText(inputImg, label, Point(0, 15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 255));
		namedWindow("res", WINDOW_NORMAL);
		imshow("res", inputImg);

		waitKey(0);
		
}

vector<String> getOutputsNames(Net&net)
{
	static vector<String> names;
	if (names.empty())
	{
		//Get the indices of the output layers, i.e. the layers with unconnected outputs
		vector<int> outLayers = net.getUnconnectedOutLayers();

		//get the names of all the layers in the network
		vector<String> layersNames = net.getLayerNames();

		// Get the names of the output layers in names
		names.resize(outLayers.size());
		for (size_t i = 0; i < outLayers.size(); ++i)
			names[i] = layersNames[outLayers[i] - 1];
	}
	return names;
}
void drawPred(int classId, float conf, int left, int top, int right, int bottom, Mat& frame)
{
	//Draw a rectangle displaying the bounding box
	rectangle(frame, Point(left, top), Point(right, bottom), Scalar(255, 178, 50), 3);

	//Get the label for the class name and its confidence
	string label = format("%.5f", conf);
	if (!classes.empty())
	{
		CV_Assert(classId < (int)classes.size());
		label = classes[classId] + ":" + label;
	}

	//Display the label at the top of the bounding box
	int baseLine;
	Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
	top = max(top, labelSize.height);
	rectangle(frame, Point(left, top - round(1.5*labelSize.height)), Point(left + round(1.5*labelSize.width), top + baseLine), Scalar(255, 255, 255), FILLED);
	putText(frame, label, Point(left, top), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0, 0, 0), 1);
}
void postprocess(Mat& frame, const vector<Mat>& outs, float confThreshold, float nmsThreshold)
{
	vector<int> classIds;
	vector<float> confidences;
	vector<Rect> boxes;

	for (size_t i = 0; i < outs.size(); ++i)
	{
		// Scan through all the bounding boxes output from the network and keep only the
		// ones with high confidence scores. Assign the box's class label as the class
		// with the highest score for the box.
		float* data = (float*)outs[i].data;
		for (int j = 0; j < outs[i].rows; ++j, data += outs[i].cols)
		{
			Mat scores = outs[i].row(j).colRange(5, outs[i].cols);
			Point classIdPoint;
			double confidence;
			// Get the value and location of the maximum score
			minMaxLoc(scores, 0, &confidence, 0, &classIdPoint);
			if (confidence > confThreshold)
			{
				int centerX = (int)(data[0] * frame.cols);
				int centerY = (int)(data[1] * frame.rows);
				int width = (int)(data[2] * frame.cols);
				int height = (int)(data[3] * frame.rows);
				int left = centerX - width / 2;
				int top = centerY - height / 2;

				classIds.push_back(classIdPoint.x);
				confidences.push_back((float)confidence);
				boxes.push_back(Rect(left, top, width, height));
			}
		}
	}

	// Perform non maximum suppression to eliminate redundant overlapping boxes with
	// lower confidences
	vector<int> indices;
	NMSBoxes(boxes, confidences, confThreshold, nmsThreshold, indices);
	size_t i;
	for (i = 0; i < indices.size(); ++i)
	{
		int idx = indices[i];
		Rect box = boxes[idx];
		drawPred(classIds[idx], confidences[idx], box.x, box.y,box.x + box.width, box.y + box.height, frame);
	}
	cout << "目标数量数量:" << i << endl;
}




最終テストでは、1500枚の写真があり、1つの欠落したパーツが特定されました。つまり、パーツのないパーツや異なるモデルのパーツは特定されないという誤認はありませんでした。これは検出の目的を達成します。

元の記事を59件公開 いいね46 30,000回以上訪問

おすすめ

転載: blog.csdn.net/sinat_41852207/article/details/103025737