360度読み込みをPythonで

天球画像を色々いじる

import tkinter as tkfrom tkinter 
import filedialogfrom PIL 
import Image, ImageTk, ImageChops, ImageOps
import time

class PanoramaViewer:
def init(self, root):
    self.root = root
    self.root.title("Panorama Viewer")
    self.horizontal_angle = 0
    self.vertical_angle = 0
    self.view_distance = 0
    self.play_speed = 1
    self.last_update_time = time.time()

    self.image_path = ""
    self.original_image = None
    self.processed_image = None
    self.tk_image = None

    self.canvas_width = 800
    self.canvas = tk.Canvas(root, width=self.canvas_width)
    self.canvas.pack()

    self.horizontal_scrollbar = tk.Scale(root, from_=0, to=360, orient=tk.HORIZONTAL, label="Horizontal Angle", command=self.update_view_angle)
    self.horizontal_scrollbar.pack(fill=tk.X)

    self.vertical_scrollbar = tk.Scale(root, from_=-90, to=90, orient=tk.HORIZONTAL, label="Vertical Angle", command=self.update_vertical_angle)
    self.vertical_scrollbar.pack(fill=tk.X)

    self.distance_scrollbar = tk.Scale(root, from_=-100, to=100, orient=tk.HORIZONTAL, label="View Distance", command=self.update_distance)
    self.distance_scrollbar.pack(fill=tk.X)

    open_button = tk.Button(root, text="Open", command=self.open_image)
    open_button.pack(side=tk.LEFT)

    save_button = tk.Button(root, text="Save As", command=self.save_image_as)
    save_button.pack(side=tk.LEFT)

    play_button = tk.Button(root, text="Play", command=self.play)
    play_button.pack(side=tk.LEFT)

    pause_button = tk.Button(root, text="Pause", command=self.pause)
    pause_button.pack(side=tk.LEFT)

    fast_forward_button = tk.Button(root, text="Fast Forward", command=self.fast_forward)
    fast_forward_button.pack(side=tk.LEFT)

    rewind_button = tk.Button(root, text="Rewind", command=self.rewind)
    rewind_button.pack(side=tk.LEFT)

    self.image_type = tk.StringVar()
    self.image_type.set("360")

    panorama_button = tk.Radiobutton(root, text="360-Degree Image", variable=self.image_type, value="360", command=self.select_image_type)
    panorama_button.pack(side=tk.LEFT)

    plane_button = tk.Radiobutton(root, text="Flat Image", variable=self.image_type, value="flat", command=self.select_image_type)
    plane_button.pack(side=tk.LEFT)

    self.display_mode = tk.StringVar()
    self.display_mode.set("original")

    original_button = tk.Radiobutton(root, text="Original", variable=self.display_mode, value="original", command=self.select_display_mode)
    original_button.pack(side=tk.LEFT)

    grayscale_button = tk.Radiobutton(root, text="Grayscale", variable=self.display_mode, value="grayscale", command=self.select_display_mode)
    grayscale_button.pack(side=tk.LEFT)

    color_extraction_button = tk.Radiobutton(root, text="Color Extraction", variable=self.display_mode, value="color_extraction", command=self.select_display_mode)
    color_extraction_button.pack(side=tk.LEFT)

    self.lower_red_slider = tk.Scale(root, from_=0, to=255, orient=tk.HORIZONTAL, label="Lower Red", command=self.update_color_extraction)
    self.lower_red_slider.pack(side=tk.LEFT)

    self.upper_red_slider = tk.Scale(root, from_=0, to=255, orient=tk.HORIZONTAL, label="Upper Red", command=self.update_color_extraction)
    self.upper_red_slider.pack(side=tk.LEFT)

    self.lower_green_slider = tk.Scale(root, from_=0, to=255, orient=tk.HORIZONTAL, label="Lower Green", command=self.update_color_extraction)
    self.lower_green_slider.pack(side=tk.LEFT)

    self.upper_green_slider = tk.Scale(root, from_=0, to=255, orient=tk.HORIZONTAL, label="Upper Green", command=self.update_color_extraction)
    self.upper_green_slider.pack(side=tk.LEFT)

    self.lower_blue_slider = tk.Scale(root, from_=0, to=255, orient=tk.HORIZONTAL, label="Lower Blue", command=self.update_color_extraction)
    self.lower_blue_slider.pack(side=tk.LEFT)

    self.upper_blue_slider = tk.Scale(root, from_=0, to=255, orient=tk.HORIZONTAL, label="Upper Blue", command=self.update_color_extraction)
    self.upper_blue_slider.pack(side=tk.LEFT)

    self.playing = False

def open_image(self):
    file_path = filedialog.askopenfilename(title="Open Image", filetypes=[("Image files", "*.png;*.jpg;*.jpeg;*.gif")])

    if file_path:
        self.image_path = file_path
        self.original_image = Image.open(file_path)
        self.processed_image = self.original_image.copy()
        self.tk_image = ImageTk.PhotoImage(self.original_image)
        self.display_image()

def save_image_as(self):
    if self.original_image:
        file_path = filedialog.asksaveasfilename(title="Save Image As", defaultextension=".png", filetypes=[("PNG files", "*.png"), ("JPEG files", "*.jpg;*.jpeg")])
        
        if file_path:
            self.processed_image.save(file_path)

def display_image(self):
    if self.image_type.get() == "360":
        rotated_image = self.original_image.rotate(self.horizontal_angle)
        shifted_image = rotated_image.rotate(self.vertical_angle, resample=Image.BICUBIC, center=(rotated_image.width // 2, rotated_image.height // 2))
        translated_image = ImageChops.offset(shifted_image, self.view_distance, 0)
    else:
        translated_image = self.original_image

    self.processed_image = self.apply_display_mode(translated_image)
    
    self.tk_image = ImageTk.PhotoImage(self.processed_image)
    self.canvas.config(width=self.canvas_width, height=self.calculate_canvas_height())
    self.canvas.create_image(0, 0, anchor=tk.NW, image=self.tk_image)

def apply_display_mode(self, image):
    mode = self.display_mode.get()

    if mode == "grayscale":
        return ImageOps.grayscale(image)
    elif mode == "color_extraction":
        return self.extract_color(image)
    else:
        return image

def extract_color(self, image):
    lower_red = self.lower_red_slider.get()
    upper_red = self.upper_red_slider.get()
    lower_green = self.lower_green_slider.get()
    upper_green = self.upper_green_slider.get()
    lower_blue = self.lower_blue_slider.get()
    upper_blue = self.upper_blue_slider.get()

    color_mask = Image.new("RGB", image.size, color=(255, 255, 255))
    color_mask.paste(image, mask=image)

    data = color_mask.getdata()
    new_data = []

    for item in data:
        red, green, blue = item

        if lower_red <= red <= upper_red and lower_green <= green <= upper_green and lower_blue <= blue <= upper_blue:
            new_data.append(item)
        else:
            grayscale_value = int(0.299 * red + 0.587 * green + 0.114 * blue)
            new_data.append((grayscale_value, grayscale_value, grayscale_value))

        color_mask.putdata(new_data)
        return ImageChops.multiply(image, color_mask)

    def calculate_canvas_height(self):
        if self.image_type.get() == "360":
            aspect_ratio = self.original_image.width / self.original_image.height
            return int(self.canvas_width / aspect_ratio)
        else:
            return self.original_image.height

    def update_view_angle(self, angle):
        self.horizontal_angle = float(angle)
        self.display_image()

    def update_vertical_angle(self, angle):
        self.vertical_angle = float(angle)
        self.display_image()

    def update_distance(self, distance):
        self.view_distance = int(distance)
        self.display_image()

    def play(self):
        self.playing = True
        self.play_continuous()

    def play_continuous(self):
        if self.playing:
            current_time = time.time()
            elapsed_time = current_time - self.last_update_time

            update_interval = 1 / 30  # 30フレーム/秒(適宜調整)

            if elapsed_time >= update_interval:
                # 指定された再生速度で角度を更新
                self.horizontal_angle += 1 * self.play_speed  # 仮の角度更新(適宜調整)
                self.display_image()
                self.last_update_time = current_time

            self.root.after(10, self.play_continuous)

    def pause(self):
        self.playing = False

    def fast_forward(self):
        self.play_speed *= 2  # 2倍速

    def rewind(self):
        self.play_speed /= 2  # 2分の1倍速

if __name__ == "__main__":
    root = tk.Tk()
    viewer = PanoramaViewer(root)
    root.mainloop()



この記事が気に入ったらサポートをしてみませんか?