//Helper functions, which need python code, thats why they are not exported to QuantumImageHelper #region Internal Helper Functions IronPython.Runtime.PythonDictionary getBlurDictionaryFromData(out string heightDimensions, double[,] imageData, float rotation, bool useLog = false) { //dynamic pythonHelper = PythonFile.QuantumBlurHelper("Helper"); blurHelper.SetHeights(imageData, imageData.GetLength(0), imageData.GetLength(1), useLog); blurHelper.ApplyPartialX(rotation); dynamic circuit = blurHelper.GetCircuit(); int numberofQubits = circuit.num_qubits; heightDimensions = circuit.name; QuantumCircuit quantumCircuit = QuantumImageHelper.ParseCircuit(circuit.data, numberofQubits); MicroQiskitSimulator simulator = new MicroQiskitSimulator(); double[] doubleArray = new double[0]; string[] stringArray = new string[0]; QuantumImageHelper.GetProbabilityArrays(simulator.GetProbabilities(quantumCircuit), numberofQubits, ref doubleArray, ref stringArray); IronPython.Runtime.PythonDictionary dictionary = pythonFile.HeightFromProbabilities(stringArray, doubleArray, doubleArray.Length, heightDimensions, useLog); return(dictionary); }
/// <summary> /// A slightly faster version to construct a colored image from 3 quantumCircuits, 1 per channel, (which should represent a colored image). /// Used after image effect are applied to the image (the circuit) to get the modified picture /// This version should produce less garbage, however, it only makes a small difference, since the python part is the same (and the slow part) /// </summary> /// <param name="redCircuit">The circuit representing the red color channel of the (modified) image.</param> /// <param name="greenCircuit">The circuit representing the green color channel of the (modified) image</param> /// <param name="blueCircuit">The circuit representing the blue color channel of the (modified) image</param> /// <param name="useLog">If logarithmic decoding should be used to decode the image.</param> /// <returns></returns> public Texture2D GetColoreTextureFast(QuantumCircuit redCircuit, QuantumCircuit greenCircuit, QuantumCircuit blueCircuit, bool useLog = false) { MicroQiskitSimulator simulator = new MicroQiskitSimulator(); //Trying optimazations (less garbage). Negative side is we need the full arrays // TODO make circuit initialization better double[] probabilities = new double[MathHelper.IntegerPower(2, redCircuit.NumberOfQubits)]; ComplexNumber[] amplitudes = null; string[] stringArray = QuantumImageHelper.CalculateNameStrings(probabilities.Length, redCircuit.NumberOfQubits); simulator.CalculateProbabilities(redCircuit, ref probabilities, ref amplitudes); IronPython.Runtime.PythonDictionary redDictionary = pythonFile.HeightFromProbabilities(stringArray, probabilities, probabilities.Length, redCircuit.DimensionString, useLog); simulator.CalculateProbabilities(greenCircuit, ref probabilities, ref amplitudes); IronPython.Runtime.PythonDictionary greenDictionary = pythonFile.HeightFromProbabilities(stringArray, probabilities, probabilities.Length, greenCircuit.DimensionString, useLog); simulator.CalculateProbabilities(blueCircuit, ref probabilities, ref amplitudes); IronPython.Runtime.PythonDictionary blueDictionary = pythonFile.HeightFromProbabilities(stringArray, probabilities, probabilities.Length, blueCircuit.DimensionString, useLog); return(QuantumImageHelper.CalculateColorTexture(redDictionary, greenDictionary, blueDictionary, redCircuit.DimensionString)); }
public static Texture2D CalculateColorTexture(QuantumCircuit redCircuit, QuantumCircuit greenCircuit, QuantumCircuit blueCircuit, int width, int height, bool renormalize = false) { Texture2D texture = new Texture2D(width, height); int widthLog = Mathf.CeilToInt(Mathf.Log(width) / Mathf.Log(2)); int heightLog = widthLog; int[] widthLines = MakeLinesInt(widthLog); int[] heightLines = widthLines; if (height != width) { heightLog = Mathf.CeilToInt(Mathf.Log(height) / Mathf.Log(2)); heightLines = MakeLinesInt(heightLog); } MicroQiskitSimulator simulator = new MicroQiskitSimulator(); double[] redProbs = simulator.GetProbabilities(redCircuit); double[] greenProbs = simulator.GetProbabilities(greenCircuit); double[] blueProbs = simulator.GetProbabilities(blueCircuit); double normalizationRed = 0; double normalizationGreen = 0; double normalizationBlue = 0; if (!renormalize && redCircuit.OriginalSum > 0 && greenCircuit.OriginalSum > 0 && blueCircuit.OriginalSum > 0) { normalizationRed = redCircuit.OriginalSum; normalizationGreen = greenCircuit.OriginalSum; normalizationBlue = blueCircuit.OriginalSum; } else { for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { int pos = widthLines[i] * height + heightLines[j]; if (redProbs[pos] > normalizationRed) { normalizationRed = redProbs[pos]; } if (greenProbs[pos] > normalizationGreen) { normalizationGreen = greenProbs[pos]; } if (blueProbs[pos] > normalizationBlue) { normalizationBlue = blueProbs[pos]; } } } normalizationRed = 1.0 / normalizationRed; normalizationGreen = 1.0 / normalizationGreen; normalizationBlue = 1.0 / normalizationBlue; } float redValue; float greenValue; float blueValue; int posX = 0; for (int x = 0; x < width; x++) { posX = widthLines[x] * height; for (int y = 0; y < height; y++) { int index = posX + heightLines[y]; redValue = (float)(redProbs[index] * normalizationRed); greenValue = (float)(greenProbs[index] * normalizationGreen); blueValue = (float)(blueProbs[index] * normalizationBlue); texture.SetPixel(x, y, new Color(redValue, greenValue, blueValue)); } } texture.Apply(); return(texture); }
IEnumerator meshAnimationOptimized() { //It is important to create a copy of the vertices and only use "inputMesh.vertices" only once // since this is a get function. Meaning in every call of the function the mesh will be copied. Vector3[] inputVertices = InputMesh.vertices; int numberOfVertices = inputVertices.Length; //We look at our input being 2 dimensional. One dimension is the number of vertices, //the other dimension is the vector x,y,z //We create lines for the first dimension the number of vertices int numberOfVertexCubits = Mathf.CeilToInt(Mathf.Log(numberOfVertices) / Mathf.Log(2)); //The lines are needed to encode the data in a way that encoding of neighbouring vertices only differ in 1 bit int[] linesVertices = QuantumImageHelper.MakeLinesInt(numberOfVertexCubits); //We create lines for the second dimension the vector x,y,z so there are only 3 values (thats where the 3 comes from). //The 2 is to transform the thing to base 2 since we need the logarithm in base 2. int numberOfVectorQubits = Mathf.CeilToInt(Mathf.Log(3) / Mathf.Log(2)); //The lines are needed to encode the data in a way that the encoding of x and y as well as y and z only differ in 1 bit. int[] linesVector = QuantumImageHelper.MakeLinesInt(numberOfVectorQubits); //Creating a circuit big enough to fit in the mesh data int numberOfQubits = numberOfVertexCubits + numberOfVectorQubits; QuantumCircuit circuit = new QuantumCircuit(numberOfQubits, numberOfQubits, true); //Since we work with probabilities, we need the values to be positive (or 0). //Therefore we want to translate the values adding an offset to our values, such that they become positive. //Getting the smallest (most negative) values to use for the displacement //initiate values as 0 float minX = 0; float minY = 0; float minZ = 0; for (int i = 0; i < inputVertices.Length; i++) { if (inputVertices[i].x < minX) { minX = inputVertices[i].x; } if (inputVertices[i].y < minY) { minY = inputVertices[i].y; } if (inputVertices[i].z < minZ) { minZ = inputVertices[i].z; } } //Needed to transform the position of our 2 dimensions mentioned above into a single array. //The indexes look like this: 0_X, 0_Y, 0_Z, 0_?, 1_X, 1_Y, 1_Z.... Where 0,1 etc stand for the vertex number //X,Y,Z stands for the respective coordinates and ? is just a placeholder which is not really needed (but we need to have a power of 2 so 4 values for our 3) int maxHeight = linesVector.Length; for (int i = 0; i < numberOfVertices; i++) { //We use the lines produced above to calculate the new position int index = linesVertices[i] * maxHeight; //We interpret the values as probabilities. And the amplitudes are the square root of the probabilities. circuit.Amplitudes[index + linesVector[0]].Real = Math.Sqrt(inputVertices[i].x - minX); circuit.Amplitudes[index + linesVector[1]].Real = Math.Sqrt(inputVertices[i].y - minY); circuit.Amplitudes[index + linesVector[2]].Real = Math.Sqrt(inputVertices[i].z - minZ); } //We need to normalize the circuit, since probabilities should add up to 1. //We store the factor used for normalization in order to reverse this scaling later. circuit.Normalize(); double normalizationFactor = circuit.OriginalSum; //We don't want to allocate as phew memory as possible in order to minimize garbage collection //Therefore, we reuse the defined arrays double[] realAmplitudes = new double[circuit.AmplitudeLength]; Vector3[] outPutVertices = new Vector3[numberOfVertices]; //Making a copy of the amplitudes of the circuit for (int i = 0; i < circuit.AmplitudeLength; i++) { realAmplitudes[i] = circuit.Amplitudes[i].Real; } //Creating a new mesh, at this point it is just a copy of the input mesh Mesh outputMesh = new Mesh(); outputMesh.name = InputMesh.name + " blurred"; outputMesh.vertices = inputVertices; outputMesh.triangles = InputMesh.triangles; outputMesh.uv = InputMesh.uv; outputMesh.RecalculateNormals(); outputMesh.RecalculateTangents(); MicroQiskitSimulator simulator = new MicroQiskitSimulator(); double[] probs = new double[circuit.AmplitudeLength]; //simulator.GetProbabilities(circuit); ComplexNumber[] amplitudes = new ComplexNumber[circuit.AmplitudeLength]; //Setting the new mesh to the target //We can now just manipulate the vertices of this mesh in order to change it. //No need to create new meshes TargetMesh.sharedMesh = outputMesh; OutputMesh = outputMesh; float rotation = 0.0f; float progress = 0; //Making sure to have no endless loop if (Duration <= 0) { Duration = 1; } //Creating an animation by blurring the mesh step by step //Duration is the duration of the animation, StartRotation and Endrotation give //The starting points and endpoints of the animation the rest is interpolated. while (progress < 1) { //Calculating progress for the animation progress += Time.deltaTime / Duration; //Rotation is interpolated between endRotation and startRtoation depending on progress rotation = progress * EndRotation + (1 - progress) * StartRotation; //Resetting the circuit (deleting the gates) circuit.ResetGates(); //Reusing the circuit by filling in the original values before the calculation again for (int i = 0; i < circuit.AmplitudeLength; i++) { circuit.Amplitudes[i].Real = realAmplitudes[i]; } //Applying the operation to the circuit (the blur effect) ApplyPartialQ(circuit, rotation); //Calculating probabilities simulator.CalculateProbabilities(circuit, ref probs, ref amplitudes); //Filling in the new calculated values into the vertices for (int i = 0; i < numberOfVertices; i++) { int index = linesVertices[i] * maxHeight; //Since we have probabilities already we do not need to square them. //We undo the normalization from above and the translation (offset) from the beginning outPutVertices[i].x = (float)(probs[index + linesVector[0]] * normalizationFactor) + minX; outPutVertices[i].y = (float)(probs[index + linesVector[1]] * normalizationFactor) + minY; outPutVertices[i].z = (float)(probs[index + linesVector[2]] * normalizationFactor) + minZ; } //We set the new vertices to the mesh, this way the mesh changes its form automatically //and we do not need to construct a new mesh. outputMesh.vertices = outPutVertices; //wait until next frame. yield return(null); } // Reverse animation going back to startRotation if (reverseAnimation) { while (progress > 0) { //Calculating progress for the animation going back progress -= Time.deltaTime / Duration; //Rotation is interpolated between endRotation and startRtoation depending on progress rotation = progress * EndRotation + (1 - progress) * StartRotation; //Resetting the circuit (deleting the gates) circuit.ResetGates(); //Reusing the circuit by filling in the original values before the calculation again for (int i = 0; i < circuit.AmplitudeLength; i++) { circuit.Amplitudes[i].Real = realAmplitudes[i]; } //Applying the operation to the circuit (the blur effect) ApplyPartialQ(circuit, rotation); //Calculating probabilities simulator.CalculateProbabilities(circuit, ref probs, ref amplitudes); //Filling the new calculated values into the vertices for (int i = 0; i < numberOfVertices; i++) { int index = linesVertices[i] * maxHeight; //Since we have probabilities already we do not need to square them. //We undo the normalization from above and the translation (offset) from the beginning outPutVertices[i].x = (float)(probs[index + linesVector[0]] * normalizationFactor) + minX; outPutVertices[i].y = (float)(probs[index + linesVector[1]] * normalizationFactor) + minY; outPutVertices[i].z = (float)(probs[index + linesVector[2]] * normalizationFactor) + minZ; } outputMesh.vertices = outPutVertices; //We set the new vertices to the mesh, this way the mesh changes its form automatically //and we do not need to construct a new mesh.outputMesh.vertices = outPutVertices; //wait until next frame. yield return(null); } } }
/// <summary> /// Creates a blurred version of the input mesh, according to the rotation. The bigger the rotation the stronger the blur effect. /// The model gets unrecognizeable as soon as the rotation gets to big /// </summary> /// <param name="inputMesh">The mesh to produce a blurred version of.</param> /// <param name="rotation">Can be between 0 and 3.1415 (pi). For starting 0.1 would be a good value.</param> /// <returns>A blurred (distorted) mesh of the original input mesh. </returns> public Mesh CreateBluredMesh(Mesh inputMesh, float rotation) { //It is important to create a copy of the vertices and only use "inputMesh.vertices" only once // since this is a get function. Meaning in every call of the function the mesh will be copied. Vector3[] vertices = inputMesh.vertices; int numberOfVertices = vertices.Length; //We look at our input being 2 dimensional. One dimension is the number of vertices, //the other dimension is the vector x,y,z //We create lines for the first dimension the number of vertices int numberOfVertexCubits = Mathf.CeilToInt(Mathf.Log(numberOfVertices) / Mathf.Log(2)); //The lines are needed to encode the data in a way that encoding of neighbouring vertices only differ in 1 bit int[] linesVertices = QuantumImageHelper.MakeLinesInt(numberOfVertexCubits); //We create lines for the second dimension the vector x,y,z so there are only 3 values (thats where the 3 comes from). //The 2 is to transform the thing to base 2 since we need the logarithm in base 2. int numberOfVectorQubits = Mathf.CeilToInt(Mathf.Log(3) / Mathf.Log(2)); //The lines are needed to encode the data in a way that the encoding of x and y as well as y and z only differ in 1 bit. int[] linesVector = QuantumImageHelper.MakeLinesInt(numberOfVectorQubits); //Creating a circuit big enough to fit in the mesh data int numberOfQubits = numberOfVertexCubits + numberOfVectorQubits; QuantumCircuit circuit = new QuantumCircuit(numberOfQubits, numberOfQubits, true); //Since we work with probabilities, we need the values to be positive (or 0). //Therefore we want to translate the values adding an offset to our values, such that they become positive. //Getting the smallest (most negative) values to use for the displacement //initiate values as 0 float minX = 0; float minY = 0; float minZ = 0; for (int i = 0; i < vertices.Length; i++) { if (vertices[i].x < minX) { minX = vertices[i].x; } if (vertices[i].y < minY) { minY = vertices[i].y; } if (vertices[i].z < minZ) { minZ = vertices[i].z; } } //Needed to transform the position of our 2 dimensions mentioned above into a single array. //The indexes look like this: 0_X, 0_Y, 0_Z, 0_?, 1_X, 1_Y, 1_Z.... Where 0,1 etc stand for the vertex number //X,Y,Z stands for the respective coordinates and ? is just a placeholder which is not really needed (but we need to have a power of 2 so 4 values for our 3) int maxHeight = linesVector.Length; for (int i = 0; i < numberOfVertices; i++) { //We use the lines produced above to calculate the new position int index = linesVertices[i] * maxHeight; //We interpret the values as probabilities. And the amplitudes are the square root of the probabilities. circuit.Amplitudes[index + linesVector[0]].Real = Math.Sqrt(vertices[i].x - minX); circuit.Amplitudes[index + linesVector[1]].Real = Math.Sqrt(vertices[i].y - minY); circuit.Amplitudes[index + linesVector[2]].Real = Math.Sqrt(vertices[i].z - minZ); } //We need to normalize the circuit, since probabilities should add up to 1. //We store the factor used for normalization in order to reverse this scaling later. circuit.Normalize(); double normalizationFactor = circuit.OriginalSum; //We apply the effect to the circuit. Here this is the normal blur effect also used in the images. ApplyPartialQ(circuit, rotation); //Calculating the probabilities after having applied the operation above SimulatorBase simulator = new MicroQiskitSimulator(); //MicroQiskitSimulator simulator = new MicroQiskitSimulator(); //QiskitSimulator simulator = new QiskitSimulator(); double[] probs = simulator.GetProbabilities(circuit); //Fill in the new calculated values back into the vertices for (int i = 0; i < numberOfVertices; i++) { int index = linesVertices[i] * maxHeight; //Since we have probabilities already we do not need to square them. //We undo the normalization from above and the translation (offset) from the beginning vertices[i].x = (float)(probs[index + linesVector[0]] * normalizationFactor) + minX; vertices[i].y = (float)(probs[index + linesVector[1]] * normalizationFactor) + minY; vertices[i].z = (float)(probs[index + linesVector[2]] * normalizationFactor) + minZ; } //creating the new mesh Mesh outputMesh = new Mesh(); //setting the newly calculated vertices outputMesh.vertices = vertices; //Copying most stuff from original mesh outputMesh.name = inputMesh.name + " blurred"; outputMesh.triangles = inputMesh.triangles; outputMesh.uv = inputMesh.uv; //Recalculating normals for correct lighning outputMesh.RecalculateNormals(); //returning the mesh return(outputMesh); }