import pickle import sys import cv2 from PIL import Image from OpenGL.GL import * from OpenGL.GLU import * from OpenGL.GLUT import * import numpy as np # define shader code vertex_code=''' uniform float scale; attribute vec2 position; attribute vec4 color; varying vec4 v_color; attribute vec2 TexCoordIn; varying vec2 TexCoordOut; void main() { gl_Position = vec4(position*scale, 0.0, 1.0); v_color = color; TexCoordOut = TexCoordIn; }''' fragment_code=''' varying vec4 v_color; varying vec2 TexCoordOut; uniform sampler2D Texture; uniform vec2 originPosition; uniform vec2 targetPosition; vec2 curveWarp(vec2 textureCoord, vec2 originPosition, vec2 targetPosition, float radius) { vec2 offset = vec2(0.0); vec2 result = vec2(0.0); vec2 direction = targetPosition - originPosition; float infect = distance(textureCoord, originPosition)/radius; infect = 1.0 - infect; infect = clamp(infect, 0.0, 1.0); offset = direction * infect; result = textureCoord - offset; return result; } void main() { vec2 coordinate = vec2(0.0); float radius = 0.5; coordinate = curveWarp(TexCoordOut,originPosition,targetPosition,radius); gl_FragColor = v_color*0.000000000001 + texture2D(Texture, coordinate); }''' #define useful function def display(): glClear(GL_COLOR_BUFFER_BIT) glDrawArrays(GL_TRIANGLE_STRIP, 0, 4) glutSwapBuffers() def reshape(width,height): glViewport(0, 0, width, height) #step1 init the context def init(img_path): image = Image.open(img_path) glutInit() glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB) glutCreateWindow('Hello world!') glutReshapeWindow(image.width,image.height) glutReshapeFunc(reshape) glutDisplayFunc(display) return image #step 2 def initShaderProgram(): program = glCreateProgram() vertex = glCreateShader(GL_VERTEX_SHADER) fragment = glCreateShader(GL_FRAGMENT_SHADER) # Set shaders source glShaderSource(vertex, vertex_code) glShaderSource(fragment, fragment_code) # Compile shaders glCompileShader(vertex) glCompileShader(fragment) fragSuccess = glGetShaderiv(fragment, GL_COMPILE_STATUS) vertSuccess = glGetShaderiv(vertex, GL_COMPILE_STATUS) print("vertext shader compile success [%s]" % (vertSuccess,)) print("fragment shader compile success [%s]" % (fragSuccess,)) if vertSuccess == 0: print(glGetShaderInfoLog(vertex)) sys.exit(0) if fragSuccess == 0: print(glGetShaderInfoLog(fragment)) sys.exit(0) glAttachShader(program, vertex) glAttachShader(program, fragment) glLinkProgram(program) linksucc=glGetProgramiv(program, GL_LINK_STATUS) print("link program success [%s]" % (linksucc,)) glUseProgram(program) return program #step 3.1 optional setup texture def getTextureFromFile(image_file): #convert file to bytes image = image_file.transpose(Image.FLIP_TOP_BOTTOM) image = image.convert("RGBA") byteImage =np.array(list(image.getdata()), np.uint8) #setup texture texIndex=glGenTextures(1) glEnable( GL_TEXTURE_2D ) glBindTexture(GL_TEXTURE_2D,texIndex) glPixelStorei(GL_UNPACK_ALIGNMENT,1) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) #make the texture the default glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texIndex, 0) glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,image.width,image.height,0,GL_RGBA,GL_UNSIGNED_BYTE,byteImage) return texIndex #step 4.1 optional get the image from BufferFrame def saveImageFromFBO(width, height, output_img_path): glReadBuffer(GL_COLOR_ATTACHMENT0) glPixelStorei(GL_PACK_ALIGNMENT, 1) data = glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE) image = Image.new("RGB", (width, height), (0, 0, 0)) image.frombytes(data) image = image.transpose(Image.FLIP_TOP_BOTTOM) image.save(output_img_path) #step 4 optional if need to OSR generate a BufferFrame def setupSelfDefineFBO(program, image, data, output_img_path): fbWidth, fbHeight = image.width, image.height # Setup framebuffer framebuffer = glGenFramebuffers(1) glBindFramebuffer(GL_FRAMEBUFFER, framebuffer) # # Setup colorbuffer # colorbuffer = glGenRenderbuffers(1) # glBindRenderbuffer(GL_RENDERBUFFER, colorbuffer) # glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, fbWidth, fbHeight) # glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorbuffer) # Setup depthbuffer depthbuffer = glGenRenderbuffers (1) glBindRenderbuffer (GL_RENDERBUFFER,depthbuffer) glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT, image.width, image.height) glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthbuffer) #first init VBO, then other parameters buffer = glGenBuffers(1) # Request a buffer slot from GPU glBindBuffer(GL_ARRAY_BUFFER, buffer) # Make this buffer the default one glBufferData(GL_ARRAY_BUFFER, data.nbytes, data, GL_DYNAMIC_DRAW) # Upload data # Create texture to render to # glBufferData(GL_FRAMEBUFFER, data.nbytes, data, GL_DYNAMIC_DRAW) loc = glGetAttribLocation(program, "position") #get the index of the attribute in program glEnableVertexAttribArray(loc) #allow this attribute decide by index can be use stride = data.strides[0] #define how to read buffer offset = ctypes.c_void_p(0) #define the offset where the data begin in buffer glVertexAttribPointer(loc, 2, GL_FLOAT, False, stride, offset) offset = ctypes.c_void_p(data.dtype["position"].itemsize) loc = glGetAttribLocation(program, "color") glEnableVertexAttribArray(loc) glVertexAttribPointer(loc, 4, GL_FLOAT, False, stride, offset) #setup other parameters loc = glGetUniformLocation(program, "scale") glUniform1f(loc, 1.0) # originPosition = glGetUniformLocation(program, "originPosition") # glUniform2f(originPosition, 0.5, 0.5) # # targetPosition = glGetUniformLocation(program, "targetPosition") # glUniform2f(targetPosition, 0.47, 0.47) # # glUniform2f(targetPosition, 0.5, 0.5) # following code to bind uniform texture if needed aTexture = getTextureFromFile(image) glViewport(0, 0, fbWidth, fbHeight) glActiveTexture(GL_TEXTURE0) glBindTexture(GL_TEXTURE_2D, aTexture) loc = glGetUniformLocation(program, "Texture") glUniform1i(loc, 0) loc = glGetAttribLocation(program, "TexCoordIn") glEnableVertexAttribArray(loc) offset=ctypes.c_void_p(data.dtype["color"].itemsize+8) glVertexAttribPointer(loc, 2, GL_FLOAT, False, stride, offset) status = glCheckFramebufferStatus (GL_FRAMEBUFFER) if status != GL_FRAMEBUFFER_COMPLETE: print( "Error in framebuffer activation") glDrawArrays(GL_TRIANGLE_STRIP, 0, 4) saveImageFromFBO(fbWidth, fbHeight, output_img_path) glBindFramebuffer(GL_FRAMEBUFFER, GL_NONE) glDeleteTextures([aTexture]) glDeleteFramebuffers(1, [framebuffer]) print('save image from FBO success') def pt_in_img(pt, img_w, img_h): if pt[0] < 0 or pt[1] < 0 or pt[0] >= img_w or pt[1] >= img_h: return False else: return True def demo_test(): input_img_path = "../test_data/pics/female003.jpg" output_img_path = "../tmp.png" ###### define vertex and color array # data = np.zeros(4, dtype=[("position", np.float32, 2), ("color", np.float32, 4), ("textureCoord", np.float32, 2)]) # data['color'] = [(1, 1, 0, 1), (1, 1, 0, 1), (1, 1, 0, 1), (1, 1, 0, 1)] # # data['position'] = [(-1, -1), (-1, 1), (1, -1), (1, 1)] # data['position'] = [(-1, -1), (-1, 1), (0, -1), (0, 1)] # # data['textureCoord'] = [(0, 0), (0, 1), (1, 0), (1, 1)] # data['textureCoord'] = [(0, 0), (0, 1), (1, 0), (0.5, 1)] data = np.zeros(8, dtype=[("position", np.float32, 2), ("color", np.float32, 4), ("textureCoord", np.float32, 2)]) data['color'] = [(1, 1, 0, 1), (1, 1, 0, 1), (1, 1, 0, 1), (1, 1, 0, 1), (1, 1, 0, 1), (1, 1, 0, 1), (1, 1, 0, 1), (1, 1, 0, 1)] data['position'] = [(-1, -1), (-1, +1), (0, -1), (0, +1), (0, -1), (0, +1), (+1, -1), (+1, +1)] # data['textureCoord'] = [(0, 0), (0, 1), (0.5, 0), (0.5, 1), (0.5, 0), (0.5, 1), (1, 0), (1, 1)] mid_x = 0.6 data['textureCoord'] = [(0, 0), (0, 1), (mid_x, 0), (mid_x, 1), (mid_x, 0), (mid_x, 1), (1, 0), (1, 1)] # data = np.zeros(3, dtype=[("position", np.float32, 2), ("color", np.float32, 4), ("textureCoord", np.float32, 2)]) # data['color'] = [(1, 1, 0, 1), (1, 1, 0, 1), (1, 1, 0, 1)] # data['position'] = [(-1, -1), (-1, +1), (1, -1)] # # data['position'] = [(-1, -1), (1, 1), (1, -1)] # data['textureCoord'] = [(0, 0), (0, 1), (0.5, 0)] # # data['textureCoord'] = [(0, 0), (1, 1), (1, 0)] # data = np.zeros(6, dtype=[("position", np.float32, 2), ("color", np.float32, 4), ("textureCoord", np.float32, 2)]) # data['color'] = [(1, 1, 0, 1), (1, 1, 0, 1), (1, 1, 0, 1), (1, 1, 0, 1), (1, 1, 0, 1), (1, 1, 0, 1)] # data['position'] = [(-1, -1), (-1, +1), (0, -1), (0, +1), (1, -1), (1, 1)] # # data['textureCoord'] = [(0, 0), (0, 1), (0.5, 0), (0.5, 1), (1, 0), (1, 1)] # data['textureCoord'] = [(0, 0), (0, 1), (0.5, 0), (0.5, 1), (1, 0), (1, 1)] # mid_x = 0.4 # data['textureCoord'] = [(0, 0), (0, 1), (mid_x, 0), (mid_x, 1), (1, 0), (1, 1)] image = init(input_img_path) program = initShaderProgram() setupSelfDefineFBO(program, image, data, output_img_path) def demo_warp_pt137(): input_img_path = "../test_data/pics/female003.jpg" output_img_path = "../tmp.png" import pickle with open("../test_render.pkl", "rb") as fp: info = pickle.load(fp) vertice = info["vertice"] dst_vertice = info["dst_vertice"] faces = info["faces"] color_list = [] position_list = [] textureCoord_list = [] img = cv2.imread(input_img_path) img_h, img_w, _ = img.shape for i in range(len(faces)): # print("index: ", faces[i]) pt1 = vertice[faces[i][0]] pt2 = vertice[faces[i][1]] pt3 = vertice[faces[i][2]] dst_pt1 = dst_vertice[faces[i][0]] dst_pt2 = dst_vertice[faces[i][1]] dst_pt3 = dst_vertice[faces[i][2]] # if pt1[0] != dst_pt1[0]: # print("use warp!") if pt_in_img(pt1, img_w, img_h) and pt_in_img(pt2, img_w, img_h) and pt_in_img(pt3, img_w, img_h): color_list.append((1, 1, 0, 1)) color_list.append((1, 1, 0, 1)) color_list.append((1, 1, 0, 1)) position_list.append((dst_pt1[0] * 2 / img_w - 1, dst_pt1[1] * 2 / img_h - 1)) position_list.append((dst_pt2[0] * 2 / img_w - 1, dst_pt2[1] * 2 / img_h - 1)) position_list.append((dst_pt3[0] * 2 / img_w - 1, dst_pt3[1] * 2 / img_h - 1)) textureCoord_list.append((pt1[0] / img_w, pt1[1] / img_h)) textureCoord_list.append((pt2[0] / img_w, pt2[1] / img_h)) textureCoord_list.append((pt3[0] / img_w, pt3[1] / img_h)) data = np.zeros(len(color_list), dtype=[("position", np.float32, 2), ("color", np.float32, 4), ("textureCoord", np.float32, 2)]) data['color'] = color_list data['position'] = position_list data['textureCoord'] = textureCoord_list image = init(input_img_path) program = initShaderProgram() setupSelfDefineFBO(program, image, data, output_img_path) if __name__ == '__main__': demo_test() # demo_warp_pt137()