1

我正在为图像处理应用程序创建一个远程查看器,并且需要通过网络发送结果以便记录它们。我目前正在尝试使用基于 QUpdSocket 的 UDP 套接字流式传输图像。UDP 很好,因为我不需要每个图像,所以我可以处理潜在的数据包丢失。在 localhost 上测试下面的实现时,我可以得到 >20 fps,这比我在实际应用程序中需要的要多得多。但是,一旦我移至网络上的 2 台 PC,我几乎不会收到任何图像。我知道数据包正在通过,因为我可以看到网络活动,但是我正在丢弃负载,或者解码/渲染发生了其他奇怪的事情。

该实现非常基本,因此任何指针都会受到赞赏。或者我不应该自己这样做,因为这类事情有很好的库?

发送方:等待接收方发送数据包并识别自己,找到后,拍摄一张图像,将其拆分为数据报大小(此处为 508),然后发送 x 个数据报。

while (!foundReceiver) {
                while (!socket.waitForReadyRead(500));
                QByteArray buffer;
                buffer.resize(socket.pendingDatagramSize());

                socket.readDatagram(buffer.data(), buffer.size(), &sender, nullptr);
                if (strcmp("RECEIVER", buffer) == 0) {
                    foundReceiver = true;
                    printf_s("Found receiver on : %s", sender.toString().toStdString().c_str());
                }
            }

            printf_s("ctrl-c to quit\n");

            while (!socket.hasPendingDatagrams()) {
                cv::Mat img = ih->getLatestImage();
                if (!img.empty()) {
                    std::vector<uint8_t> buf;
                    std::vector<int> params;
                    params.push_back(IMWRITE_JPEG_QUALITY);
                    params.push_back(80);
                    cv::imencode(".jpg", img, buf, params);

                    char imgDetails[28];
                    int numPackets = std::ceil((float)buf.size() / (float)datagramSize);
                    sprintf_s(imgDetails, "START %04d %04d %04d %06d", img.rows, img.cols, numPackets, datagramSize);

                    bool result = socket.writeDatagram(imgDetails, sizeof(imgDetails), sender, 9000);

                    for (int i = 0; i < numPackets; i++) {
                        int start = i * datagramSize;
                        int end = ((i + 1) * datagramSize);
                        if (end > buf.size())
                            end = buf.size();
                        std::vector<uint8_t> shortBuff((end - start) + 4);
                        char np[5];
                        sprintf_s(np, "%04d", i);
                        for (int j = 0; j < 4; j++)
                            shortBuff[j] = np[j];
                        std::copy(buf.begin() + start, buf.begin() + end, shortBuff.begin() + 4);
                        bool result = socket.writeDatagram((char*)shortBuff.data(), shortBuff.size(), sender, 9000);
                    }
                }
                camera.ExecuteSoftwareTrigger();
            }
            QByteArray buffer;
            buffer.resize(socket.pendingDatagramSize());

            socket.readDatagram(buffer.data(), buffer.size(), &sender, nullptr);
            if (strcmp("RECEIVER DISCONNECT", buffer) == 0) {
                foundReceiver = false;
                printf_s("Receiver %s disconnected", sender.toString().toStdString().c_str());
            }
        }

接收方:QUdpSocket 调用 datagramPending 读取数据报,将其发送到 precessDatagram 决定是头部还是图像数据包。如果它是一个图像数据包,则将内容移动到由数据包开头的 4 位数字指定的位置的缓冲区中。当所有图像数据包都被接收到后,将缓冲区转换为图像。

void UDPSocket::datagramPending() {
    while (socket->hasPendingDatagrams()) {
        QByteArray buffer;
        buffer.resize(socket->pendingDatagramSize());

        QHostAddress sender;

        socket->readDatagram(buffer.data(), buffer.size(), &sender, nullptr);

        emit receivedDatagram(buffer, sender);
    }
}

void Receiver::processDatagram(QByteArray buff, QHostAddress address) {

if (buff.size() > 0) {
    ImageHeader ih = isStartPacket(buff);

    if (ih.startPacket) {
        streamStarted = true;
        senderAddress = address;

        currentImageRows = ih.rows;
        currentImageCols = ih.cols;
        currentImageNumPackets = ih.numPackets;
        currentImagePacketSize = ih.packetSize;

        rawBuffer = std::vector<uint8_t>(currentImagePacketSize * currentImageNumPackets);

        numReceivedPackets = 0;
    }
    else {
        if (rawBuffer.size() > 0) {
            std::vector<uint8_t> packetNumChar(5);
            std::move(buff.begin(), buff.begin() + 4, packetNumChar.begin());

            int packetNumber;
            packetNumChar[4] = '\0';
            sscanf_s((const char*)packetNumChar.data(), "%04d", &packetNumber);

            int start = packetNumber * currentImagePacketSize;

            if ((start + buff.size()) < rawBuffer.size())
                std::move(buff.begin() + 4, buff.end(), rawBuffer.begin() + start);

            numReceivedPackets++;
            if (numReceivedPackets == currentImageNumPackets)
                emit receivedImage(rawBuffer);
        }
    }
}
}

ImageHeader Receiver::isStartPacket(QByteArray buff) {
    int rows = 0, cols = 0, numPackets = 0, packetSize = 0;
    bool startPacket = false;

    if (strncmp(buff.data(), startHeader, 5) == 0)
        startPacket = true;

    if (startPacket) 
        sscanf_s(buff.data(), "START %04d %04d %04d %06d", &rows, &cols, &numPackets, &packetSize);

    ImageHeader ih = { startPacket, rows, cols, numPackets, packetSize };

    return ih;
}

void Receiver::receivedImage(std::vector<uint8_t> buff) {
    rawImageData = cv::Mat(1, buff.size(), CV_8UC1, buff.data());
    currentImage = cv::imdecode(rawImageData, IMREAD_COLOR);
}
4

0 回答 0