Renderizado.py
Код: Выделить всё
import pygame as pg
from OpenGL.GL import *
import numpy as np
import ctypes
import glm
import cv2
from OpenGL.GL.shaders import compileProgram, compileShader
import json
import os
from pathlib import Path
class App:
def __init__(self):
# Loads camera position from JSON and sets up OpenGL
self.load_json_data()
# Initializes pygame and sets the OpenGL display mode
pg.init()
pg.display.set_mode((640, 480), pg.OPENGL | pg.DOUBLEBUF)
self.clock = pg.time.Clock()
# Configures background color and depth testing
glClearColor(0, 0, 0, 1.0)
glEnable(GL_DEPTH_TEST)
# Create and use the shader
self.shader = self.createShader("shaders/vertex.txt", "shaders/fragment.txt")
glUseProgram(self.shader)
# Perspective projection setup
self.projection = glm.perspective(glm.radians(100), 640 / 480, 0.1, 2000.0)
self.projection[0][0] *= -1 # Invert the X-axis in the projection
glUniformMatrix4fv(glGetUniformLocation(self.shader, "projection"), 1, GL_FALSE, glm.value_ptr(self.projection))
# Camera position from JSON
self.camera_pos = self.camera_position + glm.vec3(0, 35, 0) # Camera position in the JSON
# Sets the camera target to point forward along the positive Z-axis
self.camera_target = self.camera_pos + glm.vec3(0, -0.65, 1) # Changes the direction to point forward
# Adjusts the camera "up" vector
self.camera_up = glm.vec3(0, 1, 0) # Invert the Y-axis if the camera is upside down
# Sets up the camera view to point forward
self.view = glm.lookAt(self.camera_pos, self.camera_target, self.camera_up)
glUniformMatrix4fv(glGetUniformLocation(self.shader, "view"), 1, GL_FALSE, glm.value_ptr(self.view))
# Sets the rendering mode to fill
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
# Rotation variables to control the view
self.rotation_x = 0.0
self.rotation_y = 0.0
try:
# Creates cubes and loads objects from JSON
self.cubes = []
self.create_cubes_from_json()
print(f"Created {len(self.cubes)} cubes")
except Exception as e:
print(f"Error during initialization: {str(e)}")
pg.quit()
raise
# Starts the main application loop
self.mainloop()
def load_json_data(self):
"""Load JSON data from the data directory"""
current_dir = Path(__file__).parent
json_path = current_dir / "data" / "SmartDeviceDataPositions.json"
print(f"Attempting to load JSON from: {json_path}")
if not json_path.exists():
raise FileNotFoundError(f"JSON file not found at {json_path}")
try:
with open(json_path, 'r') as f:
data = json.load(f)
# Confirm that 'Items' is in data
if 'Items' not in data:
print("Error: the 'Items' key is not in the JSON.")
raise KeyError("The JSON file does not contain 'Items' key")
# Assign data
self.items = data['Items']
print("'Items' data loaded successfully:", self.items)
# Confirm that 'camera' has 'Pos' before assigning it
if 'camera' in self.items and 'Pos' in self.items['camera']:
self.camera_position = glm.vec3(*self.items['camera']['Pos'])
print("Camera position loaded:", self.camera_position)
else:
raise KeyError("The camera does not have a defined position in the JSON")
except json.JSONDecodeError as e:
raise Exception(f"Error parsing JSON file: {str(e)}")
except KeyError as e:
raise Exception(f"Invalid JSON structure: {str(e)}")
except Exception as e:
print(f"Error during JSON loading: {str(e)}")
self.items = None # Ensure it is None in case of failure
def create_cubes_from_json(self):
"""Create cubes from JSON data with adjusted scale"""
if self.items is None:
print("Error: 'self.items' did not load correctly.")
return # Exit the function if data was not loaded
"""Create cubes from JSON data with adjusted scale"""
positions = [] # To calculate the center of objects
for item_name, item_data in self.items.items():
if item_name != "camera" and "Size" in item_data:
try:
pos = item_data["Pos"]
size = item_data["Size"]
# Adjust scale factor for better visibility
scale_factor = 0.5 # Increased scale factor
# Print debug information
print(f"Creating cube for {item_name}:")
print(f"Original position: {pos}")
print(f"Original size: {size}")
print(f"Scaled position: {[p * scale_factor for p in pos]}")
print(f"Scaled size: {[s * scale_factor for s in size]}")
cube = Cubo(
position=[p * scale_factor for p in pos],
size=[s * scale_factor for s in size],
color=[0.0, 0.0, 1.0] # Blue color for JSON cubes
)
self.cubes.append(cube)
except KeyError as e:
print(f"Warning: Skipping item {item_name} - missing required data: {str(e)}")
except Exception as e:
print(f"Warning: Error creating cube for {item_name}: {str(e)}")
if positions:
avg_x = sum(p[0] for p in positions) / len(positions)
avg_y = sum(p[1] for p in positions) / len(positions)
avg_z = sum(p[2] for p in positions) / len(positions)
self.camera_target = glm.vec3(avg_x, avg_y, avg_z)
print("New camera target (camera_target):", self.camera_target)
else:
self.camera_target = glm.vec3(0, 0, 0) # In case there are no positions
# Dentro de la clase App, después de las funciones de inicialización y renderizado
def render_mask(self):
# Clear buffers
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glUseProgram(self.shader)
# Render cubes in white
for cube in self.cubes:
cube.color = [1.0, 1.0, 1.0]
cube.draw()
# Capture pixels
width, height = 640, 480
pixels = glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE)
# Convert to NumPy array
image_array = np.frombuffer(pixels, dtype=np.uint8)
image_array = image_array.reshape(height, width, 3)
# Convert to grayscale
gray_image = cv2.cvtColor(image_array, cv2.COLOR_RGB2GRAY)
# Apply only vertical transformation
gray_image = cv2.flip(gray_image, 0)
# Ensure the output directory exists
output_dir = self.ensure_output_directory()
output_path = output_dir / "mask.png"
# Save the image
success = cv2.imwrite(str(output_path), gray_image)
if success:
print(f"Mask image saved successfully to {output_path}")
# Show only a preview after saving
cv2.imshow('Mask Preview', gray_image)
cv2.waitKey(1)
else:
print("Error saving the mask image")
return gray_image # Return the image in case we need to use it elsewhere
def ensure_output_directory(self):
output_dir = Path(__file__).parent / "output"
output_dir.mkdir(exist_ok=True)
return output_dir
def save_frame_as_image(self, filename):
try:
output_dir = self.ensure_output_directory()
output_path = output_dir / filename
# Capture pixels de OpenGL
pixels = glReadPixels(0, 0, 640, 480, GL_RGB, GL_UNSIGNED_BYTE)
# Convert to NumPy array
image_array = np.frombuffer(pixels, dtype=np.uint8)
image_array = image_array.reshape(480, 640, 3)
# Convert to grayscale si es necesario
if filename.endswith('mask.png'):
image_array = cv2.cvtColor(image_array, cv2.COLOR_RGB2GRAY)
# Apply only vertical transformation
image_array = cv2.flip(image_array, 0)
# Save the image
success = cv2.imwrite(str(output_path), image_array)
if success:
print(f"Image saved successfully to {output_path}")
else:
print(f"Error saving image to {output_path}")
except Exception as e:
print(f"Error saving image: {str(e)}")
def createShader(self, vertexFilepath, fragmentFilepath):
try:
with open(vertexFilepath, 'r') as f:
vertex_src = f.readlines()
with open(fragmentFilepath, 'r') as f:
fragment_src = f.readlines()
shader = compileProgram(compileShader(vertex_src, GL_VERTEX_SHADER),
compileShader(fragment_src, GL_FRAGMENT_SHADER))
return shader
except Exception as e:
raise Exception(f"Error creating shader: {str(e)}")
def mainloop(self):
running = True
save_image = False
while running:
for event in pg.event.get():
if event.type == pg.QUIT:
running = False
elif event.type == pg.KEYDOWN:
if event.key == pg.K_m: # Press 'M' to render the mask and save it
self.render_mask()
self.save_frame_as_image("mask.png") # Guarda la máscara como imagen
elif event.key == pg.K_ESCAPE:
running = False
# Other existing controls (rotation, camera, etc.)
# Main rendering and update logic
# ...
# Add debug keys
elif event.key == pg.K_r: # Reset rotation
self.rotation_x = 0.0
self.rotation_y = 0.0
elif event.key == pg.K_z: # Move camera in/out
self.camera_pos.z -= 100
self.view = glm.lookAt(self.camera_pos, self.camera_target, self.camera_up)
glUniformMatrix4fv(glGetUniformLocation(self.shader, "view"), 1, GL_FALSE, glm.value_ptr(self.view))
elif event.key == pg.K_x: # Move camera out
self.camera_pos.z += 100
self.view = glm.lookAt(self.camera_pos, self.camera_target, self.camera_up)
glUniformMatrix4fv(glGetUniformLocation(self.shader, "view"), 1, GL_FALSE, glm.value_ptr(self.view))
# Handle mouse movement for rotation
mouse_dx, mouse_dy = pg.mouse.get_rel()
self.rotation_x += mouse_dy * 0.1
self.rotation_y += mouse_dx * 0.1
# Clear buffers
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glUseProgram(self.shader)
# Update model matrix with rotations
model = glm.mat4(1.0)
model = glm.rotate(model, glm.radians(self.rotation_x), glm.vec3(1, 0, 0))
model = glm.rotate(model, glm.radians(self.rotation_y), glm.vec3(0, 1, 0))
glUniformMatrix4fv(glGetUniformLocation(self.shader, "model"), 1, GL_FALSE, glm.value_ptr(model))
# Render all cubes
for cube in self.cubes:
cube.draw()
if save_image:
self.save_frame_as_image("initial_mask.png")
save_image = False
pg.display.flip()
self.clock.tick(60)
# Cleanup
for cube in self.cubes:
cube.destructor()
glDeleteProgram(self.shader)
pg.quit()
class Cubo:
def __init__(self, position=[0,0,0], size=[1,1,1], color=[1.0, 1.0, 1.0]):
x, y, z = position
w, h, d = size
# Use the provided color for all faces
self.vertices = np.array([
# Front face
x, y, z+d, *color,
x+w, y, z+d, *color,
x+w, y+h, z+d, *color,
x, y+h, z+d, *color,
# Back face
x, y, z, *color,
x+w, y, z, *color,
x+w, y+h, z, *color,
x, y+h, z, *color,
# Left face
x, y, z, *color,
x, y, z+d, *color,
x, y+h, z+d, *color,
x, y+h, z, *color,
# Right face
x+w, y, z, *color,
x+w, y, z+d, *color,
x+w, y+h, z+d, *color,
x+w, y+h, z, *color,
# Top face
x, y+h, z+d, *color,
x+w, y+h, z+d, *color,
x+w, y+h, z, *color,
x, y+h, z, *color,
# Bottom face
x, y, z+d, *color,
x+w, y, z+d, *color,
x+w, y, z, *color,
x, y, z, *color,
], dtype=np.float32)
self.vertex_count = 24
self.vao = glGenVertexArrays(1)
glBindVertexArray(self.vao)
self.vbo = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
glBufferData(GL_ARRAY_BUFFER, self.vertices.nbytes, self.vertices, GL_STATIC_DRAW)
# Position attribute
glEnableVertexAttribArray(0)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(0))
# Color attribute
glEnableVertexAttribArray(1)
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 24, ctypes.c_void_p(12))
def draw(self):
glBindVertexArray(self.vao)
glDrawArrays(GL_QUADS, 0, self.vertex_count)
def destructor(self):
glDeleteVertexArrays(1, (self.vao,))
glDeleteBuffers(1, (self.vbo,))
if __name__ == "__main__":
myApp = App()
def process_mask_image():
mask = cv2.imread("mask.png", cv2.IMREAD_GRAYSCALE)
edges = cv2.Canny(mask, 100, 200) # Detect edges in the mask
cv2.imshow("Mask Edges", edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
Код: Выделить всё
{
"Notes": "Camera is set to origin. Positions and sizes, if not stated otherwise, are in centimeters. Positions are (left, bottom, front) corner as seen from camera looking down positive z axis. When applicable, name is the HomeAssistant ID of the item",
"Items": {
"camera": {
"Pos": [
0,
0,
0
]
},
"light.led_rgb_stick_fisheye": {
"Pos": [
0,
-235,
7
],
"Size": [
2,
150,
2
]
},
"light.led_rgb_stick_l_monitor_array": {
"Pos": [
-270,
-235,
290
],
"Size": [
2,
150,
2
]
},
"light.led_rgb_stick_r_monitor_array": {
"Pos": [
-270,
-235,
590
],
"Size": [
2,
150,
2
]
},
"light.led_rgb_rect_server": {
"Pos": [
-270,
-52,
75
],
"Size": [
10,
30,
120
]
},
"light.led_rgb_rectangle_window": {
"Pos": [
-270,
-122,
735
],
"Size": [
10,
120,
30
]
},
"light.led_rgb_square": {
"Pos": [
250,
-77,
325
],
"Size": [
10,
60,
60
]
},
"Monitor_array": {
"Pos": [
-270,
-92,
340
],
"Size": [
5,
60,
210
]
},
"TV_Window": {
"Pos": [
260,
-117,
565
],
"Size": [
10,
80,
130
]
},
"TV_Smart": {
"Pos": [
-209,
-134,
5
],
"Size": [
145,
83,
5
]
},
"Curved_Light_Server": {
"Pos": [
-270,
-235,
0
],
"Size": [
40,
170,
35
]
},
"Curved_Light_Window": {
"Pos": [
215,
-235,
715
],
"Size": [
45,
170,
75
]
}
}
}
Код: Выделить всё
#version 330 core
out vec4 outColor;
void main()
{
outColor = vec4(1.0, 1.0, 1.0, 1.0);
}
Код: Выделить всё
#version 330 core
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 color;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
out vec3 fragColor;
void main()
{
gl_Position = projection * view * model * vec4(position, 1.0);
fragColor = color;
}
Изображение, которому должна соответствовать маска
Я попробовал изменить часть «self.camera_target = self. camera_pos + glm.vec3(0, -0.65, 1)", чтобы максимально точно соответствовать изображению путем ввода случайных значений.
Чтобы визуализировать, как маска должна выглядеть при наложении на фотографию, я нарисовал места, где изображения должны быть в редакторе.
Подробнее здесь: https://stackoverflow.com/questions/791 ... -in-python
Мобильная версия