Translate

one-dimensional array for Image and Image3D in C

Recently, I watched an interesting video about the Hilbert curve on YouTube, which can process higher-dimensional data using one-dimensional data. I had some interesting ideas and decided to implement them. However, due to the large number of point operations and complex calculations involved, Python's performance was not sufficient, so I decided to use C, which was also a good opportunity to learn the basics of the C language.
C language also has OOP and is easy to use. I first asked AI to create a class called Image that uses a one-dimensional array to store data. The implementation is simple as long as the logic for getPoint and setPoint is correct. Then, we can use these methods to perform various image operations, which is no different from a regular Image.
After creating the class, the most important step is verification. By observing the crude output results, we can verify whether various functions meet our expectations. I didn't find any problems in the basic implementation, so I asked AI to use a similar logic to create a class called Image3D, which also uses a one-dimensional data storage for three-dimensional information. As long as the logic for getPoint and setPoint is correct, all other methods are also correct. I only added simple methods like drawLine and drawCube, which also produced correct results in the output. I plan to implement other functions based on my own ideas, so I didn't ask AI to create them.
Higher-dimensional complexity is too high, and the required resources increase geometrically, which is beyond imagination. I don't plan to study it for now, and I intend to operate three-dimensional data mainly through accessing slices.
Code and testing :


#include <stdio.h>

#include <stdlib.h>

#include <math.h>

#include <cstdio>

#include <cstring>

#include <cstdlib>

#include <cmath>

#include <algorithm>

#include <cstdlib> 

struct Image {

private:

    int width;

    int height;

    int* array;

    int getIndex(int x, int y) {

        return y * width + x;

    }

public:

    Image(int w, int h) {

        width = w;

        height = h;

        array = (int*)malloc(width * height * sizeof(int));

        if (array == NULL) {

            fprintf(stderr, "Failed to allocate memory for array\n");

            exit(EXIT_FAILURE);

        }

        for (int i = 0; i < width * height; i++) {

            array[i] = 0;

        }

    }

    ~Image() {

        free(array);

    }

    int getWidth() {

        return width;

    }

    int getHeight() {

        return height;

    }

    int getPoint(int x, int y) {

        if (x < 0 || x >= width || y < 0 || y >= height) {

            fprintf(stderr, "Invalid point: (%d, %d)\n", x, y);

            exit(EXIT_FAILURE);

        }

        return array[getIndex(x, y)];

    }

    void setPoint(int x, int y, int color) {

        if (x < 0 || x >= width || y < 0 || y >= height) {

            fprintf(stderr, "Invalid point: (%d, %d)\n", x, y);

            return;

        }

        array[getIndex(x, y)] = color;

    }

    void setLine(int x0, int y0, int x1, int y1, int color) {

        int dx = abs(x1 - x0);

        int dy = abs(y1 - y0);

        int sx = x0 < x1 ? 1 : -1;

        int sy = y0 < y1 ? 1 : -1;

        int err = dx - dy;

        while (1) {

            setPoint(x0, y0, color);

            if (x0 == x1 && y0 == y1) {

                break;

            }

            int e2 = 2 * err;

            if (e2 > -dy) {

                err -= dy;

                x0 += sx;

            }

            if (e2 < dx) {

                err += dx;

                y0 += sy;

            }

        }

    }

void printMatrix() {

    printf("Converted two-dimensional matrix:\n");

    for (int i = 0; i < height; i++) {

        for (int j = 0; j < width; j++) {

            printf("%2d", array[getIndex(j, i)]);

        }

        printf("\n");

    }

}

    void printArray() {

        printf("Converted one-dimensional array:\n");

        for (int i = 0; i < height; i++) {

            for (int j = 0; j < width; j++) {

                printf("%2d", array[getIndex(j, i)]);

            }

        }

        printf("\n");

    }

    Image cropImage(int x, int y, int width, int height) {

        int i, j;

        Image croppedImage(width, height);

        for (i = y; i < y + height; i++) {

            for (j = x; j < x + width; j++) {

                croppedImage.setPoint(j - x, i - y, getPoint(j, i));

            }

        }

        return croppedImage;

    }

    void drawRectangle(int x, int y, int width, int height, int color) {

        int i, j;

        for (i = y; i < y + height; i++) {

            for (j = x; j < x + width; j++) {

                setPoint(j, i, color);

            }

        }

    }

    Image resizeImage(int width, int height) {

        int i, j;

        double horizontalRatio = (double)(getWidth() - 1) / (double)(width - 1);

        double verticalRatio = (double)(getHeight() - 1) / (double)(height - 1);

        Image resizedImage(width, height);

        for (i = 0; i < height; i++) {

            for (j = 0; j < width; j++) {

                resizedImage.setPoint(j, i,

                    getPoint(

                        (int)(j * horizontalRatio + 0.5),

                        (int)(i * verticalRatio + 0.5)

                    )

                );

            }

        }

        return resizedImage;

    }

    void drawCircle(int centerX, int centerY, int radius, int color) {

        int x, y, r2;

        r2 = radius * radius;

        for (x = -radius; x <= radius; x++) {

            y = (int)(sqrt(r2 - x * x) + 0.5);

            setPoint(centerX + x, centerY + y, color);

            setPoint(centerX + x, centerY - y, color);

        }

        for (y = -radius; y <= radius; y++) {

            x = (int)(sqrt(r2 - y * y) + 0.5);

            setPoint(centerX + x, centerY + y, color);

            setPoint(centerX - x, centerY + y, color);

        }

    }

};

struct Image3D {

private:

    int xLength;

    int yLength;

    int zLength;

    int* array;

    int getIndex(int x, int y, int z) {

        return z * xLength * yLength + y * xLength + x;

    }

public:

    Image3D(int xLen, int yLen, int zLen) {

        xLength = xLen;

        yLength = yLen;

        zLength = zLen;

        array = new int[xLength * yLength * zLength];

        memset(array, 0, xLength * yLength * zLength * sizeof(int));

    }

    ~Image3D() {

        delete[] array;

    }

    int getXLength() {

        return xLength;

    }

    int getYLength() {

        return yLength;

    }

    int getZLength() {

        return zLength;

    }

    int* getData() {

        return array;

    }

    int getPoint(int x, int y, int z) {

        if (x < 0 || x >= xLength || y < 0 || y >= yLength || z < 0 || z >= zLength) {

            fprintf(stderr, "Invalid point: (%d, %d, %d)\n", x, y, z);

            exit(EXIT_FAILURE);

        }

        return *(array + getIndex(x, y, z));

    }

    void setPoint(int x, int y, int z, int color) {

        if (x < 0 || x >= xLength || y < 0 || y >= yLength || z < 0 || z >= zLength) {

            fprintf(stderr, "Invalid point: (%d, %d, %d)\n", x, y, z);

            return;

        }

        *(array + getIndex(x, y, z)) = color;

    }

    void printMatrix() {

        printf("Converted three-dimensional matrix:\n");

        for (int z = 0; z < zLength; z++) {

            printf("z=%d\n", z);

            for (int y = 0; y < yLength; y++) {

                for (int x = 0; x < xLength; x++) {

                    printf("%2d ", *(array + getIndex(x, y, z)));

                }

                printf("\n");

            }

            printf("\n");

        }

    }

    void printArray() {

        printf("Converted one-dimensional array:\n");

        for (int i = 0; i < xLength * yLength * zLength; i++) {

            printf("%2d ", *(array + i));

        }

        printf("\n");

    }

    void drawLine(int x1, int y1, int z1, int x2, int y2, int z2, int color) {

    int dx = abs(x2 - x1);

    int dy = abs(y2 - y1);

    int dz = abs(z2 - z1);

    int sx = (x1 < x2) ? 1 : -1;

    int sy = (y1 < y2) ? 1 : -1;

    int sz = (z1 < z2) ? 1 : -1;

    int maxDimension = std::max(dx, std::max(dy, dz));

    if (maxDimension == 0) {

        setPoint(x1, y1, z1, color);

        return;

    }

    float stepX = dx / (float)maxDimension;

    float stepY = dy / (float)maxDimension;

    float stepZ = dz / (float)maxDimension;

    float x = x1;

    float y = y1;

    float z = z1;

    for (int i = 0; i <= maxDimension; i++) {

        setPoint(round(x), round(y), round(z), color);

        x += stepX * sx;

        y += stepY * sy;

        z += stepZ * sz;

    }

}

    void drawCube( int x1, int y1, int z1, int x2, int y2, int z2, int color) {

        drawLine(x1, y1, z1, x1, y1, z2, color);

        drawLine(x1, y1, z2, x2, y1, z2, color);

        drawLine( x2, y1, z2, x2, y1, z1, color);

        drawLine(x2, y1, z1, x1, y1, z1, color);

        drawLine(x1, y2, z1, x1, y2, z2, color);

        drawLine(x1, y2, z2, x2, y2, z2, color);

        drawLine(x2, y2, z2, x2, y2, z1, color);

        drawLine(x2, y2, z1, x1, y2, z1, color);

        drawLine(x1, y1, z1, x1, y2, z1, color);

        drawLine(x1, y1, z2, x1, y2, z2, color);

        drawLine(x2, y1, z2, x2, y2, z2, color);

        drawLine(x2, y1, z1, x2, y2, z1, color);

    }

};

int main() {

    int rows, cols;

    // Input the length and width of the image

    printf("Please enter the rows and cols of the image:\n");

    if (scanf("%d%d", &rows, &cols) != 2) {

        fprintf(stderr, "Invalid input for rows and cols\n");

        exit(EXIT_FAILURE);

    }

    // Create an Image object

    Image image(cols, rows); 

    image.printMatrix();

    // Perform some operations on the Image object

    image.setPoint(3, 5, 77);    // Set the value of point (3, 5) to 77

    

    image.setLine(0, 0, cols-1, rows-1, 99);    // Draw a line from (0, 0) to (cols-1, rows-1) with the value of 99

    

    image.setLine(0, cols - 1, rows - 1, 0, 33);    // Draw a line from (0, cols-1) to (rows-1, 0) with the value of 33

    

    image.drawCircle(6, 6, 6, 66);    // Draw a circle with the center at (6, 6), radius 6, and the value of 66

    

    image.drawRectangle(0, 0, 3, 3, 11);    // Draw a rectangle with width 3, height 3, starting at (0, 0) and the value of 11

    

    printf("Set the value of point (3, 5) to 77.\n a line from (0, 0) to (cols-1, rows-1) with the value of 99\nDraw a line from (0, cols-1) to (rows-1, 0) with the value of 33\nDraw a circle with the center at (6, 6), radius 6, and the value of 66\nDraw a rectangle with width 3, height 3, starting at (0, 0) and the value of 11\n");

    image.printMatrix();

   

    

    // Crop the image to a 6x6 region

    printf("Cropped Image object:\n");

    Image i2 = image.cropImage(0, 0, 6, 6);

    i2.printMatrix();

    i2.printArray();

    printf("Resized to 25x25:\n");

    Image i3 = image.resizeImage(25, 25);

    i3.printMatrix();

    i3.printArray();

    

    printf("Resized to 12x12:\n");

    Image i4 = image.resizeImage(12, 12);

    i4.printMatrix();

    i4.printArray();

    

    printf("Image3D 6x6x6:\ndrawLine(0,0,0,5,5,5,99);\ndrawCube(0,0,0,3,3,3,33);\n ");

    Image3D i5=Image3D(6,6,6);

    i5.drawLine(0,0,0,5,5,5,99);

    i5.drawCube(0,0,0,3,3,3,33);

    

    i5.printMatrix();

    i5.printArray();

    return 0;

}

The output .


Please enter the rows and cols of the image:

20

20

Converted two-dimensional matrix:

 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Set the value of point (3, 5) to 77.

 a line from (0, 0) to (cols-1, rows-1) with the value of 99

Draw a line from (0, cols-1) to (rows-1, 0) with the value of 33

Draw a circle with the center at (6, 6), radius 6, and the value of 66

Draw a rectangle with width 3, height 3, starting at (0, 0) and the value of 11

Converted two-dimensional matrix:

111111 06666666666 0 0 0 0 0 0 0 0 0 033

11111166 0 0 0 0 066 0 0 0 0 0 0 0 033 0

111111 0 0 0 0 0 0 066 0 0 0 0 0 033 0 0

 066 099 0 0 0 0 0 0 066 0 0 0 033 0 0 0

66 0 0 099 0 0 0 0 0 0 066 0 033 0 0 0 0

66 0 077 099 0 0 0 0 0 066 033 0 0 0 0 0

66 0 0 0 0 099 0 0 0 0 06633 0 0 0 0 0 0

66 0 0 0 0 0 099 0 0 0 066 0 0 0 0 0 0 0

66 0 0 0 0 0 0 099 0 03366 0 0 0 0 0 0 0

 066 0 0 0 0 0 0 0993366 0 0 0 0 0 0 0 0

 0 066 0 0 0 0 0 03366 0 0 0 0 0 0 0 0 0

 0 0 066 0 0 0 03366 099 0 0 0 0 0 0 0 0

 0 0 0 06666666666 0 0 099 0 0 0 0 0 0 0

 0 0 0 0 0 033 0 0 0 0 0 099 0 0 0 0 0 0

 0 0 0 0 033 0 0 0 0 0 0 0 099 0 0 0 0 0

 0 0 0 033 0 0 0 0 0 0 0 0 0 099 0 0 0 0

 0 0 033 0 0 0 0 0 0 0 0 0 0 0 099 0 0 0

 0 033 0 0 0 0 0 0 0 0 0 0 0 0 0 099 0 0

 033 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 099 0

33 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 099

Cropped Image object:

Converted two-dimensional matrix:

111111 06666

11111166 0 0

111111 0 0 0

 066 099 0 0

66 0 0 099 0

66 0 077 099

Converted one-dimensional array:

111111 0666611111166 0 0111111 0 0 0 066 099 0 066 0 0 099 066 0 077 099Resized to 25x25:

Converted two-dimensional matrix:

11111111 0666666666666 0 0 0 0 0 0 0 0 0 0 0 0 033

1111111166 0 0 0 0 0 066 0 0 0 0 0 0 0 0 0 0 033 0

11111111 0 0 0 0 0 0 0 06666 0 0 0 0 0 0 03333 0 0

11111111 0 0 0 0 0 0 0 06666 0 0 0 0 0 0 03333 0 0

 066 0 099 0 0 0 0 0 0 0 0 066 0 0 0 0 033 0 0 0 0

66 0 0 0 099 0 0 0 0 0 0 0 0 066 0 0 033 0 0 0 0 0

66 0 0 077 099 0 0 0 0 0 0 0 066 0 033 0 0 0 0 0 0

66 0 0 0 0 0 09999 0 0 0 0 0 0663333 0 0 0 0 0 0 0

66 0 0 0 0 0 09999 0 0 0 0 0 0663333 0 0 0 0 0 0 0

66 0 0 0 0 0 0 0 099 0 0 0 0 066 0 0 0 0 0 0 0 0 0

66 0 0 0 0 0 0 0 0 099 0 0 03366 0 0 0 0 0 0 0 0 0

 066 0 0 0 0 0 0 0 0 099333366 0 0 0 0 0 0 0 0 0 0

 0 06666 0 0 0 0 0 0 0336666 0 0 0 0 0 0 0 0 0 0 0

 0 06666 0 0 0 0 0 0 0336666 0 0 0 0 0 0 0 0 0 0 0

 0 0 0 066 0 0 0 0 03366 0 099 0 0 0 0 0 0 0 0 0 0

 0 0 0 0 0666666666666 0 0 0 099 0 0 0 0 0 0 0 0 0

 0 0 0 0 0 0 03333 0 0 0 0 0 0 09999 0 0 0 0 0 0 0

 0 0 0 0 0 0 03333 0 0 0 0 0 0 09999 0 0 0 0 0 0 0

 0 0 0 0 0 033 0 0 0 0 0 0 0 0 0 0 099 0 0 0 0 0 0

 0 0 0 0 033 0 0 0 0 0 0 0 0 0 0 0 0 099 0 0 0 0 0

 0 0 0 033 0 0 0 0 0 0 0 0 0 0 0 0 0 0 099 0 0 0 0

 0 03333 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 09999 0 0

 0 03333 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 09999 0 0

 033 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 099 0

33 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 099

Converted one-dimensional array:

11111111 0666666666666 0 0 0 0 0 0 0 0 0 0 0 0 0331111111166 0 0 0 0 0 066 0 0 0 0 0 0 0 0 0 0 033 011111111 0 0 0 0 0 0 0 06666 0 0 0 0 0 0 03333 0 011111111 0 0 0 0 0 0 0 06666 0 0 0 0 0 0 03333 0 0 066 0 099 0 0 0 0 0 0 0 0 066 0 0 0 0 033 0 0 0 066 0 0 0 099 0 0 0 0 0 0 0 0 066 0 0 033 0 0 0 0 066 0 0 077 099 0 0 0 0 0 0 0 066 0 033 0 0 0 0 0 066 0 0 0 0 0 09999 0 0 0 0 0 0663333 0 0 0 0 0 0 066 0 0 0 0 0 09999 0 0 0 0 0 0663333 0 0 0 0 0 0 066 0 0 0 0 0 0 0 099 0 0 0 0 066 0 0 0 0 0 0 0 0 066 0 0 0 0 0 0 0 0 099 0 0 03366 0 0 0 0 0 0 0 0 0 066 0 0 0 0 0 0 0 0 099333366 0 0 0 0 0 0 0 0 0 0 0 06666 0 0 0 0 0 0 0336666 0 0 0 0 0 0 0 0 0 0 0 0 06666 0 0 0 0 0 0 0336666 0 0 0 0 0 0 0 0 0 0 0 0 0 0 066 0 0 0 0 03366 0 099 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0666666666666 0 0 0 099 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 03333 0 0 0 0 0 0 09999 0 0 0 0 0 0 0 0 0 0 0 0 0 03333 0 0 0 0 0 0 09999 0 0 0 0 0 0 0 0 0 0 0 0 033 0 0 0 0 0 0 0 0 0 0 099 0 0 0 0 0 0 0 0 0 0 033 0 0 0 0 0 0 0 0 0 0 0 0 099 0 0 0 0 0 0 0 0 033 0 0 0 0 0 0 0 0 0 0 0 0 0 0 099 0 0 0 0 0 03333 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 09999 0 0 0 03333 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 09999 0 0 033 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 099 033 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 099

Resized to 12x12:

Converted two-dimensional matrix:

1111 06666 0 0 0 0 0 033

1111 0 0 0 066 0 0 033 0

 0 099 0 0 0 0 0 033 0 0

66 07799 0 0 06633 0 0 0

66 0 0 099 0 066 0 0 0 0

 0 0 0 0 09933 0 0 0 0 0

 066 0 0 03366 0 0 0 0 0

 0 0 06666 0 099 0 0 0 0

 0 0 033 0 0 0 099 0 0 0

 0 033 0 0 0 0 0 099 0 0

 033 0 0 0 0 0 0 0 099 0

33 0 0 0 0 0 0 0 0 0 099

Converted one-dimensional array:

1111 06666 0 0 0 0 0 0331111 0 0 0 066 0 0 033 0 0 099 0 0 0 0 0 033 0 066 07799 0 0 06633 0 0 066 0 0 099 0 066 0 0 0 0 0 0 0 0 09933 0 0 0 0 0 066 0 0 03366 0 0 0 0 0 0 0 06666 0 099 0 0 0 0 0 0 033 0 0 0 099 0 0 0 0 033 0 0 0 0 0 099 0 0 033 0 0 0 0 0 0 0 099 033 0 0 0 0 0 0 0 0 0 099Image3D 6x6x6:

drawLine(0,0,0,5,5,5,99);

drawCube(0,0,0,3,3,3,33);

 Converted three-dimensional matrix:

z=0

33 33 33 33  0  0

33  0  0 33  0  0

33  0  0 33  0  0

33 33 33 33  0  0

 0  0  0  0  0  0

 0  0  0  0  0  0

z=1

33  0  0 33  0  0

 0 99  0  0  0  0

 0  0  0  0  0  0

33  0  0 33  0  0

 0  0  0  0  0  0

 0  0  0  0  0  0

z=2

33  0  0 33  0  0

 0  0  0  0  0  0

 0  0 99  0  0  0

33  0  0 33  0  0

 0  0  0  0  0  0

 0  0  0  0  0  0

z=3

33 33 33 33  0  0

33  0  0 33  0  0

33  0  0 33  0  0

33 33 33 33  0  0

 0  0  0  0  0  0

 0  0  0  0  0  0

z=4

 0  0  0  0  0  0

 0  0  0  0  0  0

 0  0  0  0  0  0

 0  0  0  0  0  0

 0  0  0  0 99  0

 0  0  0  0  0  0

z=5

 0  0  0  0  0  0

 0  0  0  0  0  0

 0  0  0  0  0  0

 0  0  0  0  0  0

 0  0  0  0  0  0

 0  0  0  0  0 99

Converted one-dimensional array:

33 33 33 33  0  0 33  0  0 33  0  0 33  0  0 33  0  0 33 33 33 33  0  0  0  0  0  0  0  0  0  0  0  0  0  0 33  0  0 33  0  0  0 99  0  0  0  0  0  0  0  0  0  0 33  0  0 33  0  0  0  0  0  0  0  0  0  0  0  0  0  0 33  0  0 33  0  0  0  0  0  0  0  0  0  0 99  0  0  0 33  0  0 33  0  0  0  0  0  0  0  0  0  0  0  0  0  0 33 33 33 33  0  0 33  0  0 33  0  0 33  0  0 33  0  0 33 33 33 33  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 99  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 99 

[Program finished]

沒有留言:

發佈留言