You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

widget.cpp 18 kB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. /*
  2. * Copyright (C) 2021 刘臣轩
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. #include "widget.h"
  18. #include "ui_widget.h"
  19. Widget::Widget(QWidget* parent)
  20. : QWidget(parent)
  21. , ui(new Ui::Widget)
  22. {
  23. ui->setupUi(this);
  24. connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(close()));
  25. ui->frame->setStyleSheet("#frame {border-image: url(:/new/prefix1/image/主.png);}");
  26. ui->textEdit->append("开始初始化设备");
  27. // Time
  28. QTimer* timer = new QTimer(this);
  29. connect(timer, SIGNAL(timeout()), this, SLOT(timerUpdate()));
  30. timer->start(500);
  31. initSerial();
  32. #ifdef Q_OS_WIN
  33. initCamera();
  34. #else
  35. connect(this, SIGNAL(imageCaptured(int, QImage)), this, SLOT(onImageCaptured(int, QImage)));
  36. ui->textEdit->append("摄像头初始化成功");
  37. #endif
  38. // Network
  39. networkManager = new QNetworkAccessManager();
  40. networkRequest = new QNetworkRequest();
  41. networkRequest->setHeader(QNetworkRequest::ContentTypeHeader, " application/json;charset=UTF-8");
  42. url = new QUrl("https://aiapi.jd.com/jdai/garbageImageSearch");
  43. connect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(onRequestFinished(QNetworkReply*)));
  44. ui->label_3->setText("工训大赛");
  45. ui->label_4->setStyleSheet("border-image: url(:/new/prefix1/image/工训大赛.png);\nborder-radius: 10px;\n");
  46. ui->label_4->setVisible(true);
  47. ui->label_5->setVisible(false);
  48. ui->textEdit->append("设备初始化成功√");
  49. number = 0;
  50. // Video
  51. player = new QMediaPlayer;
  52. videoWidget = new QVideoWidget(this);
  53. playList = new QMediaPlaylist;
  54. #ifdef Q_OS_WIN
  55. playList->addMedia(QUrl::fromLocalFile("../WasteSorting/test.mp4"));
  56. #else
  57. playList->addMedia(QUrl::fromLocalFile("/home/pi/WasteSorting/test.mp4"));
  58. #endif
  59. playList->setPlaybackMode(QMediaPlaylist::CurrentItemInLoop);
  60. player->setPlaylist(playList);
  61. player->setVideoOutput(videoWidget);
  62. ui->verticalLayout->addWidget(videoWidget);
  63. videoWidget->setVisible(false);
  64. videoTimer = new QTimer(this);
  65. connect(videoTimer, SIGNAL(timeout()), this, SLOT(videoTimerUpdate()));
  66. videoTimer->setSingleShot(true);
  67. videoTimer->start(10000);
  68. #ifdef Q_OS_WIN
  69. #else
  70. // Tensorflow
  71. model = tflite::FlatBufferModel::BuildFromFile(model_file.c_str());
  72. tflite::InterpreterBuilder(*model, resolver)(&interpreter);
  73. interpreter->SetNumThreads(4);
  74. interpreter->AllocateTensors();
  75. input_tensor = interpreter->tensor(interpreter->inputs()[0]);
  76. TfLiteIntArray* output_dims = interpreter->tensor(interpreter->outputs()[0])->dims;
  77. output_size = output_dims->data[output_dims->size - 1];
  78. #endif
  79. //captureImage();
  80. }
  81. Widget::~Widget()
  82. {
  83. delete ui;
  84. }
  85. void Widget::timerUpdate()
  86. {
  87. QString str = QDateTime::currentDateTime().toString("yyyy年MM月dd日 hh:mm:ss");
  88. ui->label->setText(str);
  89. }
  90. void Widget::videoTimerUpdate()
  91. {
  92. ui->label_4->setVisible(false);
  93. player->play();
  94. videoWidget->setVisible(true);
  95. ui->label_3->setText("播放视频");
  96. ui->textEdit->append("播放视频");
  97. }
  98. void Widget::initSerial()
  99. {
  100. ui->textEdit->append("开始初始化串口");
  101. serialPort = new QSerialPort();
  102. connect(serialPort, SIGNAL(readyRead()), this, SLOT(serialRead()));
  103. if (QSerialPortInfo::availablePorts().length() == 0) {
  104. QMessageBox::critical(this, "错误", "无可用串口设备,请检查硬件连接后重试");
  105. exit(0);
  106. }
  107. #ifdef Q_OS_WIN
  108. QString portName = QSerialPortInfo::availablePorts()[1].portName();
  109. qDebug() << portName;
  110. ui->textEdit->append("尝试连接串口" + portName);
  111. serialPort->setPortName(portName);
  112. #else
  113. QString portName = "ttyUSB0";
  114. ui->textEdit->append("尝试连接串口ttyUSB0");
  115. serialPort->setPortName("ttyUSB0");
  116. #endif
  117. if (serialPort->open(QIODevice::ReadWrite)) {
  118. ui->textEdit->append("串口连接成功");
  119. serialPort->setBaudRate(115200);
  120. serialPort->setDataBits(QSerialPort::Data8);
  121. serialPort->setParity(QSerialPort::NoParity);
  122. serialPort->setStopBits(QSerialPort::OneStop);
  123. } else {
  124. QMessageBox::critical(this, "错误", "串口设备" + portName + "无法打开,请检查硬件连接后重试");
  125. exit(0);
  126. }
  127. ui->textEdit->append("串口初始化成功");
  128. serialWrite('\xFF');
  129. }
  130. void Widget::initCamera()
  131. {
  132. // for (QCameraInfo& info : QCameraInfo::availableCameras()) {
  133. // qDebug() << info.deviceName();
  134. // }
  135. int camNum = QCameraInfo::availableCameras().length();
  136. if (!camNum) {
  137. QMessageBox::critical(this, "错误", "无可用摄像头,请检查硬件连接后重试");
  138. exit(0);
  139. }
  140. ui->textEdit->append("开始初始化摄像头");
  141. camera = new QCamera(QCameraInfo::availableCameras()[0], this);
  142. imageCapture = new QCameraImageCapture(camera);
  143. connect(imageCapture, SIGNAL(imageCaptured(int, QImage)), this, SLOT(onImageCaptured(int, QImage)));
  144. camera->setCaptureMode(QCamera::CaptureStillImage);
  145. imageCapture->setCaptureDestination(QCameraImageCapture::CaptureToBuffer);
  146. camera->start();
  147. ui->textEdit->append("摄像头初始化成功");
  148. }
  149. void Widget::serialRead()
  150. {
  151. QByteArray buffer = serialPort->readAll();
  152. // qDebug() << buffer;
  153. if (buffer[0] == '\x03' && buffer[1] == '\xFC'
  154. && buffer[3] == '\xFC' && buffer[4] == '\x03') {
  155. switch (buffer[2]) {
  156. case '\x00':
  157. ui->textEdit->append("取消警报");
  158. ui->label_3->setText("取消警报");
  159. ui->label_4->setVisible(true);
  160. ui->label_5->setVisible(false);
  161. ui->frame->setStyleSheet("#frame {border-image: url(:/new/prefix1/image/主.png);}");
  162. videoTimer->start(10000);
  163. break;
  164. case '\x01':
  165. ui->textEdit->append("触发拍照信号");
  166. ui->label_3->setText("触发拍照");
  167. videoTimer->stop();
  168. videoWidget->setVisible(false);
  169. player->stop();
  170. #ifdef Q_OS_WIN
  171. imageCapture->capture();
  172. #else
  173. captureImage();
  174. #endif
  175. break;
  176. case '\x02':
  177. ui->textEdit->append("投递完毕");
  178. ui->label_3->setText("投递完毕");
  179. ui->label_4->setVisible(true);
  180. ui->label_5->setVisible(false);
  181. ui->frame->setStyleSheet("#frame {border-image: url(:/new/prefix1/image/主.png);}");
  182. videoTimer->start(10000);
  183. break;
  184. case '\x04':
  185. ui->textEdit->append("满载警报");
  186. ui->label_3->setText("满载警报");
  187. ui->label_4->setVisible(false);
  188. videoTimer->stop();
  189. videoWidget->setVisible(false);
  190. player->stop();
  191. ui->frame->setStyleSheet("#frame {border-image: url(:/new/prefix1/image/满载警报.png);}");
  192. break;
  193. case '\x08':
  194. // qDebug() << "倾倒警报";
  195. ui->textEdit->append("倾倒警报");
  196. ui->label_3->setText("倾倒警报");
  197. ui->label_4->setVisible(false);
  198. videoTimer->stop();
  199. videoWidget->setVisible(false);
  200. player->stop();
  201. ui->frame->setStyleSheet("#frame {border-image: url(:/new/prefix1/image/倾倒警报.png);}");
  202. break;
  203. case '\xFF':
  204. break;
  205. }
  206. }
  207. }
  208. void Widget::serialWrite(const char data)
  209. {
  210. QByteArray buffer("\x30\xCF\x0F\xCF\x30");
  211. buffer[2] = data;
  212. serialPort->write(buffer);
  213. }
  214. void Widget::captureImage()
  215. {
  216. // system("raspistill -o ../WasteSorting/WasteSorting.jpg -t 1 -br 60 -hf -awb sun");
  217. system("python3 ../WasteSorting/capture.py");
  218. QImage image("../WasteSorting/WasteSorting.jpg");
  219. emit(imageCaptured(0, image));
  220. }
  221. void Widget::onImageCaptured(int, QImage image)
  222. {
  223. // 显示图片
  224. ui->label_4->setVisible(false);
  225. ui->label_5->setPixmap(QPixmap::fromImage(image).scaled(405, 306));
  226. ui->label_5->setVisible(true);
  227. ui->label_3->setText("识别中");
  228. ui->frame->setStyleSheet("#frame {border-image: url(:/new/prefix1/image/识别中.png);}");
  229. /* 使用京东垃圾识别 API
  230. // Base64编码
  231. QByteArray ba;
  232. QBuffer buf(&ba);
  233. image.save(&buf, "JPG", -1);
  234. QByteArray imageBase64 = ba.toBase64();
  235. // qDebug() << "data:image/jpg;base64," + imageBase64;
  236. // Request
  237. sendRequest(imageBase64);
  238. */
  239. /* TensorFlow Lite Python
  240. #ifdef Q_OS_WIN
  241. image.save("../WasteSorting/WasteSorting.jpg");
  242. FILE* fp = _popen("python ../WasteSorting/tensorflow/label_image.py", "rt");
  243. char buf[255] = { 0 };
  244. fscanf(fp, "%s", buf);
  245. _pclose(fp);
  246. #else
  247. FILE* fp = popen("python3 ../WasteSorting/tensorflow/label_image.py", "r");
  248. char buf[255] = { 0 };
  249. printf("%s", buf);
  250. fscanf(fp, "%s", buf);
  251. pclose(fp);
  252. #endif
  253. QString cate_name = QString::fromLocal8Bit(buf);
  254. qDebug() << cate_name;
  255. classifyFinished(cate_name);
  256. */
  257. /* Tensorflow Lite C++ */
  258. image.save("../WasteSorting/WasteSorting.jpg");
  259. image = image.convertToFormat(QImage::Format_RGB888).mirrored(true, false);
  260. formatImageTFLite<uint8_t>(interpreter->typed_tensor<uint8_t>(interpreter->inputs()[0]), image.bits(),
  261. image.height(), image.width(), 3, 224, 224, 3, false);
  262. interpreter->Invoke();
  263. std::vector<std::pair<float, int>> top_results;
  264. get_top_n<uint8_t>(interpreter->typed_output_tensor<uint8_t>(0),
  265. output_size, 1, 0.01f, &top_results, kTfLiteUInt8);
  266. int index = top_results[0].second;
  267. QString cate_name;
  268. switch (index) {
  269. case 0:
  270. cate_name = "识别失败";
  271. break;
  272. case 1:
  273. case 2:
  274. case 3:
  275. cate_name = "有害垃圾";
  276. break;
  277. case 4:
  278. case 5:
  279. case 6:
  280. cate_name = "可回收垃圾";
  281. break;
  282. case 7:
  283. case 8:
  284. cate_name = "厨余垃圾";
  285. break;
  286. default:
  287. cate_name = "其他垃圾";
  288. break;
  289. }
  290. qDebug() << cate_name;
  291. classifyFinished(cate_name);
  292. }
  293. template <class T>
  294. void Widget::formatImageTFLite(T* out, const uint8_t* in, int image_height, int image_width, int image_channels, int wanted_height, int wanted_width, int wanted_channels, bool input_floating)
  295. {
  296. const float input_mean = 127.5f;
  297. const float input_std = 127.5f;
  298. int number_of_pixels = image_height * image_width * image_channels;
  299. std::unique_ptr<tflite::Interpreter> interpreter(new tflite::Interpreter);
  300. int base_index = 0;
  301. // two inputs: input and new_sizes
  302. interpreter->AddTensors(2, &base_index);
  303. // one output
  304. interpreter->AddTensors(1, &base_index);
  305. // set input and output tensors
  306. interpreter->SetInputs({ 0, 1 });
  307. interpreter->SetOutputs({ 2 });
  308. // set parameters of tensors
  309. TfLiteQuantizationParams quant;
  310. interpreter->SetTensorParametersReadWrite(0, kTfLiteFloat32, "input", { 1, image_height, image_width, image_channels }, quant);
  311. interpreter->SetTensorParametersReadWrite(1, kTfLiteInt32, "new_size", { 2 }, quant);
  312. interpreter->SetTensorParametersReadWrite(2, kTfLiteFloat32, "output", { 1, wanted_height, wanted_width, wanted_channels }, quant);
  313. tflite::ops::builtin::BuiltinOpResolver resolver;
  314. const TfLiteRegistration* resize_op = resolver.FindOp(tflite::BuiltinOperator_RESIZE_BILINEAR, 1);
  315. auto* params = reinterpret_cast<TfLiteResizeBilinearParams*>(malloc(sizeof(TfLiteResizeBilinearParams)));
  316. params->align_corners = false;
  317. interpreter->AddNodeWithParameters({ 0, 1 }, { 2 }, nullptr, 0, params, resize_op, nullptr);
  318. interpreter->AllocateTensors();
  319. // fill input image
  320. // in[] are integers, cannot do memcpy() directly
  321. auto input = interpreter->typed_tensor<float>(0);
  322. for (int i = 0; i < number_of_pixels; i++)
  323. input[i] = in[i];
  324. // fill new_sizes
  325. interpreter->typed_tensor<int>(1)[0] = wanted_height;
  326. interpreter->typed_tensor<int>(1)[1] = wanted_width;
  327. interpreter->Invoke();
  328. auto output = interpreter->typed_tensor<float>(2);
  329. auto output_number_of_pixels = wanted_height * wanted_height * wanted_channels;
  330. for (int i = 0; i < output_number_of_pixels; i++) {
  331. if (input_floating)
  332. out[i] = (output[i] - input_mean) / input_std;
  333. else
  334. out[i] = (uint8_t)output[i];
  335. }
  336. }
  337. template <class T>
  338. void Widget::get_top_n(T* prediction, int prediction_size, size_t num_results,
  339. float threshold, std::vector<std::pair<float, int>>* top_results,
  340. TfLiteType input_type)
  341. {
  342. // Will contain top N results in ascending order.
  343. std::priority_queue<std::pair<float, int>, std::vector<std::pair<float, int>>,
  344. std::greater<std::pair<float, int>>>
  345. top_result_pq;
  346. const long count = prediction_size; // NOLINT(runtime/int)
  347. float value = 0.0;
  348. for (int i = 0; i < count; ++i) {
  349. switch (input_type) {
  350. case kTfLiteFloat32:
  351. value = prediction[i];
  352. break;
  353. case kTfLiteInt8:
  354. value = (prediction[i] + 128) / 256.0;
  355. break;
  356. case kTfLiteUInt8:
  357. value = prediction[i] / 255.0;
  358. break;
  359. default:
  360. break;
  361. }
  362. // Only add it if it beats the threshold and has a chance at being in
  363. // the top N.
  364. if (value < threshold) {
  365. continue;
  366. }
  367. top_result_pq.push(std::pair<float, int>(value, i));
  368. // If at capacity, kick the smallest value out.
  369. if (top_result_pq.size() > num_results) {
  370. top_result_pq.pop();
  371. }
  372. }
  373. // Copy to output vector and reverse into descending order.
  374. while (!top_result_pq.empty()) {
  375. top_results->push_back(top_result_pq.top());
  376. top_result_pq.pop();
  377. }
  378. std::reverse(top_results->begin(), top_results->end());
  379. }
  380. void Widget::sendRequest(QByteArray& imageBase64)
  381. {
  382. QUrlQuery query;
  383. query.addQueryItem("appkey", "3a24b33468565b633d25d426eb0c660c");
  384. qint64 timestamp = QDateTime::currentDateTime().toMSecsSinceEpoch();
  385. query.addQueryItem("timestamp", QString::number(timestamp));
  386. QString sign = "58125e5985e6ef2d385ebfaa646987ba" + QString::number(timestamp);
  387. QByteArray ba = QCryptographicHash::hash(sign.toUtf8(), QCryptographicHash::Md5);
  388. query.addQueryItem("sign", ba.toHex());
  389. url->setQuery(query);
  390. networkRequest->setUrl(*url);
  391. // qDebug() << query.toString();
  392. QJsonObject json;
  393. json.insert("imgBase64", QString(imageBase64));
  394. json.insert("cityId", "440300");
  395. QJsonDocument document;
  396. document.setObject(json);
  397. QByteArray data = document.toJson(QJsonDocument::Compact);
  398. // qDebug() << QString(data);
  399. networkManager->post(*networkRequest, data);
  400. }
  401. void Widget::onRequestFinished(QNetworkReply* reply)
  402. {
  403. QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
  404. if (!statusCode.isValid()) {
  405. QMessageBox::critical(this, "错误", "网络连接不可用,请检查后重试");
  406. exit(0);
  407. }
  408. QByteArray replyData = reply->readAll();
  409. QJsonParseError jsonError;
  410. QJsonDocument document = QJsonDocument::fromJson(replyData, &jsonError);
  411. if (!document.isNull() && (jsonError.error == QJsonParseError::NoError)) {
  412. QJsonObject object = document.object().value("result").toObject();
  413. QJsonArray array = object.value("garbage_info").toArray();
  414. QJsonObject max = std::max_element(array.begin(), array.end(),
  415. [](QJsonValue const& a, QJsonValue const& b) { return a.toObject().value("confidence").toDouble()
  416. < b.toObject().value("confidence").toDouble(); })
  417. ->toObject();
  418. QString cate_name = max.value("cate_name").toString();
  419. QString garbage_name = max.value("garbage_name").toString();
  420. double confidence = max.value("confidence").toDouble();
  421. // qDebug() << "cate_name:" << cate_name;
  422. // qDebug() << "garbage_name:" << garbage_name;
  423. // qDebug() << "confidence:" << confidence;
  424. // ui->textEdit->append("cate_name: " + cate_name);
  425. // ui->textEdit->append("garbage_name: " + garbage_name);
  426. // ui->textEdit->append("confidence: " + QString::number(confidence));
  427. classifyFinished(cate_name);
  428. } else {
  429. serialWrite('\xFD');
  430. //ui->textEdit->append("识别失败,请重试");
  431. //ui->label_3->setText("识别失败");
  432. ui->label_4->setVisible(true);
  433. ui->label_5->setVisible(false);
  434. ui->frame->setStyleSheet("#frame {border-image: url(:/new/prefix1/image/主.png);}");
  435. }
  436. }
  437. void Widget::classifyFinished(QString cate_name)
  438. {
  439. ui->frame->setStyleSheet("#frame {border-image: url(:/new/prefix1/image/" + cate_name + ".PNG);}");
  440. ui->label_3->setText("投递中");
  441. if (cate_name == "识别失败") {
  442. serialWrite('\xFD');
  443. //ui->textEdit->append("识别失败,请重试");
  444. //ui->label_3->setText("识别失败");
  445. ui->label_4->setVisible(true);
  446. ui->label_5->setVisible(false);
  447. ui->frame->setStyleSheet("#frame {border-image: url(:/new/prefix1/image/主.png);}");
  448. } else {
  449. number += 1;
  450. ui->textEdit->append(QString::number(number) + " " + cate_name + " 1 OK!");
  451. if (cate_name == "可回收垃圾")
  452. serialWrite('\x01');
  453. else if (cate_name == "厨余垃圾")
  454. serialWrite('\x02');
  455. else if (cate_name == "有害垃圾")
  456. serialWrite('\x04');
  457. else
  458. serialWrite('\x08');
  459. }
  460. }

No Description

Contributors (1)