เชื่อมช่องว่าง


14

เมื่อกำหนดภาพขาวดำที่มีพื้นหลังสีขาวและชุดจุดสีดำให้ทาสีชุดพิกเซลสีขาวสีแดงเพื่อให้มีเส้นทางระหว่างพิกเซลสีดำแต่ละคู่

รายละเอียด

  • เส้นทางคือชุดของพิกเซลที่เชื่อมต่อ (การเชื่อมต่อ 8-Neighborhood) พิกเซลสีดำสามารถใช้เป็นส่วนหนึ่งของเส้นทาง เป้าหมายพยายามลดจำนวนพิกเซลสีแดงให้น้อยที่สุดภายใต้เงื่อนไขข้างต้นและแสดงภาพที่สอดคล้องกัน

  • คุณไม่ต้องไปหาทางแก้ปัญหาที่ดีที่สุด

  • วิธีแก้ปัญหาที่เลวร้ายที่สุดและในเวลาเดียวกันนั้นก็แค่ทาสีขาวพิกเซลสีแดงทั้งหมด

  • ตัวอย่าง (ขยายพิกเซลเพื่อให้มองเห็นได้):

รายละเอียด

  • รับภาพพิกเซล (ในรูปแบบที่เหมาะสม) ส่งคืนรูปภาพอื่นที่มีจุดเชื่อมต่อตามที่ระบุข้างต้นรวมทั้งจำนวนเต็มที่ระบุว่ามีการใช้พิกเซลสีแดงจำนวนเท่าใด
  • คะแนนเป็นผลคูณของ (1 + จำนวนพิกเซลสีแดง) สำหรับแต่ละการทดสอบ 14 ครั้ง
  • เป้าหมายคือการมีคะแนนต่ำสุด

Testcases

รายการทดสอบทั้ง 14 ชุดแสดงอยู่ด้านล่าง โปรแกรมไพ ธ อนเพื่อตรวจสอบการเชื่อมต่อของเอาต์พุตสามารถพบได้ที่นี่

Meta

ขอบคุณ @Veskah, @Falize, @ wizzwizz4 และ @trichoplax สำหรับคำแนะนำต่างๆ


1
ความท้าทายที่ดี; ฉันชอบคนที่มีรูปแบบการให้คะแนนที่แตกต่างและสร้างสรรค์ ฉันถือว่าโปรแกรมจำเป็นต้องทำงานกับอิมเมจโดยพลการไม่ใช่ตัวอย่างเฉพาะ 14 ข้อเหล่านี้ใช่ไหม ถ้าเป็นเช่นนั้นเราสามารถสมมติขนาดสูงสุดที่เหมาะสมเช่น 512x512 ต่อภาพ Mona Lisa หรือ 1024x1024 ได้หรือไม่
BradC

ขอบคุณสำหรับความคิดเห็น! ใช่คุณสามารถสันนิษฐานได้ว่าขนาดสูงสุด (ขนาดที่เล็กที่สุดถ้าจำเป็น) ตราบใดที่สามารถประมวลผล 14 ตัวอย่างทั้งหมดได้
ข้อผิดพลาด

ฉันจะแปลง png เป็น ascii หรือ json ได้อย่างไรหรืออย่างอื่นแยกวิเคราะห์ได้ง่าย
ngn

คุณต้องคำนวณคะแนนของคุณเองหรือไม่? โปรแกรมที่พยายามรวมพิกเซลสีขาวที่เป็นไปได้ทั้งหมดเพื่อทาสีแดงและดูว่าเซตย่อยใดมีพิกเซลสีแดงที่น้อยที่สุดในขณะที่เชื่อมต่อพิกเซลสีดำทั้งหมดจะมีคะแนนที่ดีที่สุด แต่มันช้ามากจนใช้เวลานานกว่าตลอดชีวิต ของจักรวาลเพื่อคำนวณคะแนนนั้น
Leo Tenenbaum

1
@ngn เปิดใน GIMP บันทึกเป็นรูปแบบ netpbm
wizzwizz4

คำตอบ:


7

C, คะแนน 2.397x10 ^ 38

ผู้ชายคนนี้ใช้เวลาทำนานเกินไปน่าจะเกิดจากการเลือกใช้ภาษาของฉัน ฉันได้อัลกอริทึมทำงานค่อนข้างเร็ว แต่พบปัญหามากมายเกี่ยวกับการจัดสรรหน่วยความจำ (ไม่สามารถทำซ้ำได้ฟรีเนื่องจากกองล้นล้นขนาดรั่วมีขนาดใหญ่)

ยังคง! มันเป็นอีกทางเลือกหนึ่งในทุกกรณีการทดสอบและแม้กระทั่งวิธีที่ดีที่สุดก็สามารถเข้าใกล้หรือดีที่สุดได้ตลอดเวลา

อย่างไรก็ตามนี่คือรหัส:

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>

#define WHITE 'W'
#define BLACK 'B'
#define RED   'R'


typedef struct image {
    int w, h;
    char* buf;
} image;

typedef struct point {
    int x, y;
    struct point *next;
    struct point *parent;
} point;

typedef struct shape {
    point* first_point;
    point* last_point;

    struct shape* next_shape;
} shape;


typedef struct storage {
    point* points;
    size_t points_size;
    size_t points_index;

    shape* shapes;
    size_t shapes_size;
    size_t shapes_index;
} storage;

char getpx(image* img, int x, int y) {
    if (0>x || x>=img->w || 0>y || y>=img->h) {
        return WHITE;
    } else {
        return img->buf[y*img->w+x];
    }
}

storage* create_storage(int w, int h) {
    storage* ps = (storage*)malloc(sizeof(storage));

    ps->points_size = 8*w*h;
    ps->points = (point*)calloc(ps->points_size, sizeof(point));
    ps->points_index = 0;

    ps->shapes_size = 2*w*h;
    ps->shapes = (shape*)calloc(ps->shapes_size, sizeof(shape));
    ps->shapes_index = 0;

    return ps;
}

void free_storage(storage* ps) {
    if (ps != NULL) {
        if (ps->points != NULL) {
            free(ps->points);
            ps->points = NULL;
        }
        if (ps->shapes != NULL) {
            free(ps->shapes);
            ps->shapes = NULL;
        }
        free(ps);
    }
}


point* alloc_point(storage* ps) {
    if (ps->points_index == ps->points_size) {
        printf("WHOAH THERE BUDDY SLOW DOWN\n");
        /*// double the size of the buffer
        point* new_buffer = (point*)malloc(ps->points_size*2*sizeof(point));
        // need to change all existing pointers to point to new buffer
        long long int pointer_offset = (long long int)new_buffer - (long long int)ps->points;
        for (size_t i=0; i<ps->points_index; i++) {
            new_buffer[i] = ps->points[i];
            if (new_buffer[i].next != NULL) {
                new_buffer[i].next += pointer_offset;
            }
            if (new_buffer[i].parent != NULL) {
                new_buffer[i].parent += pointer_offset;
            }
        }

        for(size_t i=0; i<ps->shapes_index; i++) {
            if (ps->shapes[i].first_point != NULL) {
                ps->shapes[i].first_point += pointer_offset;
            }
            if (ps->shapes[i].last_point != NULL) {
                ps->shapes[i].last_point += pointer_offset;
            }
        }

        free(ps->points);
        ps->points = new_buffer;
        ps->points_size = ps->points_size * 2;*/
    }
    point* out = &(ps->points[ps->points_index]);
    ps->points_index += 1;
    return out;
}

shape* alloc_shape(storage* ps) {
    /*if (ps->shapes_index == ps->shapes_size) {
        // double the size of the buffer
        shape* new_buffer = (shape*)malloc(ps->shapes_size*2*sizeof(shape));
        long long int pointer_offset = (long long int)new_buffer - (long long int)ps->shapes;
        for (size_t i=0; i<ps->shapes_index; i++) {
            new_buffer[i] = ps->shapes[i];
            if (new_buffer[i].next_shape != NULL) {
                new_buffer[i].next_shape += pointer_offset;
            }
        }
        free(ps->shapes);
        ps->shapes = new_buffer;
        ps->shapes_size = ps->shapes_size * 2;
    }*/
    shape* out = &(ps->shapes[ps->shapes_index]);
    ps->shapes_index += 1;
    return out;
}

shape floodfill_shape(image* img, storage* ps, int x, int y, char* buf) {
    // not using point allocator for exploration stack b/c that will overflow it

    point* stack = (point*)malloc(sizeof(point));
    stack->x = x;
    stack->y = y;
    stack->next = NULL;
    stack->parent = NULL;

    point* explored = NULL;
    point* first_explored;
    point* next_explored;

    while (stack != NULL) {
        int sx = stack->x;
        int sy = stack->y;
        point* prev_head = stack;
        stack = stack->next;
        free(prev_head);

        buf[sx+sy*img->w] = 1; // mark as explored

        // add point to shape
        next_explored = alloc_point(ps);
        next_explored->x = sx;
        next_explored->y = sy;
        next_explored->next = NULL;
        next_explored->parent = NULL;

        if (explored != NULL) {
            explored->next = next_explored;
        } else {
            first_explored = next_explored;
        }
        explored = next_explored;

        for (int dy=-1; dy<2; dy++) {
        for (int dx=-1; dx<2; dx++) {
            if (dy != 0 || dx != 0) {
                int nx = sx+dx;
                int ny = sy+dy;
                if (getpx(img, nx, ny) == WHITE || buf[nx+ny*img->w]) {
                    // skip adding point to fringe
                } else {
                    // push point to top of stack
                    point* new_point = (point*)malloc(sizeof(point));
                    new_point->x = nx;
                    new_point->y = ny;
                    new_point->next = stack;
                    new_point->parent = NULL;

                    stack = new_point;
                } 
            }
        }
        }
    }

    /*if (getpx(img, x, y) == WHITE || buf[x+y*img->w]) {
        return (shape){NULL, NULL, NULL};
    } else {
        buf[x+y*img->w] = 1;

        shape e  = floodfill_shape(img, ps, x+1, y,   buf);
        shape ne = floodfill_shape(img, ps, x+1, y+1, buf);
        shape n  = floodfill_shape(img, ps, x,   y+1, buf);
        shape nw = floodfill_shape(img, ps, x-1, y+1, buf);
        shape w  = floodfill_shape(img, ps, x-1, y,   buf);
        shape sw = floodfill_shape(img, ps, x-1, y-1, buf);
        shape s  = floodfill_shape(img, ps, x,   y-1, buf);
        shape se = floodfill_shape(img, ps, x+1, y-1, buf);

        point *p = alloc_point(ps);
        p->x = x;
        p->y = y;
        p->next = NULL;
        p->parent = NULL;

        shape o = (shape){p, p, NULL};
        if (e.first_point != NULL) {
            o.last_point->next = e.first_point;
            o.last_point = e.last_point;
        }
        if (ne.first_point != NULL) {
            o.last_point->next = ne.first_point;
            o.last_point = ne.last_point;
        }
        if (n.first_point != NULL) {
            o.last_point->next = n.first_point;
            o.last_point = n.last_point;
        }
        if (nw.first_point != NULL) {
            o.last_point->next = nw.first_point;
            o.last_point = nw.last_point;
        }
        if (w.first_point != NULL) {
            o.last_point->next = w.first_point;
            o.last_point = w.last_point;
        }
        if (sw.first_point != NULL) {
            o.last_point->next = sw.first_point;
            o.last_point = sw.last_point;
        }
        if (s.first_point != NULL) {
            o.last_point->next = s.first_point;
            o.last_point = s.last_point;
        }
        if (se.first_point != NULL) {
            o.last_point->next = se.first_point;
            o.last_point = se.last_point;
        }

        return o;
    }*/

    shape out = {first_explored, explored, NULL};

    return out;
}

shape* create_shapes(image* img, storage* ps) {
    char* added_buffer = (char*)calloc(img->w*img->h, sizeof(char));
    shape* first_shape = NULL;
    shape* last_shape = NULL;
    int num_shapes = 0;
    for (int y=0; y<img->h; y++) {
        for (int x=0; x<img->w; x++) {
            if (getpx(img, x, y) != WHITE && !(added_buffer[x+y*img->w])) {
                shape* alloced_shape = alloc_shape(ps);
                *alloced_shape = floodfill_shape(img, ps, x, y, added_buffer);

                if (first_shape == NULL) {
                    first_shape = alloced_shape;
                    last_shape = alloced_shape;
                } else if (last_shape != NULL) {
                    last_shape->next_shape = alloced_shape;
                    last_shape = alloced_shape;
                }

                num_shapes++;
            }
        }
    }

    free(added_buffer);

    return first_shape;
}

void populate_buf(image* img, shape* s, char* buf) {
    point* p = s->first_point;

    while (p != NULL) {
        buf[p->x+p->y*img->w] = 1;
        p = p->next;
    }
}

bool expand_frontier(image* img, storage* ps, shape* prev_frontier, shape* next_frontier, char* buf) {
    point* p = prev_frontier->first_point;
    point* n = NULL;

    bool found = false;

    size_t starting_points_index = ps->points_index;

    while (p != NULL) {
        for (int dy=-1; dy<2; dy++) {
        for (int dx=-1; dx<2; dx++) {
            if (dy != 0 || dx != 0) {
                int nx = p->x+dx;
                int ny = p->y+dy;
                if ((0<=nx && nx<img->w && 0<=ny && ny<img->h) // in bounds
                        && !buf[nx+ny*img->w]) {               // not searched yet
                    buf[nx+ny*img->w] = 1;
                    if (getpx(img, nx, ny) != WHITE) {
                        // found a new shape!
                        ps->points_index = starting_points_index;
                        n = alloc_point(ps);
                        n->x = nx;
                        n->y = ny;
                        n->next = NULL;
                        n->parent = p;
                        found = true;
                        goto __expand_frontier_fullbreak;
                    } else {
                        // need to search more
                        point* f = alloc_point(ps);
                        f->x = nx;
                        f->y = ny;
                        f->next = n;
                        f->parent = p;
                        n = f;
                    }
                }
            }
        }}

        p = p->next;
    }
__expand_frontier_fullbreak:
    p = NULL;
    point* last_n = n;
    while (last_n->next != NULL) {
        last_n = last_n->next;
    }

    next_frontier->first_point = n;
    next_frontier->last_point = last_n;

    return found;
}

void color_from_frontier(image* img, point* frontier_point) {
    point* p = frontier_point->parent;

    while (p->parent != NULL) { // if everything else is right,
                                // a frontier point should come in a chain of at least 3
                                // (f point (B) -> point to color (W) -> point in shape (B) -> NULL)
        img->buf[p->x+p->y*img->w] = RED;
        p = p->parent;
    }
}

int main(int argc, char** argv) {
    if (argc < 3) {
        printf("Error: first argument must be filename to load, second argument filename to save to.\n");
        return 1;
    }

    char* fname = argv[1];
    FILE* fp = fopen(fname, "r");

    if (fp == NULL) {
        printf("Error opening file \"%s\"\n", fname);
        return 1;
    }

    int w, h;
    w = 0;
    h = 0;
    fscanf(fp, "%d %d\n", &w, &h);

    if (w==0 || h==0) {
        printf("Error: invalid width/height specified\n");
        return 1;
    }

    char* buf = (char*)malloc(sizeof(char)*w*h+1);
    fgets(buf, w*h+1, fp);
    fclose(fp);

    image img = (image){w, h, buf};

    int nshapes = 0;
    storage* ps = create_storage(w, h);

    while (nshapes != 1) {
        // main loop, do processing step until one shape left
        ps->points_index = 0;
        ps->shapes_index = 0;

        shape* head = create_shapes(&img, ps);
        nshapes = 0;
        shape* pt = head;
        while (pt != NULL) {
            pt = pt->next_shape;
            nshapes++;
        }
        if (nshapes % 1024 == 0) {
            printf("shapes left: %d\n", nshapes);
        }
        if (nshapes == 1) {
            goto __main_task_complete;
        }


        shape* frontier = alloc_shape(ps);
        // making a copy so we can safely free later
        point* p = head->first_point;
        point* ffp = NULL;
        point* flp = NULL;
        while (p != NULL) {
            if (ffp == NULL) {
                ffp = alloc_point(ps);
                ffp->x = p->x;
                ffp->y = p->y;
                ffp->next = NULL;
                ffp->parent = NULL;
                flp = ffp;
            } else {
                point* fnp = alloc_point(ps);
                fnp->x = p->x;
                fnp->y = p->y;
                fnp->next = NULL;
                fnp->parent = NULL;

                flp->next = fnp;
                flp = fnp;
            }

            p = p->next;
        }
        frontier->first_point = ffp;
        frontier->last_point = flp;
        frontier->next_shape = NULL;

        char* visited_buf = (char*)calloc(img.w*img.h+1, sizeof(char));
        populate_buf(&img, frontier, visited_buf);

        shape* new_frontier = alloc_shape(ps);
        new_frontier->first_point = NULL;
        new_frontier->last_point = NULL;
        new_frontier->next_shape = NULL;

        while (!expand_frontier(&img, ps, frontier, new_frontier, visited_buf)) {
            frontier->first_point = new_frontier->first_point;
            frontier->last_point = new_frontier->last_point;
            new_frontier->next_shape = frontier;
        }

        free(visited_buf);
        color_from_frontier(&img, new_frontier->first_point);
__main_task_complete:
        img = img;
    }

    free_storage(ps);

    char* outfname = argv[2];
    fp = fopen(outfname, "w");

    if (fp == NULL) {
        printf("Error opening file \"%s\"\n", outfname);
        return 1;
    }

    fprintf(fp, "%d %d\n", img.w, img.h);
    fprintf(fp, "%s", img.buf);

    free(img.buf);

    fclose(fp);

    return 0;
}

ทดสอบกับ: Arch Linux, GCC 9.1.0, -O3

รหัสนี้ใช้อินพุต / เอาต์พุตในไฟล์ที่กำหนดเองที่ฉันเรียกว่า "cppm" (เพราะมันเป็นรูปแบบ PPM แบบคลาสสิกแบบย่อ) สคริปต์ python ในการแปลงเป็น / จากด้านล่าง:

from PIL import Image

BLACK='B'
WHITE='W'
RED  ='R'


def image_to_cppm(infname, outfname):
    outfile = open(outfname, 'w')
    im = Image.open(infname)

    w, h = im.width, im.height
    outfile.write(f"{w} {h}\n")
    for y in range(h):
        for x in range(w):
            r, g, b, *_ = im.getpixel((x, y))
            if r==0 and g==0 and b==0:
                outfile.write(BLACK)
            elif g==0 and b==0:
                outfile.write(RED)
            else:
                outfile.write(WHITE)
    outfile.write("\n")
    outfile.close()
    im.close()

def cppm_to_image(infname, outfname):
    infile = open(infname, 'r')

    w, h = infile.readline().split(" ")
    w, h = int(w), int(h)

    im = Image.new('RGB', (w, h), color=(255, 255, 255))

    for y in range(h):
        for x in range(w):
            c = infile.read(1)
            if c==BLACK:
                im.putpixel((x,y), (0, 0, 0))
            elif c==RED:
                im.putpixel((x,y), (255, 0, 0))

    infile.close()
    im.save(outfname)
    im.close()


if __name__ == "__main__":
    import sys
    if len(sys.argv) < 3:
        print("Error: must provide 2 files to convert, first is from, second is to")

    infname = sys.argv[1]
    outfname = sys.argv[2]

    if not infname.endswith("cppm") and outfname.endswith("cppm"):
        image_to_cppm(infname, outfname)
    elif infname.endswith("cppm") and not outfname.endswith("cppm"):
        cppm_to_image(infname, outfname)
    else:
        print("didn't do anything, exactly one file must end with .cppm")

คำอธิบายอัลกอริทึม

อัลกอริทึมนี้ทำงานอย่างไรโดยเริ่มจากการค้นหารูปร่างที่เชื่อมต่อทั้งหมดในภาพรวมถึงพิกเซลสีแดง จากนั้นจะใช้พิกเซลแรกและขยายขอบเขตหนึ่งพิกเซลในแต่ละครั้งจนกว่าจะพบกับรูปร่างอื่น จากนั้นให้สีพิกเซลทั้งหมดจากการสัมผัสกับรูปร่างดั้งเดิม (ใช้ลิสต์ที่เชื่อมโยงซึ่งทำไว้ตลอดเส้นทางเพื่อติดตาม) ในที่สุดมันจะทำซ้ำกระบวนการค้นหารูปร่างใหม่ทั้งหมดที่สร้างขึ้นจนกว่าจะมีเพียงหนึ่งรูปร่างที่เหลือ

แกลเลอรี่ภาพ

ทดสอบ 1, 183 พิกเซล

testcase 1

ทดสอบ 2, 140 พิกเซล

testcase 2

ทดสอบ 3, 244 พิกเซล

testcase 3

ทดสอบ 4, 42 พิกเซล

testcase 4

ทดสอบ 5, 622 พิกเซล

testcase 5

ทดสอบ 6, 1 พิกเซล

testcase 6

ทดสอบ 7, 104 พิกเซล

testcase 7

ทดสอบ 8, 2286 พิกเซล

testcase 8

ทดสอบ 9, 22 พิกเซล

testcase 9

ทดสอบ 10, 31581 พิกเซล

testcase 10

ทดสอบ 11, 21421 พิกเซล

testcase 11

ทดสอบที่ 12, 5465 พิกเซล

testcase 12

ทดสอบ 13, 4679 พิกเซล

testcase 13

ทดสอบ 14, 7362 พิกเซล

testcase 14


2
เยี่ยมมาก! ดูเหมือนว่าจะมีประสิทธิภาพมากถึงแม้ว่าฉันจะนึกภาพไม่กี่รูปทรงด้วยวิธีแก้ปัญหาที่ดีที่สุดเล็กน้อย: Testcase 3 (4 จุดในรูปสี่เหลี่ยม) ตัวอย่างเช่นฉัน (ด้วยตนเอง) มีระดับต่ำถึง 175 (สีแดง X) ไม่แน่ใจว่าเป็นอย่างไร ฉันจะบังคับผ่านอัลกอริทึม
BradC

6

Python, 2.62 * 10 ^ 40

อัลกอริทึมนี้เพิ่งเติมน้ำท่วม (BFS) เครื่องบินเริ่มจากส่วนสีดำของภาพซึ่งสำหรับแต่ละพิกเซลใหม่เราบันทึกส่วนสีดำที่มันถูกน้ำท่วม ทันทีที่เรามีพิกเซลข้างเคียงสองชิ้นที่มีส่วนสีดำแตกต่างกันในฐานะบรรพบุรุษเราจะรวมส่วนที่เป็นสีดำทั้งสองนี้เข้าด้วยกันโดยเข้าร่วมผ่านบรรพบุรุษของทั้งสองประเทศที่เราเพิ่งพบ ในทางทฤษฎีแล้วสิ่งนี้สามารถนำไปใช้ได้O(#pixels)แต่เพื่อให้ปริมาณของรหัสอยู่ในระดับที่ยอมรับได้การดำเนินการนี้แย่กว่าเล็กน้อย

เอาท์พุต

ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่

import numpy as np
from scipy import ndimage
import imageio
from collections import deque

# path to your image
for k in range(1, 15):
    fname=str(k).zfill(2) +'.png'
    print("processing ", fname)

    # load image
    img = imageio.imread("./images/"+fname, pilmode="RGB")
    print(img.shape)

    # determine non_white part
    white = np.logical_and(np.logical_and(img[:,:,0] == 255, img[:,:,1] == 255), img[:,:,2] == 255)
    non_white = np.logical_not(white)

    # find connected components of non-white part
    neighbourhood = np.ones((3,3))
    labeled, nr_objects = ndimage.label(non_white, neighbourhood)

    # print result
    print("number of separate objects is {}".format(nr_objects))

    # start flood filling algorithm
    ind = np.nonzero(labeled)
    front = deque(zip(ind[0],ind[1]))

    membership = np.copy(labeled)
    is_merge_point = np.zeros_like(labeled) > 0
    parent = np.zeros((2,) + labeled.shape) #find ancestor of each pixel
    is_seed = labeled > 0
    size_i, size_j = labeled.shape
    # flood from every seed
    while front: #while we have unexplored pixels
        point = front.popleft()
        # check neighbours:
        for (di,dj) in [(-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1)]:
            current = membership[point[0], point[1]]
            new_i, new_j = point[0]+di, point[1]+dj
            if 0 <= new_i < size_i and 0 <= new_j < size_j:
                value = membership[new_i, new_j]
                if value == 0:
                    membership[new_i, new_j] = current
                    front.append((new_i, new_j))
                    parent[:, new_i, new_j] = point
                elif value != current: #MERGE!
                    is_merge_point[point[0], point[1]] = True
                    is_merge_point[new_i, new_j] = True
                    membership[np.logical_or(membership == value, membership == current)] = min(value, current)

    # trace back from every merger
    ind = np.nonzero(is_merge_point)
    merge_points = deque(zip(ind[0].astype(np.int),ind[1].astype(np.int)))
    for point in merge_points:
        next_p = point
        while not is_seed[next_p[0], next_p[1]]:
            is_merge_point[next_p[0], next_p[1]] = True
            next_p = parent[:, next_p[0], next_p[1]].astype(np.int)

    # add red points:
    img_backup = np.copy(img)
    img[:,:,0][is_merge_point] = 255 * img_backup[:,:,0]
    img[:,:,1][is_merge_point] = 0   * img_backup[:,:,1]
    img[:,:,2][is_merge_point] = 0   * img_backup[:,:,2]

    #compute number of new points
    n_red_points = (img[:,:,0] != img[:,:,1]).sum()
    print("#red points:", n_red_points)

    # plot: each component should have separate color
    imageio.imwrite("./out_images/"+fname, np.array(img))

คะแนน

(1+183)*(1+142)*(1+244)*(1+42)*(1+1382)*(1+2)*(1+104)*(1+7936)*(1+26)*(1+38562)*(1+42956)*(1+6939)*(1+8882)*(1+9916)
= 26208700066468930789809050445560539404000
= 2.62 * 10^40

- นี่ฉันเชื่อว่าเหมาะสมที่สุด ทำได้ดีมาก - โอเคนี่มันไม่เหมาะ ฉันไม่เข้าใจว่าทำไมไม่
wizzwizz4

@ wizzwizz4 ดูกรณีง่าย ๆ ของมุมทั้งสี่ของสี่เหลี่ยมจัตุรัส: วิธีแก้ปัญหาที่ดีที่สุดจะเป็น X ในทางทฤษฎีอัลกอริทึมของฉันสามารถหาวิธีแก้ปัญหานี้ได้ มีโอกาสมากขึ้นที่จะพบทางออกที่มีสามเส้นทางแต่ละจุดเชื่อมต่อสองจุด
ข้อบกพร่อง

@ wizzwizz4 เอาละลองดูตัวอย่างข้อความจากวิกิพีเดียและคุณจะเห็นสถานที่เล็ก ๆ มากมายที่เส้นทางเชื่อมต่อที่แตกต่างกันจะช่วยประหยัดพิกเซลสีแดงหรือสองตัว พวกเขาจะเพิ่มขึ้น
BradC

แต่ตอนนี้ดูเหมือนว่าสบู่ฟองบนหมุดซึ่งเป็นวิธีการแก้ปัญหาที่ถูกต้องกับปัญหาต้นไม้ทิ
wizzwizz4

1
@ wizzwizz4 ความแตกต่างแล้วจะต้องเป็นไปได้ว่าเราไม่ได้เชื่อมต่อจุดที่เรากำลังเชื่อมต่อชุดของจุดดังนั้นเราจะต้องไม่ถูกตัดสินใจที่จุดในแต่ละชุดการเชื่อมต่อในวิธีที่ดีที่สุด ขยายเข้าไปในข้อความตัวอย่างอีกครั้งการปรับปรุงที่คุณสามารถดูส่วนใหญ่จะต้องทำด้วยซึ่งชิ้นส่วนของแต่ละรูปมีการเชื่อมต่อ
BradC

5

Python 3: 1.7x10 ^ 42 1.5x10 ^ 41

การใช้Pillow, และnumpyscipy

ภาพจะถือว่าอยู่ในimagesโฟลเดอร์ที่อยู่ในไดเรกทอรีเดียวกันกับสคริปต์

คำเตือน : มันใช้เวลานานในการประมวลผลภาพทั้งหมด

รหัส

import sys
import os

from PIL import Image
import numpy as np
import scipy.ndimage


def obtain_groups(image, threshold, structuring_el):
    """
    Obtain isles of unconnected pixels via a threshold on the R channel
    """
    image_logical = (image[:, :, 1] < threshold).astype(np.int)
    return scipy.ndimage.measurements.label(image_logical, structure=structuring_el)


def swap_colors(image, original_color, new_color):
    """
    Swap all the pixels of a specific color by another color 
    """
    r1, g1, b1 = original_color  # RGB value to be replaced
    r2, g2, b2 = new_color  # New RGB value
    red, green, blue = image[:, :, 0], image[:, :, 1], image[:, :, 2]
    mask = (red == r1) & (green == g1) & (blue == b1)
    image[:, :, :3][mask] = [r2, g2, b2]
    return image


def main(image_path=None):
    images = os.listdir("images")
    f = open("results.txt", "w")

    if image_path is not None:
        images = [image_path]

    for image_name in images:
        im = Image.open("images/"+image_name).convert("RGBA")
        image = np.array(im)

        image = swap_colors(image, (255, 255, 255), (255, 0, 0))

        # create structuring element to determine unconnected groups of pixels in image
        s = scipy.ndimage.morphology.generate_binary_structure(2, 2)

        for i in np.ndindex(image.shape[:2]):
            # skip black pixels
            if sum(image[i[0], i[1]]) == 255:
                continue
            image[i[0], i[1]] = [255, 255, 255, 255]
            # label the different groups, considering diagonal connections as valid
            groups, num_groups = obtain_groups(image, 255, s)
            if num_groups != 1:
                image[i[0], i[1]] = [255, 0, 0, 255]
            # Show percentage
            print((i[1] + i[0]*im.size[0])/(im.size[0]*im.size[1]))

        # Number of red pixels
        red_p = 0
        for i in np.ndindex(image.shape[:2]):
            j = (im.size[1] - i[0] - 1, im.size[0] - i[1] - 1)
            # skip black and white pixels
            if sum(image[j[0], j[1]]) == 255 or sum(image[j[0], j[1]]) == 255*4:
                continue
            image[j[0], j[1]] = [255, 255, 255, 255]
            # label the different groups, considering diagonal connections as valid
            groups, num_groups = obtain_groups(image, 255, s)
            if num_groups != 1:
                image[j[0], j[1]] = [255, 0, 0, 255]
            # Show percentage
            print((j[1] + j[0]*im.size[0])/(im.size[0]*im.size[1]))
            red_p += (sum(image[j[0], j[1]]) == 255*2)

        print(red_p)
        f.write("r_"+image_name+": "+str(red_p)+"\n")

        im = Image.fromarray(image)
        im.show()
        im.save("r_"+image_name)
    f.close()


if __name__ == "__main__":
    if len(sys.argv) == 2:
        main(sys.argv[1])
    else:
        main()

คำอธิบาย

วิธีแก้ปัญหาเล็กน้อย เราเริ่มต้นด้วยการเปลี่ยนสีของพิกเซลสีขาวทั้งหมดในภาพเป็นสีแดง ด้วยการทำเช่นนี้จึงรับประกันได้ว่าองค์ประกอบทั้งหมด (เกาะใด ๆ ของพิกเซลดำ) เชื่อมต่ออยู่

จากนั้นเราวนพิกเซลทั้งหมดในภาพโดยเริ่มจากมุมบนซ้ายและเลื่อนไปทางขวาและลง สำหรับทุกพิกเซลสีแดงเราพบว่าเราเปลี่ยนสีเป็นสีขาว หากหลังจากการเปลี่ยนแปลงของสีนี้ยังมีเพียงหนึ่งองค์ประกอบ (ตอนนี้องค์ประกอบที่มีพิกเซลสีดำและสีแดง) เราจะปล่อยให้พิกเซลสีขาวและย้ายไปยังพิกเซลต่อไป อย่างไรก็ตามหากหลังจากเปลี่ยนสีจากสีแดงเป็นสีขาวจำนวนขององค์ประกอบที่ใหญ่กว่าหนึ่งเราจะปล่อยให้พิกเซลสีแดงและย้ายไปยังพิกเซลต่อไป

ปรับปรุง

ตามที่สามารถเห็นได้ (และคาดว่า) การเชื่อมต่อที่ได้รับจากการใช้วิธีการนี้จะแสดงรูปแบบปกติและในบางกรณีเช่นในภาพที่ 6 และ 11 มีพิกเซลสีแดงที่ไม่จำเป็น

พิกเซลสีแดงพิเศษนี้สามารถลบออกได้อย่างง่ายดายโดยวนซ้ำอีกครั้งบนภาพและดำเนินการตามที่อธิบายไว้ข้างต้น แต่จากมุมล่างขวาไปยังมุมบนซ้าย รหัสผ่านที่สองนี้เร็วขึ้นมากเนื่องจากจำนวนพิกเซลสีแดงที่ต้องตรวจสอบ

ผล

ภาพที่มีการแก้ไขหลังจากผ่านรอบที่สองจะถูกแสดงสองครั้งเพื่อแสดงความแตกต่าง

18825

จำนวนพิกเซลสีแดง: 18825

334

จำนวนพิกเซลสีแดง: 334

1352

จำนวนพิกเซลสีแดง: 1352

20214

จำนวนพิกเซลสีแดง: 20214

ป้อนคำอธิบายรูปภาพที่นี่

จำนวนพิกเซลสีแดง: 47268

63 ป้อนคำอธิบายรูปภาพที่นี่

จำนวนพิกเซลสีแดง: 63 27

17889

จำนวนพิกเซลสีแดง: 17889

259

จำนวนพิกเซลสีแดง: 259

6746

จำนวนพิกเซลสีแดง: 6746

586

จำนวนพิกเซลสีแดง: 586

9 ป้อนคำอธิบายรูปภาพที่นี่

จำนวนพิกเซลสีแดง: 9 1

126

จำนวนพิกเซลสีแดง: 126

212

จำนวนพิกเซลสีแดง: 212

683

จำนวนพิกเซลสีแดง: 683

การคำนวณคะแนน:

(1 + 6746) * (1 + 126) * (1 + 259) * (1 + 17889) * (1 + 334) * (1 + 586) * (1 + 18825) * (1 + 9) * (1 + 9) +683) * (1 + 1352) * (1 + 20214) * (1 + 212) * (1 + 63) * (1 + 47268) = 1778700054505858720992088713763655500800000 ~ 1.7x10 ^ 42

อัปเดตการคำนวณคะแนนหลังจากเพิ่มรหัสผ่านที่สอง:

(1+ 18825) * (1+ 1352) * (1+ 20214) * (1+ 47268) * (1+ 27) * (1+ 17889) * (1+ 6746) * (1+ 586) * (1+ 586) * (1 + 1) * (1+ 126) * (1+ 212) * (1+ 334) * (1 + 259) * (1 + 683) = 155636254769262638086807762454319856320000 ~ 1.5x10 ^ 41


ทำได้ดีมาก ดูเหมือนว่าเราอาจจำเป็นต้องให้คะแนนสิ่งนี้ในเอกสารทางวิทยาศาสตร์: 1.7x10 ^ 42
BradC
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.