dev-logs

[C] 인접한 직사각형 합쳐서 최대 만들기 본문

공부/알고리즘

[C] 인접한 직사각형 합쳐서 최대 만들기

두룹두두 2018. 4. 9. 17:53






여러 개의 인접한 직사각형을 합치는 알고리즘 입니다! (아직 버그가 있습니다.....ㅜ)


저 같은 경우에는 라벨링 후 처리 로 사용하려고 만들었습니다.



라벨링을 해도 잘 안 합쳐지는 게 간혹 있더라고요. 


그리고 isolated 된 부분의 경우, 간혹 라벨이 포개어져서? 나오기도 합니다.





간략하게 그림으로 그리면 아래와 같습니다.













input: 직사각형들

output: input을 모두 포함하는 최대 직사각형




※ C언어 입니다. (코드 못짬 주의)



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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
void MK_maxNmin(int a, int b, int c, int d, int *max, int *min){ //4개 정수에서 최대, 최소 구하기
 int arr[4];
 int i;
 arr[0= a; arr[1= b; arr[2= c; arr[3= d;
 *max = a;
 *min = a;
 for(i=1; i < 4; i++){
     if(*max < arr[i])
         *max = arr[i];
     if(*min > arr[i])
         *min = arr[i];
 }
        
}
int mergeLabel(MK_Label *labels, MK_Label *dst,int labelNum){
    //아주 인접한 label은 합친다.
    int left1, right1, top1, bottom1;
    int left2, right2, top2, bottom2; //비교 라벨
    int left3, right3, top3, bottom3; //최종 라벨
    int i,j,a,b;
    MK_Label tmp[20]; //input 개수가 최대 20개여야 함.
    int labelCnt = 0;
    int intersectionCnt=0//겹친 횟수 세는 변수
    int x=0, y=0;
    int overlapFlag=0//이미 탐색 됐는지 mark 하는 변수
    for(i=0; i<20; i++){
        for(j=0; j<4; j++){
            rectArr[i][j]=0//rectArr 초기화
        }
    }
    for(i=0; i<labelNum; i++){
        if(labelCnt > 19)
            break;
        if(intersectionCnt == labelNum-1//라벨 모두 탐색했으면
            break;
        intersectionCnt = 0;
        //라벨링 박스 주변 영역의 좌표저장
        left1 = labels[i].boundingRect.x;
        top1 = labels[i].boundingRect.y;
        right1 = labels[i].boundingRect.x + labels[i].boundingRect.width-1;
        bottom1 = labels[i].boundingRect.y + labels[i].boundingRect.height-1;
        //다른 라벨과 위치 비교
        for(j = i+1; j< labelNum; j++){
            //다른 라벨 꼭지점 저장
            left2 = labels[j].boundingRect.x;
            top2 = labels[j].boundingRect.y;
            right2 = labels[j].boundingRect.x + labels[j].boundingRect.width-1;
            bottom2 = labels[j].boundingRect.y + labels[j].boundingRect.height-1;
            for(a=0; a<y; a++){
                b=0;
                if(rectArr[a][b] == left2 && rectArr[a][b+1== right2 && rectArr[a][b+2== top2 && rectArr[a][b+3== bottom2) // 현재 탐색라벨이 이미 탐색됐다면 표시하고, 겹침을 고려하지 않음
                    overlapFlag = 1;
                else if(rectArr[a][b] == left1 && rectArr[a][b+1== right1 && rectArr[a][b+2== top1 && rectArr[a][b+3== bottom1)
                    overlapFlag =1;
            }
            
            if( (top1-5 > bottom2+5|| (bottom1+5 < top2-5|| (left1 > right2) || (right1 < left2)){ //수직 방향으로 5px 인접하면 합침. (이 부분에서 인접범위 조절 가능)
                if(overlapFlag == 1)
                    continue;
                if(j==labelNum-1){
                    if(intersectionCnt>0){//마지막 회차, 적어도 한번 이상 겹쳤으면 최대영역 좌표 저장
                        tmp[labelCnt].boundingRect.x = left3;
                        tmp[labelCnt].boundingRect.y = top3;
                        tmp[labelCnt].boundingRect.width = right3 - left3+1;
                        tmp[labelCnt].boundingRect.height = bottom3 - top3+1;
                        tmp[labelCnt].center.x = (right3+left3)/2;
                        tmp[labelCnt++].center.y = (top3+bottom3)/2;
                    }
                    else{//한번도 안겹쳤으면 원래 좌표 저장
                        tmp[labelCnt].boundingRect.x = labels[i].boundingRect.x;
                        tmp[labelCnt].boundingRect.y = labels[i].boundingRect.y;
                        tmp[labelCnt].boundingRect.width = labels[i].boundingRect.width;
                        tmp[labelCnt].boundingRect.height = labels[i].boundingRect.height;
                        tmp[labelCnt].center.x = labels[i].center.x;
                        tmp[labelCnt++].center.y = labels[i].center.y;
                    }
                }
                continue;
            }
            //두 라벨이 겹치는 경우
            rectArr[y][x++= left2; rectArr[y][x++= right2; rectArr[y][x++= top2; rectArr[y][x++= bottom2; y++; x=0;
            rectArr[y][x++= left1; rectArr[y][x++= right1; rectArr[y][x++= top1; rectArr[y][x++= bottom1; y++;
            MK_maxNmin(left1, left2, right1, right2, &right1, &left1);
            MK_maxNmin(top1, top2, bottom1, bottom2, &bottom1, &top1);
            right3 = right1; left3 = left1; bottom3 = bottom1; top3 = top1; //최대 좌표 저장
            intersectionCnt ++//겹친 횟수 증가
        
            if(j == labelNum-1){ //마지막 회차, 적어도 한번 이상 겹쳤으면 최대영역 좌표 저장
                tmp[labelCnt].boundingRect.x = left1;
                tmp[labelCnt].boundingRect.y = top1;
                tmp[labelCnt].boundingRect.width = right1 - left1+1;
                tmp[labelCnt].boundingRect.height = bottom1 - top1+1;
                tmp[labelCnt].center.x = left1 + (right1 - left1+1)/2;
                tmp[labelCnt++].center.y = top1 + (bottom1 - top1+1)/2;
                
            }
        }
    }
    //merge 된 라벨 다시 옮기기
    for(i=0; i < labelCnt; i++){
        dst[i] = tmp[i];
    }
    return labelCnt;
}
cs


저는 위로 5px, 아래로 5px 인접하는 직사각형을 합쳤습니다. 

인접하는 범위를 조절하려면 line 46을 수정하시면 됩니다.




사용된 구조체 입니다. 저는 openCV를 사용할 수 없기에ㅜ 만들어서 쓰고 있습니다.

아마 cv::Rect를 사용하셔도 될 것 같습니다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
typedef struct // 라벨
{
    MK_Point    center;                            // 중심점
    MK_Rect     boundingRect;                    // 박스
}MK_Label;
 
 
typedef struct MK_Point    
{
    int x;
    int y;
}MK_Point;
 
typedef struct MK_Rect     
{
    int width;
    int height;
    int x;
    int y;
}MK_Rect;
cs



위 코드는 아직 버그? 가 있는 것 같습니다ㅜ

output이 최대 직사각형이 나오긴 하지만..




혹시 간단한 버그 해결 방법을 아시는 분은...ㅎㅎ 

알려주시면 정말 감사하겠습니다.




'공부 > 알고리즘' 카테고리의 다른 글

[Graph similarity] 그래프 유사도 측정1  (0) 2021.01.18
백준 10266번  (0) 2019.06.26
[C++] 부분집합 경우의 수 구하기  (0) 2017.12.15
Comments