py2_draw the pictures

本文介绍了Python标准库中的Turtle绘图库,详细讲解了Turtle库的基本概念,包括坐标系统、角度系统、窗口设置、移动控制、方向控制等功能。同时,文章还介绍了颜色系统及如何使用Turtle库进行绘图创作。
  • The overview of Turtle Library

Turtle Library is a python implementation of the Turtle Mapping System.(from 1969, for programming design)
Turtle LIbrary is a standard library from python language.(Standard library:the functional module in the operating system directly installed by the interpreter)
Library, Package, Module have some different.

Absolute Coordinate System: the central of the screen is the zero-point of this coordinate system, others are same as the Mathematical Coordinate System. this system include the Absolute Angle System, which is also same as Angle System of the Mathematical Coordiante System.
Turtle Coordinate System: the direction the turtle moves called “forward”, the left and the right direction is turtle’s left and right. The function "turtle.circle(rad, angle)"means that the curve is run with a point which is in the left of the turtle’s current position as the center of the circle, parameters “rad” means radius and "angle"means the rotation angle. It also has a Turtle Angle System which is based on turtle’s direction.

turtle.setup(width, height, startx, starty):Used to sitting the size and position of the window.
It has 4 parameters and the later two are alternated.
The parameters “width” and “height” means the width and the height of the window.
The parameters “startx” and “starty” determined the position of the top-left-corner of the window.
Setup Function is not necessary, only if you want control the size of window.
turtle.goto(x, y):Used to let the turtle go to the target-point.
The position of the target-point are determined by the parameters “x” and “y”.
turtle.seth(angle) [In Absolute Angle System]:Used to change the direction of the turtle, this function only changes the direction but does NOT move.
The parameter “angle” is absolute angle.
turtle.left(angle) / turtle.right(angle) [In Turtle Angle System]:Used to change the direction of the turtle, based on Turtle Angle System.
The parameter “angle” is turtle’s angle, beyond the turtle is positive angle, below is negative.*

  • RGB Color System

common RGB color

EnNameRGBRGB in Decimal
white255, 255, 255[1, 1, 1]
yellow255, 255, 0[1, 1, 0]
magenta255, 0, 255[1, 0, 1]
cyan0, 255, 255[0, 1, 1]
blue0, 0, 255[0, 0, 1]
black0, 0, 0[0, 0, 0]
seashell255, 245, 238[1, 0.96, 0,93]
gold255, 215, 0[1, 0.84, 0]
pink255, 192, 203[1, 0.75, 0.80]
brown165, 42, 42[0.65, 0.16, 0.16]
purple160, 32, 240[0.63, 0.13, 0.94]
tomato255, 99, 71[1, 0.39, 0.28]

turtle.colormode(mode)Two options:
1.0: RGB small numerical
255: RGB integer

  • The reference of Turtle Base and “import”

Using the key word “import” to cite the Turtle base.

There are four Pen Control Functions in Python language:
penup(): Used to apprent with pendown(), when the Penup Function is operating, the track of turtle will not show on the screen.
pendown(): Has the opposite function with the Penup Function, when the Pendown Function is operating, the track of turtle will show on the screen.
pensize(width): AKA turtle.width(width), used to setup the size of pen.
pencolor(color): give the pen color, using Color String, RGB Decimal Value or RGB Array to assign the parameter “color”.

There are two Mobile Control Functions:
forward(d): AKA turtle.fd(d), make the turtle go straight, the parameter “d” is distance of the turtle move, when its value is under zero, means backward.
circle(r, extent): Draw the arc of the “extent” angle according to the radium “r”. The default central of the circle is on the left of the turtle.

There is a Direction Control Function:
setheading(angle): AKA turtle.seth(angle): to change the direction of the turtle, makes turtle go at an angle.[In Absolute Coordinate System]

Loop Statement and function range(num):
for in is a couple of key words, in For i In range(num) statement, executive the statement from i to num-1, when i wasn’t assigned, the default value is zero.

Using turtle.done() to hang out the window.

################################## # Semi-OOP version of runes.py # # Last updated: 21 January 2025 # ################################## ######### START OF SETUP ######### # Imports import functools import graphics import math import time import PyGif from random import random # Constants viewport_size = 600 # This is the height of the viewport spread = 20 active_hollusion = None lastframe = None Posn = graphics.Posn Rgb = graphics.Rgb draw_solid_polygon = graphics.draw_solid_polygon graphics.init(viewport_size) vp = graphics.open_viewport("ViewPort", viewport_size, viewport_size) lp = graphics.open_pixmap("LeftPort", viewport_size, viewport_size) rp = graphics.open_pixmap("RightPort", viewport_size, viewport_size) def type_checker(types): """ A setup function to help you check for types. Nothing to see here. """ def decorator(func): name = func.__name__ # functools.wraps preserves the docstring @functools.wraps(func) def checker(*args): for i in range(len(args)): if not isinstance(args[i], types[i]): expected = types[i].__name__ actual = type(args[i]).__name__ raise TypeError( f"Function {name}: Expected type {expected} for argument #{i + 1} but given {args[i]} of type {actual}" ) return func(*args) return checker return decorator def is_list_or_tuple(lst): """ A setup function to check for types. Nothing to see here. """ return isinstance(lst, (list, tuple)) class Frame: """ A frame object. Nothing to see here. """ def __init__(self, p0, p1, p2, z1, z2): self.orig = p0 self.x = p1 self.y = p2 self.z1 = z1 self.z2 = z2 unit_frame = Frame(Posn(0, 0), Posn(viewport_size, 0), Posn(0, viewport_size), 0, 1) class Rune: """ A rune object. Nothing to see here. """ def __init__(self, func): self.fn = func def show(self, vp, frame): self.fn(vp, frame) def __str__(self): return "Rune" def scale_vect(mult, p): """ A setup function to scale vectors. Nothing to see here. """ return Posn(mult * p.x, mult * p.y) def transform_posn(frame): """ A setup function for position transformation. Nothing to see here. """ def f(posn): return frame.orig + ( scale_vect(posn.x / viewport_size, frame.x) + scale_vect(posn.y / viewport_size, frame.y) ) return f def inverse_transform_posn(frame): """ The 'inverse' of transform_posn. Nothing to see here. """ a = frame.x.x b = frame.y.x c = frame.x.y d = frame.y.y det = a * d - b * c if det == 0: raise Exception("somehow you managed to zero the determinant for your frame") inv_mat = ((d / det, -b / det), (-c / det, a / det)) def function(posn): nonlocal inv_mat t = list( map( lambda m: m[0] * (posn.x - frame.orig.x) + m[1] * (posn.y - frame.orig.y), inv_mat, ) ) return Posn(viewport_size * t[0], viewport_size * t[1]) return Rune(function) def center_and_fill(p): """ A function used in circle_bb, spiral_bb, and ribbon_bb. Nothing to see here. """ center = Posn(viewport_size / 2, viewport_size / 2) return center + scale_vect(viewport_size / 2, p) ############ END OF SETUP ############ @type_checker((Rune,)) def show(rune): """ Shows the `rune` at the working window. """ return rune.show(vp, unit_frame) def clear_all(): """ Clears the current working window. """ global active_hollusion global vp, lp, rp if active_hollusion != None: active_hollusion("kill") active_hollusion = None graphics.clear_viewport(vp) graphics.clear_viewport(lp) graphics.clear_viewport(rp) def blank_bbf(vp, frame): """ A blank rune. Basically nothing. """ blank_bb = Rune(blank_bbf) def sail_bbf(vp, frame): """ A sail-shaped rune. """ p = [ Posn(viewport_size / 2, 0), Posn(viewport_size / 2, viewport_size), Posn(viewport_size, viewport_size), ] if is_list_or_tuple(vp[0]): for count, port in enumerate(vp): draw_solid_polygon( port, map(transform_posn(frame), p), Posn( (0.3 - frame.z1) * (spread * (((2 * count) / (len(vp) - 1)) - 1)), 0 ), Rgb(frame.z1, frame.z1, frame.z1), ) elif vp != None: draw_solid_polygon( vp, map(transform_posn(frame), p), Posn(0, 0), Rgb(frame.z1, frame.z1, frame.z1), ) sail_bb = Rune(sail_bbf) def corner_bbf(vp, frame): """ A small triangular rune at the corner. """ p = [ Posn(viewport_size / 2, 0), Posn(viewport_size, 0), Posn(viewport_size, viewport_size / 2), ] if is_list_or_tuple(vp[0]): for count, port in enumerate(vp): draw_solid_polygon( port, map(transform_posn(frame), p), Posn( (0.3 - frame.z1) * (spread * (((2 * count) / (len(vp) - 1)) - 1)), 0 ), Rgb(frame.z1, frame.z1, frame.z1), ) elif vp != None: draw_solid_polygon( vp, map(transform_posn(frame), p), Posn(0, 0), Rgb(frame.z1, frame.z1, frame.z1), ) corner_bb = Rune(corner_bbf) def black_bbf(vp, frame): """ A rune without any blank space. """ p = [ Posn(0, 0), Posn(viewport_size, 0), Posn(viewport_size, viewport_size), Posn(0, viewport_size), ] if is_list_or_tuple(vp[0]): for count, port in enumerate(vp): draw_solid_polygon( port, map(transform_posn(frame), p), Posn( (0.3 - frame.z1) * (spread * (((2 * count) / (len(vp) - 1)) - 1)), 0 ), Rgb(frame.z1, frame.z1, frame.z1), ) elif vp != None: draw_solid_polygon( vp, map(transform_posn(frame), p), Posn(0, 0), Rgb(frame.z1, frame.z1, frame.z1), ) black_bb = Rune(black_bbf) def spiral_bbf(vp, frame): """ A rune that definitely looks like a spiral. """ theta_max = 30 offset = 0.1 angle = 0 p = [] while angle < theta_max: p.append( Posn( (offset + angle / theta_max) * math.cos(angle), (offset + angle / theta_max) * math.sin(angle), ) ) angle += offset if is_list_or_tuple(vp[0]): for count, port in enumerate(vp): draw_solid_polygon( port, map(transform_posn(frame), map(center_and_fill, p)), Posn( (0.3 - frame.z1) * (spread * (((2 * count) / (len(vp) - 1)) - 1)), 0 ), Rgb(frame.z1, frame.z1, frame.z1), ) elif vp != None: draw_solid_polygon( vp, map(transform_posn(frame), map(center_and_fill, p)), Posn(0, 0), Rgb(frame.z1, frame.z1, frame.z1), ) spiral_bb = Rune(spiral_bbf) def circle_bbf(vp, frame): """ A perfect circle rune! """ unit = 50 p = [] angle = 0 while angle < 2 * math.pi: p.append(Posn(math.cos(angle), math.sin(angle))) angle += unit / viewport_size if is_list_or_tuple(vp[0]): for count, port in enumerate(vp): draw_solid_polygon( port, map(transform_posn(frame), map(center_and_fill, p)), Posn( (0.3 - frame.z1) * (spread * (((2 * count) / (len(vp) - 1)) - 1)), 0 ), Rgb(frame.z1, frame.z1, frame.z1), ) elif vp != None: draw_solid_polygon( vp, map(transform_posn(frame), map(center_and_fill, p)), Posn(0, 0), Rgb(frame.z1, frame.z1, frame.z1), ) circle_bb = Rune(circle_bbf) def quarter_bbf(vp, frame): """ Quarter of a circle """ unit = 50 p = [] angle = 0 while angle < math.pi / 2: p.append(Posn(-2 * math.cos(angle) + 1, -2 * math.sin(angle) + 1)) angle += unit / viewport_size p.append(Posn(1, -1)) # Top corner p.append(Posn(1, 1)) # Center of circle if is_list_or_tuple(vp[0]): for count, port in enumerate(vp): draw_solid_polygon( port, map(transform_posn(frame), map(center_and_fill, p)), Posn( (0.3 - frame.z1) * (spread * (((2 * count) / (len(vp) - 1)) - 1)), 0 ), Rgb(frame.z1, frame.z1, frame.z1), ) elif vp != None: draw_solid_polygon( vp, map(transform_posn(frame), map(center_and_fill, p)), Posn(0, 0), Rgb(frame.z1, frame.z1, frame.z1), ) quarter_bb = Rune(quarter_bbf) def pentagram_bbf(vp, frame): """ A star-shaped rune. How cool is that? """ unit_offset = viewport_size / 2 s1 = math.sin(2 * math.pi / 5) * unit_offset c1 = math.cos(2 * math.pi / 5) * unit_offset s2 = math.sin(4 * math.pi / 5) * unit_offset c2 = math.cos(math.pi / 5) * unit_offset a = s2 / (s1 + s2) a_ = 1 - a p = [ Posn(-s1 + unit_offset, -c1 + unit_offset), Posn(s1 + unit_offset, -c1 + unit_offset), Posn(-s2 + unit_offset, c2 + unit_offset), Posn(unit_offset, 0), Posn(s2 + unit_offset, c2 + unit_offset), ] p = [ p[0], p[3] * a + p[2] * a_, p[2], p[1] * a + p[2] * a_, p[4], p[2] * a + p[1] * a_, p[1], p[4] * a + p[3] * a_, p[3], p[2] * a + p[3] * a_, ] if is_list_or_tuple(vp[0]): for count, port in enumerate(vp): draw_solid_polygon( port, map(transform_posn(frame), p), Posn( (0.3 - frame.z1) * (spread * (((2 * count) / (len(vp) - 1)) - 1)), 0 ), Rgb(frame.z1, frame.z1, frame.z1), ) elif vp != None: draw_solid_polygon( vp, map(transform_posn(frame), p), Posn(0, 0), Rgb(frame.z1, frame.z1, frame.z1), ) pentagram_bb = Rune(pentagram_bbf) def heart_bbf(vp, frame): """ A heart-shaped rune. Good to spread positivity :) """ k = math.sqrt(2) / 2 p = [ Posn(viewport_size / 2, (1 - k) / (1 + 3 * k) * viewport_size), Posn( (1 - k) / (1 + k) * viewport_size / 2, (1 + k) / (1 + 3 * k) * viewport_size ), Posn(viewport_size / 2, viewport_size), Posn( viewport_size - (1 - k) / (1 + k) * viewport_size / 2, (1 + k) / (1 + 3 * k) * viewport_size, ), ] # Draws a kite if is_list_or_tuple(vp[0]): for count, port in enumerate(vp): draw_solid_polygon( port, map(transform_posn(frame), p), Posn( (0.3 - frame.z1) * (spread * (((2 * count) / (len(vp) - 1)) - 1)), 0 ), Rgb(frame.z1, frame.z1, frame.z1), ) elif vp != None: draw_solid_polygon( vp, map(transform_posn(frame), p), Posn(0, 0), Rgb(frame.z1, frame.z1, frame.z1), ) # Draws the top of the heart heart_circle = stack_frac( 2 / (1 + 3 * k), quarter_turn_right(stack_frac(k / (1 + k), blank_bb, circle_bb)), blank_bb, ) heart_circle.show(vp, frame) flip_horiz(heart_circle).show(vp, frame) heart_bb = Rune(heart_bbf) def rcross_bbf(vp, frame): """ An upper triangular rune with some small part mirrored to its diagonal. """ p1 = [ Posn(0, 0), Posn(viewport_size / 4, viewport_size / 4), Posn(3 * viewport_size / 4, viewport_size / 4), Posn(3 * viewport_size / 4, 3 * viewport_size / 4), Posn(viewport_size, viewport_size), Posn(viewport_size, 0), ] p2 = [ Posn(viewport_size / 4, viewport_size / 4), Posn(viewport_size / 4, 3 * viewport_size / 4), Posn(3 * viewport_size / 4, 3 * viewport_size / 4), ] if is_list_or_tuple(vp[0]): for count, port in enumerate(vp): draw_solid_polygon( port, map(transform_posn(frame), p1), Posn( (0.3 - frame.z1) * (spread * (((2 * count) / (len(vp) - 1)) - 1)), 0 ), Rgb(frame.z1, frame.z1, frame.z1), ) draw_solid_polygon( port, map(transform_posn(frame), p2), Posn( (0.3 - frame.z1) * (spread * (((2 * count) / (len(vp) - 1)) - 1)), 0 ), Rgb(frame.z1, frame.z1, frame.z1), ) elif vp != None: draw_solid_polygon( vp, map(transform_posn(frame), p1), Posn(0, 0), Rgb(frame.z1, frame.z1, frame.z1), ) draw_solid_polygon( vp, map(transform_posn(frame), p2), Posn(0, 0), Rgb(frame.z1, frame.z1, frame.z1), ) rcross_bb = Rune(rcross_bbf) def ribbon_bbf(vp, frame): """ A ribbon-shaped rune, similar to a beautiful spiral. """ theta_max = 30 thickness = -1 / theta_max unit = 0.1 p = [] angle = 0 # Make ribbon while angle < theta_max: p.append( Posn( (angle / theta_max) * math.cos(angle), (angle / theta_max) * math.sin(angle), ) ) angle += unit # Close it while angle > 0: p.append( Posn( abs(math.cos(angle) * thickness) + (angle / theta_max * math.cos(angle)), abs(math.sin(angle) * thickness) + (angle / theta_max * math.sin(angle)), ) ) angle -= unit if is_list_or_tuple(vp[0]): for count, port in enumerate(vp): draw_solid_polygon( port, map(transform_posn(frame), map(center_and_fill, p)), Posn( (0.3 - frame.z1) * (spread * (((2 * count) / (len(vp) - 1)) - 1)), 0 ), Rgb(frame.z1, frame.z1, frame.z1), ) elif vp != None: draw_solid_polygon( vp, map(transform_posn(frame), map(center_and_fill, p)), Posn(0, 0), Rgb(frame.z1, frame.z1, frame.z1), ) ribbon_bb = Rune(ribbon_bbf) def nova_bbf(vp, frame): """ Two small sail-shaped runes combined into an L-shaped rune. """ p = [ Posn(viewport_size / 2, 0), Posn(viewport_size / 4, viewport_size / 2), Posn(viewport_size, viewport_size / 2), Posn(viewport_size / 2, viewport_size / 4), ] if is_list_or_tuple(vp[0]): for count, port in enumerate(vp): draw_solid_polygon( port, map(transform_posn(frame), p), Posn( (0.3 - frame.z1) * (spread * (((2 * count) / (len(vp) - 1)) - 1)), 0 ), Rgb(frame.z1, frame.z1, frame.z1), ) elif vp != None: draw_solid_polygon( vp, map(transform_posn(frame), p), Posn(0, 0), Rgb(frame.z1, frame.z1, frame.z1), ) nova_bb = Rune(nova_bbf) # Frame transformation factory def process_frame(op, frame): """ A frame transformation factory. Nothing to see here. """ p0 = frame.orig p1 = frame.x p2 = frame.y z1 = frame.z1 z2 = frame.z2 if op == "bottom_frac": return lambda frac: Frame( p0 + scale_vect(1 - frac, p2), p1, scale_vect(frac, p2), z1, z2 ) elif op == "top_frac": return lambda frac: Frame(p0, p1, scale_vect(frac, p2), z1, z2) # To devs, probably unused elif op == "left": return Frame(p0, scale_vect(1 / 2, p1), p2, z1, z2) # To devs, probably unused elif op == "right": return Frame(p0 + scale_vect(1 / 2, p1), scale_vect(1 / 2, p1), p2, z1, z2) elif op == "flip_horiz": return Frame(p0 + p1, scale_vect(-1, p1), p2, z1, z2) elif op == "flip_vert": return Frame(p0 + p2, p1, scale_vect(-1, p2), z1, z2) elif op == "reduce_2": # unused in original raise NotImplementedError("reduce_2 is not implemented") elif op == "rotate": def function(rad): cos_theta = math.cos(rad) sin_theta = math.sin(rad) def rotate_posn(p): return Posn( cos_theta * p.x + sin_theta * p.y, cos_theta * p.y - sin_theta * p.x ) half_gradient = scale_vect(1 / 2, p1 + p2) center = p0 + half_gradient + rotate_posn(scale_vect(-1, half_gradient)) return Frame(center, rotate_posn(p1), rotate_posn(p2), z1, z2) return function elif op == "rotate90": return Frame(p0 + p1, p2, scale_vect(-1, p1), z1, z2) elif op == "deep_frac": return lambda frac: Frame(p0, p1, p2, z1 + ((z2 - z1) * frac), z2) elif op == "shallow_frac": return lambda frac: Frame(p0, p1, p2, z1, z1 + ((z2 - z1) * frac)) elif op == "scale_independent": def function(ratio_x, ratio_y): d_xy = (p1 * (1 - ratio_x) + p2 * (1 - ratio_y)) * 0.5 center = p0 + d_xy return Frame(center, p1 * ratio_x, p2 * ratio_y, z1, z2) return function elif op == "translate": return lambda x, y: Frame( p0 + scale_vect(x, p1) + scale_vect(y, p2), p1, p2, z1, z2 ) # Basic rune combinations @type_checker((float, Rune, Rune)) def stack_frac(frac, rune1, rune2): """ Stacks `rune1` on top and `rune2` at the bottom with the vertical proportion of `rune1` equals to `frac`. """ def function(vp, frame): if not 0 <= frac <= 1: raise ValueError("stack_frac: 0 <= frac <= 1 is required") else: uf = process_frame("top_frac", frame)(frac) lf = process_frame("bottom_frac", frame)(1 - frac) rune1.show(vp, uf) rune2.show(vp, lf) return Rune(function) @type_checker((Rune, Rune)) def stack(rune1, rune2): """ Stacks `rune1` on top and `rune2` at the bottom with equal proportion. """ return stack_frac(1 / 2, rune1, rune2) @type_checker((float, Rune)) def rotate(rad, rune): """ Rotate `rune` by a certain angle `rad`, in radians, counterclockwise. """ def function(vp, frame): rune.show(vp, process_frame("rotate", frame)(rad)) return Rune(function) @type_checker((Rune,)) def eighth_turn_left(rune): """ Rotate `rune` by pi/4 radians counterclockwise. In other words, 45 degrees to the left. """ return rotate(math.pi / 4, rune) @type_checker((Rune,)) def quarter_turn_right(rune): """ Rotate `rune` by pi/2 radians clockwise. In other words, 90 degrees to the right. """ def function(vp, frame): rune.show(vp, process_frame("rotate90", frame)) return Rune(function) @type_checker((Rune,)) def flip_vert(rune): """ Flips `rune` vertically. """ def function(vp, frame): rune.show(vp, process_frame("flip_vert", frame)) return Rune(function) @type_checker((Rune,)) def flip_horiz(rune): """ Flips `rune` horizontally. """ def function(vp, frame): rune.show(vp, process_frame("flip_horiz", frame)) return Rune(function) @type_checker((float, Rune, Rune)) def overlay_frac(frac, rune1, rune2): """ Overlay `rune1` on top and `rune2` at the bottom with the z-axis proportion of `rune1` equals `frac`. Basically a 3D-representation of stack_frac. """ def function(vp, frame): if not 0 <= frac <= 1: raise ValueError("overlay_frac: 0 <= frac <= 1 is required") else: df = process_frame("deep_frac", frame)(frac) sf = process_frame("shallow_frac", frame)(frac) rune2.show(vp, df) rune1.show(vp, sf) return Rune(function) @type_checker((Rune, Rune)) def overlay(rune1, rune2): """ Overlay `rune1` on top and `rune2` at the bottom with equal z-axis proportion. """ return overlay_frac(1 / 2, rune1, rune2) @type_checker((float, float, Rune)) def scale_independent(ratio_x, ratio_y, rune): """ Scales `rune`'s width by `ratio_x` and its height by `ratio_y`. The scaling anchor point is the center of the rune. """ def function(vp, frame): rune.show(vp, process_frame("scale_independent", frame)(ratio_x, ratio_y)) return Rune(function) @type_checker((float, Rune)) def scale(ratio, rune): """ Scales `rune` by `ratio`, maintaining aspect ratio. """ return scale_independent(ratio, ratio, rune) @type_checker((float, float, Rune)) def translate(x, y, rune): """ Translates `rune` by `x` * 100% of the screen to the right and by `y` * 100% of the screen downwards. Positive `x` means to the right, positive `y` means downwards. """ def function(vp, frame): rune.show(vp, process_frame("translate", frame)(x, y)) return Rune(function) @type_checker((Rune,)) def turn_upside_down(rune): """ Turns `rune` upside down, a.k.a. 180 degrees. """ return quarter_turn_right(quarter_turn_right(rune)) @type_checker((Rune,)) def quarter_turn_left(rune): """ Turns `rune` to the left by 90 degrees. In other word, pi/2 radians counterclockwise. """ return turn_upside_down(quarter_turn_right(rune)) @type_checker((Rune, Rune)) def beside(rune1, rune2): """ Puts `rune1` and `rune2` beside each other. Both runes share the same horizontal proportion. """ return quarter_turn_right(stack(quarter_turn_left(rune2), quarter_turn_left(rune1))) @type_checker((Rune,)) def make_cross(rune): """ Creates a cross out of the base `rune`. """ return stack( beside(quarter_turn_right(rune), turn_upside_down(rune)), beside(rune, quarter_turn_left(rune)), ) # TODO refactor type_checker, pat is a function, but function is not a keyword or built-in. def repeat_pattern(n, pat, pic): """ Repeats a pattern, pat, on a rune, pic, by n times. """ if n == 0: return pic else: return pat(repeat_pattern(n - 1, pat, pic)) @type_checker((int, Rune)) def stackn(n, rune): """ Stacks n identical `rune` vertically. The vertical proportions of the runes will be all equal. """ if n == 1: return rune else: return stack_frac(1 / n, rune, stackn(n - 1, rune)) ########################################################################## # Functions below this line are deprecated! You don't have to check them # ########################################################################## def hollusion(rune, ports=None): """ Creates a hollusion of `rune`. """ global active_hollusion frequency = 2 MAX_X = round(viewport_size) MAX_Y = round(viewport_size) num = ports if ports == None or ports <= 2: num = 9 buffers = list( map( lambda p: graphics.open_pixmap("buffer", viewport_size, viewport_size), range(num), ) ) def animation(cmd=None): ports = buffers kill = False curr = -1 dir = 1 def Self(msg): nonlocal kill nonlocal curr nonlocal Self nonlocal dir if msg == "next": curr += dir if curr == num or curr < 0: dir = -dir curr += 2 * dir graphics.show_viewport(buffers[curr]) if not kill: vp[0].after( round(1000 / (frequency * len(ports))), lambda: Self("next") ) elif msg == "kill": kill = True elif msg == "buffer": return ports else: return return Self rune.show(buffers, unit_frame) if active_hollusion != None: active_hollusion("kill") active_hollusion = animation() active_hollusion("next") return active_hollusion def anaglyph(rune): """ Creates an anaglyph of `rune`. Very advisable to use 3D glasses for the effects. """ if graphics.PIL_available: MAX_X = round(viewport_size) MAX_Y = viewport_size stereo = vp depth = graphics.open_pixmap("Depthmap Viewport", viewport_size, viewport_size) def get_depth(x, y, pix): return pix[x, y][0] rune.show([lp, rp], unit_frame) lp_pix = graphics.get_pixels(lp) rp_pix = graphics.get_pixels(rp) pixels = graphics.get_pixels(stereo) for y in range(MAX_Y): for x in range(MAX_X): l = get_depth(x, y, lp_pix) r = get_depth(x, y, rp_pix) pixels[x, y] = (r, l, l) graphics.pixels_to_canvas(stereo) else: print("PIL does not appear to be available") def function_to_painter(depth_fun): """ Converts a depth function depth_fun into a rune. """ tolerance = 1 / spread def get_depth(x, y, dir, frame): # lp -> dir = -1, rp -> dir = 1 result = 1 for c in range(0, spread): ox = round(x + (dir * ((-0.3 * spread) + c))) if 0 <= ox < viewport_size: curr = depth_fun(round(ox), round(y)) if curr != 1: curr = frame.z1 + ((frame.z2 - frame.z1) * curr) d = abs(curr - c / spread) if d < tolerance: result = curr return result def painter(vp, frame): def ana_out_loop(port, count): inverse_transform = inverse_transform_posn(frame) tgtpixels = graphics.get_pixels(port) size = graphics.get_image_size(port) MAX_X = size[0] MAX_Y = size[1] tgtpixels = graphics.get_pixels(port) for y in range(MAX_Y): for x in range(MAX_X): posn = inverse_transform(Posn(x, y)) col = get_depth(posn.x, posn.y, count, frame) col = round(min(col, 1) * 255) if col < 255: tgtpixels[x, y] = ( min(col, tgtpixels[x, y][0]), min(col, tgtpixels[x, y][1]), min(col, tgtpixels[x, y][2]), ) graphics.pixels_to_canvas(port) if is_list_or_tuple(vp[0]): for count, port in enumerate(vp): ana_out_loop(port, ((2 * count) / (len(vp) - 1) - 1)) else: inverse_transform = inverse_transform_posn(frame) tgtpixels = graphics.get_pixels(vp) size = graphics.get_image_size(vp) MAX_X = size[0] MAX_Y = size[1] for y in range(MAX_Y): for x in range(MAX_X): posn = inverse_transform(Posn(x, y)) color = depth_fun(posn.x, posn.y) if color != 1: color = frame.z1 + ((frame.z2 - frame.z1) * color) color = round(min(color, 1) * 255) if color < 255: # assuming that white is the transparency color tgtpixels[x, y] = ( min(color, tgtpixels[x, y][0]), min(color, tgtpixels[x, y][1]), min(color, tgtpixels[x, y][2]), ) graphics.pixels_to_canvas(vp) return painter def image_to_painter(filename): """ Converts an image file to a rune. Probably a work in progress. """ img = graphics.load_image(filename) tolerance = 1 / spread limit = 0.86 # Process def painter(vp, frame): if is_list_or_tuple(vp[0]): def get_depth(x, y, dir): global spread nonlocal pixels for c in range(spread): ox = round(x + dir * (-0.3 * spread + c)) if 0 <= ox < viewport_size: if ( type(pixels[ox, y]) is int ): # this is a workaround for black/white pictures curr = pixels[ox, y] else: curr = pixels[ox, y][0] d = abs(curr - 255 * c / spread) if d <= tolerance * 255: return curr return 255 def ana_out_loop(port, count): nonlocal img size = graphics.get_image_size(img) MAX_X = size[0] MAX_Y = size[1] tsize = graphics.get_image_size(port) TMAX_X = tsize[0] TMAX_Y = tsize[1] tgtpixels = graphics.get_pixels(port) inv_transform = inverse_transform_posn(frame) for y in range(TMAX_Y): for x in range(TMAX_X): orig = inv_transform(Posn(x, y)) rx = round(orig.x) ry = round(orig.y) if 0 <= rx < MAX_X and 0 <= ry < MAX_Y: # within bounds col = get_depth(rx, ry, count) if col > 255 * limit: col = 999 else: col = round( frame.z1 * 255 + (frame.z2 - frame.z1) * col ) if col <= 255: tgtpixels[x, y] = ( min(col, tgtpixels[x, y][0]), min(col, tgtpixels[x, y][1]), min(col, tgtpixels[x, y][2]), ) graphics.pixels_to_canvas(port) pixels = graphics.get_pixels(img) for count, port in enumerate(vp): ana_out_loop(port, ((2 * count) / (len(vp) - 1) - 1)) else: transform = inverse_transform_posn(frame) graphics.blit_pixels( vp, transform, graphics.get_pixels(img), graphics.get_image_size(vp), graphics.get_image_size(img), True, frame.z1, frame.z2, ) # block level image transfer return painter def stereogram(painter): """ Generates a stereogram of a rune. """ E = 300 # distance between eyes in pixels D = 600 # distance between eyes and image plane in pixels delta = 40 # stereo separation MAX_X = round(viewport_size) MAX_Y = viewport_size MAX_Z = 0 CENTRE = round(MAX_X / 2) stereo = vp pixels = graphics.get_pixels(stereo) depthmap = graphics.open_pixmap("temp", viewport_size, viewport_size) depth_pix = graphics.get_pixels(depthmap) painter(depthmap, unit_frame) Infinity = float("inf") depth_pix = graphics.get_pixels(depthmap) def get_depth(x, y): if (0) < x < (MAX_X): return -100 * depth_pix[x, y][0] / 255 - 400 else: return -500 for y in range(MAX_Y): link_left = {} link_right = {} colours = {} for x in range(MAX_X): z = get_depth(x, y) s = delta + z * ( E / (z - D) ) # Determine distance between intersection of lines of sight on image left = x - round(s / 2) # x is integer, left is integer right = left + round(s) # right is integer if left > 0 and right < MAX_X: if (not (left in link_right) or s < link_right[left]) and ( not (right in link_left) or s < link_left[right] ): link_right[left] = round(s) link_left[right] = round(s) # Constraint resolution for x in range(MAX_X): try: s = link_left[x] except KeyError: s = Infinity if s != Infinity: s = x d = None if x > s: d = link_right[x - s] else: d = Infinity if s == Infinity or s > d: link_left[x] = 0 # Drawing step for x in range(MAX_X): s = link_left[x] # should be valid for [0, MAX_X - 1] try: colour = colours[x - s] except KeyError: colour = ( round(random() * 10 / 9 * 255), round(random() * 10 / 9 * 255), round(random() * 10 / 9 * 255), ) pixels[x, y] = colour colours[x] = colour graphics.pixels_to_canvas(stereo) def save_image(filename): """ Saves a rune into an image file. """ graphics.saveImage(vp, filename) def save_hollusion(filename): """ Saves a hollusion into a GIF file. filename is a string without ".gif" at the back. """ if graphics.PIL_available: if active_hollusion == None: raise ("No hollusion active") else: filename += ".gif" frames = list( map(lambda vp: graphics.get_image(vp), active_hollusion("buffer")) ) rev = frames[1 : len(frames) - 1] rev.reverse() frames.extend(rev) PyGif.saveAnimated(filename, frames, 1 / len(frames)) else: print("PIL does not appear to be available")
09-01
参考下面时间戳的命名方式代码 import logging from maix import camera, display, image, nn, app, uart, time import requests import json import os import threading from datetime import datetime # 日志配置 logging.basicConfig( level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s', handlers=[ logging.FileHandler("/root/operation.log"), logging.StreamHandler() ] ) logger = logging.getLogger("MAIX_PRO") # 状态定义 class SystemState: NORMAL = 0 # 正常检测模式 OBJECT_DETECTED = 1 # 物体检测处理中 SPECIAL_HANDLING = 2# 特殊处理模式(标签19) WAIT_FOR_LABEL1 = 3 # 等待标签1状态 # 模型加载 try: detector = nn.YOLOv5(model="/root/models/mymodels/model_195130.mud", dual_buff=True) logger.info("YOLOv5 model loaded") except Exception as e: logger.critical(f"Model load failed: {str(e)}") exit(1) # 保存目录 # SAVE_DIR = "/root/models/mymodel/" SAVE_DIR = "/boot/Pictures/" os.makedirs(SAVE_DIR, exist_ok=True) # 硬件初始化(调整为硬件支持的分辨率) try: cam = camera.Camera(width=1080, height=640) # 使用标准VGA分辨率 logger.debug(f"Camera resolution: {cam.width()}x{cam.height()}") except RuntimeError as e: logger.critical(f"Camera init failed: {str(e)}") exit(1) disp = display.Display() # UART初始化 device = "/dev/ttyS0" serial0 = uart.UART(device, 38400) logger.info("UART initialized") # 登录获取token login_url = "http://111.230.114.23/api/user/login" headers_login = {'Content-Type': 'application/json'} login_data = {"userAccount": "lanyating", "userPassword": 12345678} json_data = json.dumps(login_data) try: login_response = requests.post(login_url, data=json_data, headers=headers_login) response_json = login_response.json() token = response_json.get('data') if token: logger.info(f"Login successful, token obtained: {token[:10]}...") # 只显示部分token以保护隐私 else: logger.error(f"Login failed: No token returned in response") exit(1) except Exception as e: logger.critical(f"Login failed: {str(e)}") exit(1) def send_uart(data): try: serial0.write_str(data + "\n") logger.info(f"UART sent: {data}") except Exception as e: logger.error(f"UART send failed: {str(e)}") class OperationController: def __init__(self): self.state = SystemState.NORMAL self.current_label = None self.last_detect_time = 0 self.upload_complete = False self.lock = threading.Lock() self.timers = [] # 初始发送forward self.send_uart("forward") # 初始化 photo_url 和 data_url self.photo_url = "http://111.230.114.23/api/file/upload" self.data_url = "http://111.230.114.23/api/data/add" # 确保 token 在整个类中可用 self.token = token def send_uart(self, command): try: serial0.write_str(command + "\n") logger.info(f"UART sent: {command}") except Exception as e: logger.error(f"UART send failed: {str(e)}") def save_and_upload(self, img, label): try: # 生成文件名 timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename = f"{SAVE_DIR}{label}_{timestamp}.jpg" # 保存图片 if img.save(filename, quality=90): logger.info(f"Image saved: {filename}") # 同步上传 with open(filename, 'rb') as file: files = { 'file': ('image.jpg', file, 'image/jpeg') } params = { 'biz': 'plant_picture', } headers = { "token": self.token } logger.info(f"Uploading {filename} with label {label}, Token: {self.token[:10]}...") response = requests.post( self.photo_url, files=files, headers=headers, params=params ) if response.json().get('code') == 0 : logger.info(f"Upload success: {filename}, Response: {response.text}") return response.json().get('data') else: logger.warning(f"Upload failed: {response.text}") else: logger.error("Image save failed") except Exception as e: logger.error(f"Capture failed: {str(e)}") return None def save_data(self, data): try: params = [{ "deviceName": 1, "plantId": 1, "growthStage": "flowering", "healthStage": "healthy", "height": "5", "crownWidth": "5", "humidity": '', "ph": '', "dan": '', "lin": '', "jia": '', "photoUrl": data, "notes": "" }] headers = { "token": self.token } response = requests.post( self.data_url, headers=headers, json=params ) logger.info(f"Response: {data}") if response.json().get('code') == 0 : logger.info(f"Data save success: {response.text}") else: logger.warning(f"Data save failed: {response.text}") except Exception as e: logger.error(f"Data upload error: {str(e)}") def handle_detection(self, objs, img): with self.lock: current_time = time.time() # 状态机逻辑 if self.state == SystemState.NORMAL: for obj in objs: if obj.class_id == 19: logger.info("Special label 19 detected") self.state = SystemState.SPECIAL_HANDLING self.send_uart("stop") # 1秒后保存并上传 def delayed_save(): print("开始上传") data = self.save_and_upload(img, 19) print("上传成功") if data: self.save_data(data) self.send_uart("back") self.add_timer(1.0, delayed_save) break elif 1 <= obj.class_id <= 18: logger.info(f"Label {obj.class_id} detected") self.state = SystemState.OBJECT_DETECTED self.send_uart("stop") # 1秒后保存并上传 def delayed_save(): print("开始上传") data = self.save_and_upload(img, obj.class_id) print("上传成功") if data: self.save_data(data) self.add_timer(1.0, delayed_save) # 2秒后发送forward def delayed_forward(): self.send_uart("forward") self.state = SystemState.NORMAL self.add_timer(2.0, delayed_forward) break elif self.state == SystemState.SPECIAL_HANDLING: # 等待上传完成 pass # 在同步上传的情况下不需要等待标志位 elif self.state == SystemState.WAIT_FOR_LABEL1: for obj in objs: if obj.class_id == 1: logger.info("Label1 after special handling") self.send_uart("stop") self.add_timer(1.0, lambda: self.send_uart("forward")) self.state = SystemState.NORMAL break def add_timer(self, delay, callback): timer = threading.Timer(delay, callback) timer.start() self.timers.append(timer) def cleanup(self): for timer in self.timers: timer.cancel() logger.info("System cleanup completed") # 主控制实例 controller = OperationController() # 创建 Color 对象 red_color = image.Color(255, 0, 0) # 定义标准红色 # 主循环 try: while not app.need_exit(): try: img = cam.read() except Exception as e: logger.error(f"摄像头读取失败: {str(e)}") continue # 执行检测 try: objs = detector.detect(img, conf_th=0.6) except Exception as e: logger.error(f"目标检测失败: {str(e)}") disp.show(img) continue # 处理结果 if len(objs) > 0: controller.handle_detection(objs, img) # 显示画面 for obj in objs: # 绘制检测框(使用位置参数) img.draw_rect( obj.x, obj.y, obj.w, obj.h, red_color, # 颜色参数位置 2 # 线宽参数位置 ) # 绘制标签文本 img.draw_string( obj.x, obj.y, f"{detector.labels[obj.class_id]} {obj.score:.2f}", scale=0.5, color=red_color ) disp.show(img) except KeyboardInterrupt: logger.info("用户中断") except Exception as e: logger.critical(f"致命错误: {str(e)}") finally: controller.cleanup() logger.info("系统关闭") ******************************************************************************** cvi_bin_isp message gerritId: 97347 commitId: fc6ce647 md5: d6db2297ddfd44e8252c1f3f888f47b2 sensorNum 1 sensorName0 5440577 PQBIN message gerritId: commitId: fc6ce647 md5: d6db2297ddfd44e8252c1f3f888f47b2 sensorNum 1 sensorName0 5440577 author: lxowalle desc: os04a10 createTime: 2024-08-30 11:00:43version: tool Version: v3.0.5.48 mode: ******************************************************************************** [SAMPLE_COMM_ISP_Thread]-95: ISP Dev 0 running! 0 R:1257 B:3920 CT:2788 1 R:1588 B:2858 CT:3968 2 R:2169 B:1819 CT:7154 Golden 1588 1024 2858 wdrLEOnly:1 time: 1 Traceback (most recent call last): File "/tmp/maixpy_run/main.py", line 59, in <module> timestamp = f"{str(now[0])[-2:]}_{str(now[1]).zfill(2)}{str(now[2]).zfill(2)}_{str(now[3]).zfill(2)}{str(now[4]).zfill(2)}" ~~~^^^ TypeError: 'maix._maix.time.DateTime' object is not subscriptable maix multi-media driver released. ISP Vipipe(0) Free pa(0x8a6ee000) va(0x0x3fbeaf7000) program exit failed. exit code: 1.
07-06
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值