C++     -> 검은색 바탕 

Python -> 흰색 바탕


Socket통신 도중 GUI 쓰레드가 Block되면 안되기 때문에 쓰레드로 등록할 소켓 클래스 생성


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class SocWorker : public QObject{
    Q_OBJECT
 
public:
    SocWorker();
   ~SocWorker();
 
 
public slots:
    void connectServer(const QString _addr, const int _port);
    void disConnectServer();
    void onServerConnected();
    void onAskTask(QString _fPath, int _mode, bool compress, QString _option = ""  );
    void onRecv();
 
signals:
    void resultReady(const QByteArray result);
    void sendMsg(const QString _text);
 
 
private:
    QString hostName;
    QString port;
    QTcpSocket* sendSock;
    QByteArray recvData;
    int targetLength = -1;
    bool is_recvStart = false;
};
 
cs


 GUI로부터 IP와 PORT번호를 받아서 Python 서버 측으로 서버 접속 시도


1
2
3
4
5
6
7
8
9
10
void SocWorker::connectServer(const QString _addr, const int _port){
    qDebug() << __func__;
 
    sendSock->connectToHost(_addr, _port);
 
    if(!sendSock->waitForConnected(TIMEOUT)){
        emit sendMsg("server is not responding");
    }
 
}
cs


 Python Server측에서는 localhost의 아이피로 소켓을 생성하고 1234포트를 열고서 listen 함수에서 접속을 대기하고 있다가 접속 요청이 오면 수락한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
    ########### 통신 #############
 
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind((RECV_TCP_IP, RECV_TCP_PORT))
 
    # Wating For Connect
    s.listen(True)
 
    # When Connetion Sucessed , Return Handler and address of client
    conn, addr = s.accept()
    conn.setblocking(True)
 
    print(conn)
    print(addr)
cs


C++쪽 createProc 함수에서 프로토콜을 생성한후 소켓을 한번 비워주고(버그 예방차원에서..) 해당 프로토콜을 전송한후 제대로 전송할때까지 스레드를 블락한다.


1
2
3
4
5
6
7
8
9
    QByteArray proc = createProc(_fPath, _mode, compress, _option);
 
    qDebug() << "send data size : " << proc.length();
 
    sendSock->flush();
 
    sendSock->write(proc);
    sendSock->waitForBytesWritten(1000);
 
cs



서버측에서는 recvAndLoad 함수에서 프로토콜을 읽고 각 바이트에 맞는 정보로 분할한뒤 해당 명령이 어떤 명령인지 int형으로 리턴한다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
def recvAndLoad():
    global DRIVER_NAME, FILE_PATH, VALUE, LUT, inImage, outImage, b_img, inH, inW, outW, outH
    global R, G, V, H, S, V, sx, ex, sy, ey
 
    sx = ex = sy = ey = 0
    i_totLength = b_totData = b_meta = list_meta = 0
 
    # 전체 데이터 크기 읽기 4바이트
    i_totLength = int.from_bytes(recvall(conn, 4), byteorder='big')
 
    print("TOTAL LENGTH : ", i_totLength)
 
    # 4바이트 용량제외하고 모두다 읽기
    b_totData = recvall(conn, i_totLength - 4)
 
    # 첫 200바이트는 메타데이터
    b_meta = b_totData[0200]
 
    list_meta = str(b_meta).split(":")
 
    DRIVER_NAME = list_meta[0]
    FILE_PATH   = list_meta[1]
    MODE        = list_meta[2]
    OPTION      = list(filter(lambda x: x != "", list_meta[3].split("|")))
 
    OPTION.extend( [''* 3 );
 
    WIDTH       = OPTION[0]
    HEIGHT      = OPTION[1]
    COMPRESS    = OPTION[2]
 
 
    VALUE       = int( OPTION[3] ) if OPTION[3].isdigit() else OPTION[3]
 
    POINTS      = OPTION[4]
 
    print(POINTS)
 
    if POINTS != '':
        if POINTS[0== "P":
            loadPerspectiveValues(POINTS[1 : ])
        elif POINTS[0== "Z":
            loadRoi(POINTS[ 1 : ])
 
        else:
            loadRois(POINTS)
 
 
    # 나머지는 이미지 데이터
    b_img = b_totData[200:]
 
    print("OPTION          :", OPTION       )
    print("DRIVER_NAME     :", DRIVER_NAME  )
    print("FILE_PATH       :", FILE_PATH    )
    print("MODE            :", MODE         )
    print("IMAGE WIDTH     :", WIDTH        )
    print("IMAGE HEIGHT    :", HEIGHT       )
    print("IMAGE COMPRESS  :", COMPRESS     )
    print("VALUE           :", VALUE        )
 
 
    loadImage(WIDTH, HEIGHT)
 
    return int(MODE)
 
cs


recvAndLoad 함수로부터 어떤 명령인지를 받으면 해당 명령에 따라서 영상처리 알고리즘을 적용시킨다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
            mode  = recvAndLoad()            
 
            if mode == 0:
                equalImage()
 
            elif mode == 1:
                addImage()
 
            elif mode == 2:
                reverseImage()
 
            elif mode == 3:
                binImage()
 
            elif mode == 4:
                paraImage()
.............
 
cs


알고리즘이 적용된 이미지를 바이트로 만든 후 해당바이트의 길이를 4바이트 인트형으로 먼저 전송한 후 이미지를 뒤이어 전송한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def sendImage():
    global DRIVER_NAME, FILE_PATH, VALUE, LUT, START, inImage, outImage, b_img, inH, inW, outW, outH, b_outImg
 
    FIXED_OUTH = 512
    FIXED_OUTW = 512
 
    R = Image.fromarray(outImage[0].reshape((FIXED_OUTH, FIXED_OUTW)))
    G = Image.fromarray(outImage[1].reshape((FIXED_OUTH, FIXED_OUTW)))
    B = Image.fromarray(outImage[2].reshape((FIXED_OUTH, FIXED_OUTW)))
 
 
    b_outImg = Image.merge("RGB", (R, G, B)).tobytes()
 
    length = len(b_outImg)
 
    print("bytes To Send : ", length)
 
    conn.send(length.to_bytes(4, byteorder='little'))
    print(conn.send(b_outImg[:]))
    print( "elapsed time : ", time.time() - START )
 
 
cs


readyRead시그널에 의해서 onRecv 함수가 호출되고 


1
    connect(sendSock,  &QTcpSocket::readyRead     , this&SocWorker::onRecv           );
cs


onRecv 함수에서는 첫 4바이트를 먼저 읽고 전체 바이트가 도착할때까지 recvData에 붙이다가 전체 데이터가 도착하면 데이터를 resultReady시그널과 같이 보낸다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void SocWorker::onRecv(){
 
    if(is_recvStart != true){
 
        char header[4];
        sendSock -> read(header, 4);
 
        targetLength = *reinterpret_cast<int*>(header);
        is_recvStart = true;
 
        recvData.clear();
    }
 
    recvData.push_back(sendSock->readAll());
 
    if(recvData.length() == targetLength){
 
        emit resultReady( recvData );
        is_recvStart = false;
 
        targetLength = -1;
 
    }
}
 
cs





+ Recent posts