我得到了一个 protobuf 接口,其中有一个可以旋转 90 度的“图像”。我一直停留在图像表示上,因为它是通过数据类型字节而不是二维矩阵传入的。这是 protobuf 文件:
syntax = "proto3";
option java_multiple_files = true;
option java_package = "imageRotation";
// A single image which might be grayscale, or color.
//
// When color == false, this image is grayscale.
// In this case, the data is single channel (one byte per pixel)
// and stored row-wise.
//
// When color == true, this is a color image. In
// this case, the data is 3 channel rgb with the rgb
// triplets stored row-wise (one byte per channel, 3 bytes
// per pixel).
message Image {
bool color = 1;
bytes data = 2;
int32 width = 3;
int32 height = 4;
}
// A request to rotate an image by some multiple of 90 degrees.
//
// The input image may be color or grayscale.
//
// Positive rotations are counter clockwise.
message ImageRotateRequest {
enum Rotation {
NONE = 0;
NINETY_DEG = 1;
ONE_EIGHTY_DEG = 2;
TWO_SEVENTY_DEG = 3;
}
Rotation rotation = 1;
Image image = 2;
}
service ImageService {
rpc RotateImage(ImageRotateRequest) returns (Image);
}
我很困惑如何将“字节数据”转换为代表图像的 RGB 值网格。
这是我目前的方法:
# Import the necessary modules and generated protobuf classes
from image_pb2 import Image, ImageRotateRequest, ImageService
from concurrent import futures
import grpc
# Function to rotate an image matrix by the specified number of 90-degree rotations
def rotate_image(image, rotations):
rotation_mapping = {
ImageRotateRequest.NONE: 0,
ImageRotateRequest.NINETY_DEG: 90,
ImageRotateRequest.ONE_EIGHTY_DEG: 180,
ImageRotateRequest.TWO_SEVENTY_DEG: 270,
}
rotations = rotation_mapping.get(rotations, 0)
# Extract image details from the request
is_color = image.color
width = image.width
height = image.height
# Calculate the matrix size based on color and width/height
matrix_size = width * height * (3 if is_color else 1)
# Decode the image data from bytes to a matrix
matrix_values = list(image.data[:matrix_size])
if is_color:
# For color images, reshape the matrix to a 3D matrix (3 channels)
matrix = [matrix_values[i:i+3] for i in range(0, matrix_size, 3)]
else:
matrix = [matrix_values]
# Rotate the matrix based on the request
rotated_matrix = [list(row) for row in zip(*matrix[::-1])] # Rotate 90 degrees
# Flatten the rotated matrix back to a list of values
rotated_values = [value for row in rotated_matrix for value in row]
# Create a new Image with rotated values
rotated_image = Image(
color=is_color,
data=bytes(rotated_values),
width=height,
height=width # Swap width and height after rotation
)
return rotated_image
# gRPC service implementation
class ImageServiceImpl(ImageService):
def RotateImage(self, request, context):
# Rotate the image based on the request
rotated_image = rotate_image(request.image, request.rotation)
# Return the rotated image in the response
return rotated_image
# gRPC server setup
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
ImageService.add_RotateImageServicer_to_server(ImageServiceImpl(), server)
server.add_insecure_port('[::]:50051')
server.start()
server.wait_for_termination()
if __name__ == '__main__':
serve()
因此,我不明白传入的 bytes 参数将如何填充值的宽度 x 高度矩阵。
任何帮助将不胜感激,这是一项艰巨的任务。谢谢!
参见:
这很老套,但是:
import grpc
import image_pb2
import image_pb2_grpc
from concurrent import futures
# gRPC service implementation
class ImageService(image_pb2_grpc.ImageServiceServicer):
def RotateImage(self, request, context):
# Ensure that the number of bytes matches expection: width*height*bytes(color)
# Where bytes(color) = 1 (false) and 3 (true)
got = request.image.width * request.image.height * (3 if request.image.color else 1)
want = len(request.image.data)
if got != want:
context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
context.set_details("Image data size does not correspond to width, height and color")
return request.image
# If there's no rotation to perform, shortcut to returning the provided image
if request.rotation == image_pb2.ImageRotateRequest.NONE:
return request.image
# Convert the image to a matrix
matrix = []
current = 0
for y in range(request.image.height):
row = []
for x in range(request.image.width):
if request.image.color:
# True (RGB) requires 3 bytes (use tuple)
pixel = (
request.image.data[current],
request.image.data[current+1],
request.image.data[current+2],
)
current += 3
else:
# False (Grayscale) requires 1 byte
pixel = request.image.data[current]
current += 1
row.append(pixel)
# Append row
matrix.append(row)
print(matrix)
if request.rotation == image_pb2.ImageRotateRequest.NINETY_DEG:
print("Rotating: 090")
matrix = list(zip(*matrix[::-1]))
if request.rotation == image_pb2.ImageRotateRequest.ONE_EIGHTY_DEG:
print("Rotating: 180")
matrix = list(zip(*matrix[::-1]))
matrix = list(zip(*matrix[::-1]))
if request.rotation == image_pb2.ImageRotateRequest.TWO_SEVENTY_DEG:
print("Rotating: 270")
# Rotate counterclockwise
matrix = list(zip(*matrix))[::-1]
# Flatten the matrix
pixels = []
for y in range(request.image.height):
for x in range(request.image.width):
if request.image.color:
pixels.extend(matrix[y][x])
else:
pixels.append(matrix[y][x])
print(f"Result: {pixels}")
# Revert the flattened matrix to bytes
data = bytes(pixels)
# Return the rotated image in the response
return image_pb2.Image(
color=request.image.color,
data=data,
width=request.image.width,
height=request.image.height,
)
# gRPC server setup
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
image_pb2_grpc.add_ImageServiceServicer_to_server(ImageService(), server)
server.add_insecure_port('[::]:50051')
server.start()
server.wait_for_termination()
if __name__ == '__main__':
serve()
重构矩阵处理以便能够单独测试它会很好,但是,例如:
# Rotation: 180
REQUEST="
{
\"rotation\": 2,
\"image\": {
\"color\": ${COLOR},
\"data\": \"${DATA}\",
\"width\": 3,
\"height\": 3
}
}"
grpcurl \
-plaintext \
-proto image.proto \
-d "${REQUEST}" \
localhost:50051 \
ImageService/RotateImage \
| jq -r .data \
| base64 --decode \
| xxd -g 3
例如灰度
# Want: [[1,2,3],[4,5,6],[7,8,9]]
# Byte: 010203040506070809
# B64: AQIDBAUGBwgJ
DATA="AQIDBAUGBwgJ"
COLOR=false
00000000: 090807 060504 030201
# Want: [[9,8,7],[6,5,4],[3,2,1]]
例如颜色
# Want: [[010101,020202,030303], ... ]
# Byte: 010101020202030303...
# B64: AQEBAgICAwMDBAQEBQUFBgYGBwcHCAgICQkJ
DATA="AQEBAgICAwMDBAQEBQUFBgYGBwcHCAgICQkJ"
COLOR=true
00000000: 090909 080808 070707 060606 050505 04
00000010: 040403 030302 020201 0101
# Want [[090909, 080808, 070707], ... ]