class NormalConstraintTool(BaseTool): ## Constructor def __init__(self): super(NormalConstraintTool, self).__init__() self._normal_constraints = NormalConstraintSet() self._p_old = None self._n_old = None self._selected_constraint = None self._normal_radius = 40.0 self._image = None self._N_32F = None def setNormalConstraints(self, normal_constraints): self._normal_constraints = normal_constraints self._view.update() def normalConstraints(self): return self._normal_constraints def setImage(self, image): self._image = image self._interpolateNormal() self._view.render(image) def mousePressEvent(self, e): p = self._mousePosition(e) if self._selectConstraint(p): return else: self.addConstraint(p) def mouseReleaseEvent(self, e): print "p: ", self._selected_constraint.position() print "n: ", self._selected_constraint.normal() self._selected_constraint = None #self._interpolateNormal() def mouseMoveEvent(self, e): p = self._mousePosition(e) self._p_old = p if e.buttons() & Qt.LeftButton: if self._selected_constraint is None: return p_c = self._selected_constraint.position() N_c = self._n_old dP = p - p_c Nx, Ny = N_c[0], N_c[1] Nx += dP[0] / self._normal_radius Ny += -dP[1] / self._normal_radius r = np.linalg.norm(np.array([Nx, Ny])) Nz = np.sqrt(max(0.001, 1.0 - r * r)) N = np.array([Nx, Ny, Nz]) N /= np.linalg.norm(N) self._selected_constraint.setNormal(N) self._view.update() def keyPressEvent(self, e): if e.key() == Qt.Key_0: self._view.render(self._image) if e.key() == Qt.Key_1: if self._N_32F is None: self._interpolateNormal() A_8U = None if self._image.shape[2] == 4: A_8U = to8U(alpha(self._image)) self._view.render(normalToColor(self._N_32F, A_8U)) if e.key() == Qt.Key_2: self._interpolateNormal() if self._N_32F is None: self._interpolateNormal() A_8U = None if self._image.shape[2] == 4: A_8U = to8U(alpha(self._image)) self._view.render(normalToColor(self._N_32F, A_8U)) if e.key() == Qt.Key_Delete: self._normal_constraints.clear() self._view.update() def keyReleaseEvent(self, e): pass def addConstraint(self, p): n = np.array([0.0, 0.0, 1.0]) if self._N_32F is not None: n = self._N_32F[p[1], p[0], :] self._selected_constraint = NormalConstraint(point=p, normal=n) self._normal_constraints.addConstraint(self._selected_constraint) self._n_old = self._selected_constraint.normal() self._view.update() def _interpolateNormal(self): if self._normal_constraints.empty(): return if self._image is None: return ps = np.int32(self._normal_constraints.positions()) ns = self._normal_constraints.normals() h, w = self._image.shape[:2] N0_32F = np.zeros((h, w, 3)) N0_32F[ps[:, 1], ps[:, 0]] = ns W_32F = np.zeros((h, w)) W_32F[ps[:, 1], ps[:, 0]] = 1.0 A_8U = None if self._image.shape[2] == 4: A_8U = to8U(alpha(self._image)) self._N_32F = self._interpolateNormalImage(N0_32F, W_32F, A_8U) self._projectConstraints() def _interpolateNormalImage(self, N0_32F, W_32F, A_8U): constraints = [] constraints.append(image_constraints.laplacianConstraints(w_c=0.1)) constraints.append(image_constraints.normalConstraints(W_32F, N0_32F, w_c=3.0)) L = normalizeVector(np.array([-0.2, 0.3, 0.7])) I_32F = luminance(to32F(rgb(self._image))) I_min, I_max = np.min(I_32F), np.max(I_32F) I_32F = (I_32F - I_min) / (I_max - I_min) # constraints.append(image_constraints.brightnessConstraints(L, I_32F, w_c=0.5)) constraints.append(image_constraints.silhouetteConstraints(A_8U, w_c=0.8)) solver_iter = image_solver.solveIterator(constraints, [postNormalize(th=0.0)]) N_32F = np.array(N0_32F) N_32F = image_solver.solveMG(N_32F, solver_iter, iterations=10) N_32F = image_constraints.NxyToNz(N_32F) return N_32F def _interpolateNormalAMG(self, N0_32F, W_32F, A_8U): h, w = N0_32F.shape[:2] A_c, b_c = normalConstraints(W_32F, N0_32F) A_8U = None if self._image.shape[2] == 4: A_8U = to8U(alpha(self._image)) A_sil, b_sil = silhouetteConstraints(A_8U) A_L = laplacianMatrix((h, w)) A = 10.0 * A_c + A_L + A_sil b = 10.0 * b_c + b_sil N_32F = amg_solver.solve(A, b).reshape(h, w, 3) N_32F = normalizeImage(N_32F) return N_32F def _projectConstraints(self): for constraint in self._normal_constraints.constraints(): p = constraint.position() N = self._N_32F[p[1], p[0]] constraint.setNormal(N) self._view.update() def _overlayFunc(self, painter): pen = QPen(QColor.fromRgbF(0.0, 1.0, 0.0, 0.5)) pen.setWidth(5) painter.setPen(pen) p = self._p_old if p is not None: painter.drawPoint(QPoint(p[0], p[1])) for constraint in self._normal_constraints.constraints(): pen = QPen(QColor.fromRgbF(1.0, 0.0, 0.0, 0.7)) pen.setWidth(5) painter.setPen(pen) p = constraint.position() painter.drawPoint(QPoint(p[0], p[1])) n = np.array(constraint.normal()) n[1] = -n[1] p_n = p + self._normal_radius * n[:2] pen.setWidth(2) painter.setPen(pen) painter.drawLine(QPoint(p[0], p[1]), QPoint(p_n[0], p_n[1])) def _selectConstraint(self, p): if self._normal_constraints.empty(): return False ps = self._normal_constraints.positions() dP = normVectors(ps - p) p_min = np.argmin(dP) if dP[p_min] < 5: self._selected_constraint = self._normal_constraints.constraint(p_min) self._n_old = self._selected_constraint.normal() return True return False
class NormalConstraintBatch(DatasetBatch, CharacterBatch): def __init__(self, view, tool, name="NormalConstraint", dataset_name="3dmodel"): super(NormalConstraintBatch, self).__init__(name, dataset_name) self._view = view self._normal_constraint = NormalConstraintSet() self._tool = tool self._tool.setNormalConstraints(self._normal_constraint) def _runImp(self): normal_data = loadNormal(self._data_file) if normal_data is None: return N0_32F, A_8U = normal_data A_32F = to32F(A_8U) L = normalizeVector(np.array([-0.2, 0.3, 0.7])) C0_32F = LambertShader().diffuseShading(L, N0_32F) self._normal_constraint.clear() if os.path.exists(self.constraintFile()): self._normal_constraint.load(self.constraintFile()) self._tool.setImage(setAlpha(C0_32F, A_32F)) def _runCharacterImp(self): print self.fullLayerFile() C0_8U = loadRGBA(self.fullLayerFile()) if C0_8U is None: return C0_32F = to32F(C0_8U) print C0_32F h, w = C0_32F.shape[:2] w_low = 512 h_low = w_low * h / w C0_32F = cv2.resize(C0_32F, (w_low, h_low)) self._normal_constraint.clear() if os.path.exists(self.characterConstraintFile()): self._normal_constraint.load(self.characterConstraintFile()) self._tool.setImage(C0_32F) def constraintFile(self): return self.resultFile(self._data_name + "_normal_constraint.json") def characterConstraintFile(self): return self.characterResultFile("normal_constraint.json") def finish(self): constraint_file = self.resultFile(self._data_name + "_normal_constraint.json") self._normal_constraint.save(constraint_file) self.runNext() def finishCharacter(self): if self._character_name != "": constraint_file = self.characterResultFile("normal_constraint.json") self._normal_constraint.save(constraint_file) self.runCharacter()
class NormalConstraintBatch(DatasetBatch, CharacterBatch): def __init__(self, view, tool, name="NormalConstraint", dataset_name="3dmodel"): super(NormalConstraintBatch, self).__init__(name, dataset_name) self._view = view self._normal_constraint = NormalConstraintSet() self._tool = tool self._tool.setNormalConstraints(self._normal_constraint) def _runImp(self): normal_data = loadNormal(self._data_file) if normal_data is None: return N0_32F, A_8U = normal_data A_32F = to32F(A_8U) L = normalizeVector(np.array([-0.2, 0.3, 0.7])) C0_32F = LambertShader().diffuseShading(L, N0_32F) self._normal_constraint.clear() if os.path.exists(self.constraintFile()): self._normal_constraint.load(self.constraintFile()) self._tool.setImage(setAlpha(C0_32F, A_32F)) def _runCharacterImp(self): print self.fullLayerFile() C0_8U = loadRGBA(self.fullLayerFile()) if C0_8U is None: return C0_32F = to32F(C0_8U) print C0_32F h, w = C0_32F.shape[:2] w_low = 512 h_low = w_low * h / w C0_32F = cv2.resize(C0_32F, (w_low, h_low)) self._normal_constraint.clear() if os.path.exists(self.characterConstraintFile()): self._normal_constraint.load(self.characterConstraintFile()) self._tool.setImage(C0_32F) def constraintFile(self): return self.resultFile(self._data_name + "_normal_constraint.json") def characterConstraintFile(self): return self.characterResultFile("normal_constraint.json") def finish(self): constraint_file = self.resultFile(self._data_name + "_normal_constraint.json") self._normal_constraint.save(constraint_file) self.runNext() def finishCharacter(self): if self._character_name != "": constraint_file = self.characterResultFile( "normal_constraint.json") self._normal_constraint.save(constraint_file) self.runCharacter()