how to convert RenderTexture to 1D array? - arrays

I thought I would find an answer somewhere instantly but apparently not. So here it goes:
I need to convert a RenderTexture to a 1D array for each frame update in the script (C#) in Unity. How can I do that?
That is it actually. The rest is kind of besides the point of the question, but let me also explain why I need to do this anyway:
..so that I can then dump the 1D array within a cell of a row in a .csv file. I would collect data (including the rendered texture data) at each frame update and write all this collected per-frame data using WriteLine(line). My idea is that this would save me from wasting computation time that would otherwise be spent encoding images and saving those images as separate files with each update.

The RenderTexture, and the underlying Texture, objects don't have a lot to work with in this regard. So the idea is to record the data in the RenderTexture as a new Texture2D, and use that new item to read the data you're looking for.
Here's a piece of code that should read a 1D array of type Color32 struct (untested):
public Color32 [ ] GetRenderTexturePixels ( RenderTexture renderTexture )
{
var texture = new Texture2D( renderTexture.width, renderTexture.height, TextureFormat.RGB24, false );
// record the current render texture.
var currentRenderTexture = RenderTexture.active;
// Set the new render texture.
RenderTexture.active = renderTexture;
texture.ReadPixels ( new Rect ( 0, 0, renderTexture.width, renderTexture.height ), 0, 0 );
texture.Apply ( );
// reapply the previous render texture.
RenderTexture.active = currentRenderTexture;
// Return then texture2d pixels. This assumes mipmap level 0.
return texture.GetPixels32 ( );
}

Related

How to access an array of UDT <type>

In the Pine script reference manual the is a code snippet on
https://www.tradingview.com/pine-script-docs/en/v5/language/Objects.html
I want to know how to access the values of the array pivotHighArray.
This is because I don't want to draw a line only between the last pivot high and the previous pivot high. I want to draw a line/lines from the current pivot high to ANY previous pivot highs that meet certain conditions (e.g. previous pivot high must be higher than the current).
//#version=5
indicator("Pivot Points High", overlay = true)
int legsInput = input(10)
// Define the `pivotPoint` UDT containing the time and price of pivots.
type pivotPoint
int openTime
float level
// Create an empty `pivotPoint` array.
var pivotHighArray = array.new<pivotPoint>()
// Detect new pivots (`na` is returned when no pivot is found).
pivotHighPrice = ta.pivothigh(legsInput, legsInput)
// Add a new `pivotPoint` object to the end of the array for each detected pivot.
if not na(pivotHighPrice)
// A new pivot is found; create a new object of `pivotPoint` type, setting its `openTime` and `level` fields.
newPivot = pivotPoint.new(time[legsInput], pivotHighPrice)
// Add the new pivot object to the array.
array.push(pivotHighArray, newPivot)
// On the last historical bar, draw pivot labels and connecting lines.
if barstate.islastconfirmedhistory
var pivotPoint previousPoint = na
for eachPivot in pivotHighArray
// Display a label at the pivot point.
label.new(eachPivot.openTime, eachPivot.level, str.tostring(eachPivot.level, format.mintick), xloc.bar_time, textcolor = color.white)
// Create a line between pivots.
if not na(previousPoint)
// Only create a line starting at the loop's second iteration because lines connect two pivots.
line.new(previousPoint.openTime, previousPoint.level, eachPivot.openTime, eachPivot.level, xloc = xloc.bar_time)
// Save the pivot for use in the next iteration.
previousPoint := eachPivot
I have tried pivotHighArray.level[someindex], array.get(pivotHighArray.level, someindex) but failed.
How do I reference these values?
This question is related to my old question which I'm now trying another approach on since it fails when a new bar is printed both live and during backtesting
You're on the right track. Try:
pivotPoint t = na
if 0 < array.size(pivotHighArray)
t := array.get(pivotHighArray, 0)
plot(na(t) ? na : t.level, color = color.red)
First you just read the array element, after that you can access its properties.

Iterate over ee.FeatureCollection to compute regional statistics of an ee.ImageCollection

Dear Earth Engine community,
Can someone help me solving the following problem:
I want to compute the aggregate nightlight intensities (sum) within all first level administrative regions of the world. For that purpose I use a shapefile which contains the regional boundaries (GADM) and raster data on nightlight (VIIRS).
The issue with the following code is that 1) I am getting an error that say "Unknown element type provided: object. Expected: ee.Image, ee.ImageCollection, ee.FeatureCollection or ee.Element." for the nighttime.reduceRegion operation and 2) that only the last feature of the selection is returned on print(final).
Unfortunately I do not manage to solve these problems. It would be great if someone could help me improving the code. I am sure there are many issues since Javascript and the Earth Engine API are completely new to me..
Thanks a lot in advance!
// Import nighttime raster data.
var nighttimeCollection = ee.ImageCollection('NOAA/VIIRS/DNB/MONTHLY_V1/VCMSLCFG');
// Import shapefile containing region boundaries.
var region_boundaries = ee.FeatureCollection("users/hendrikscheewel/gadm_level_1");
// Select a specific year ::: Later this should be done within a loop.
var year = "2014";
// Aggregate monthly nighttime data to year x.
var nighttime = nighttimeCollection
.filter(ee.Filter.date(year+'-01-01', year+'-12-31'))
.select('avg_rad')
.reduce(ee.Reducer.mean());
// This function does the following:
// * Aggregrate nightlight data within a shape/feature by taking its sum,
// * Assign the result to the feature,
// * Create copy of feature with simplified geometry (centroid) and fewer columns,
// * Return the copy.
var compute_nightlight = function(feature) {
// Compute mean of average radiance for feature shape
var result = nighttime.reduceRegion({
geometry: feature.geometry(),
reducer: ee.Reducer.sum(),
scale: 30,
maxPixels: 1e9,
});
// Set "nightlight" as a new property.
feature = ee.Feature(feature.set('nightlight',result.get('avg_rad_mean')));
// Get the centroid of the feature's geometry.
var featureSimplified = feature.centroid();
// Keep this list of properties.
var keepProperties = ['GID_0','GID_1','NAME_0','NAME_1','nightlight'];
featureSimplified = featureSimplified.copyProperties(feature, keepProperties);
// Return a new Feature, copying properties from the old Feature.
return featureSimplified;
};
//print(compute_nightlight(region_boundaries.first()));
var final = region_boundaries.filter(ee.Filter.eq('NAME_0','Belgium')).iterate(compute_nightlight);
print(final)
Export.table.toDrive({
collection: final,
description: 'nl_'+year,
fileFormat: 'CSV'
});
Ok, I found my main mistake: Instead of using the .iterate() method I should have used the .map() method.
After some cleaning the code looks like this:
// Select a specific year
var year = "2014";
// Aggregate monthly nighttime data to year x.
var nighttime = nighttimeCollection
.filter(ee.Filter.date(year+'-01-01', year+'-12-31'))
.select('avg_rad')
.reduce(ee.Reducer.mean());
// This function does the following:
// * Aggregrate nightlight data within a shape/feature by taking its sum,
// * Assign the result to the feature,
// * Create copy of feature with simplified geometry (centroid) and fewer columns,
// * Return the copy.
var compute_nightlight = function(feature) {
// Compute mean of average radiance for feature shape
var result = nighttime.reduceRegion({
geometry: feature.geometry(),
reducer: ee.Reducer.sum(),
scale: 30,
maxPixels: 1e9,
});
// Set "nightlight" as a new property.
feature = ee.Feature(feature.set('nightlight',result.get('avg_rad_mean')));
// Return a new Feature, copying properties from the old Feature.
return feature.centroid();
};
var final = ee.FeatureCollection((region_boundaries).map(compute_nightlight));
Export.table.toDrive({
collection: final,
description: 'nl_'+year,
fileFormat: 'CSV'
});

actionscript 3: how to access to elements of an array created in a loop dynamically

In the library of the .fla file I have a square exported as Class "cuad" on frame 1
I want to create an Array with 100 squares so as to move them later
So I do like this:
for (var i:uint = 0; i<100;i++)
{
var cuad_mc = new cuad();
addChild(cuad_mc);
myArray.push("cuad_mc");
trace(myArray[i]);
}
I have a runtime error
The error you experience is
Error #1069: Did not find alpha propiety in the String and there is not any value predetermined
The problem comes from your line
myArray.push("cuad_mc");
What you are doing here is pushing a String Object into your Array, not the cuad Object you want. String Objects don't have Alpha values, or x values.
What you want to do is
myArray.push(cuad_mc);
cuad_mc (without the " quotation marks) is a reference to the object you just created.
This should solve your problem. I also recommend using Vectors instead of Array if you only need to store one type of Object. Like this:
var myArray:Vector<cuad> = new Vector<cuad>();
for(var i:int=0;i<100;i++){
var cuad_mc:cuad = new cuad();
addChild(cuad_mc);
myArray.push(cuad_mc);
trace(myArray[i]);
}
Vectors are just like Arrays, but they only allow one specific type, so that a situation like yours doesn't occur.

Unzip from a SQL Server text column to an image column

I have images of various formats (.png, .jpg, .bmp, etc.) stored as compressed text in a text column in a SQL Server 2005 table. I need to read the row, unzip the image and store it in an image column in another table.
I am using the SharpZip library, and all of the examples deal with file sources and destinations. I can't find anything that covers unzipping from a variable to another variable. A code snippet illustrating this or a link to a relevant resource would be much appreciated.
EDIT: A bit more information - the data is stored in a TEXT column. It appears as follows (text column abbreviated for display):
ImageID ImageData
1 FORMAT-ZIPV3 UEsDBBQAAAAIAOV6wzxdTnDvshs...
2 FORMAT-ZIPV3 UEsDBBQAAAAIAAF2yjxGncjOLgA...
3 FORMAT-ZIPV3 UEsDBBQAAAAIAKd6yjyjnQNr6gg...
4 FORMAT-ZIPV3 UEsDBBQAAAAIALdNyzyrPC8EMJw...
5 FORMAT-ZIPV3 UEsDBBQAAAAIAA1rOD1nZY1t0f0...
6 FORMAT-ZIPV3 UEsDBBQAAAAIANZplj2seyJ+VmM...
7 FORMAT-ZIPV3 UEsDBBQAAAAIAC5vhD27LPbPcv8...
8 FORMAT-ZIPV3 UEsDBBQAAAAIAK1qKz5DJNH3xMg...
9 FORMAT-ZIPV3 UEsDBBQAAAAIAHVkEztC3th/9hs...
10 FORMAT-ZIPV3 UEsDBBQAAAAIAEtXKz7DXHUdvow...
What I know for certain is that the images were compressed at some point in the process using SharpZip before being inserted into the table. It appears that the format information was added to the beginning of the data prior to inserting.
Looking at this data, would anyone have any insight on how this image data has been manipulated? Again, I need to get the uncompressed image data into a column of a data type conducive to reading for display on a web page.
EDIT: Ok, I'm stumped. Executing the following code produces the error, "Failed to convert parameter value from a Int32 to a Byte[]". It appears to be placing the length of the byte array into the byte array's value...
commandUncompressed.Connection = connectionUncompressed;
commandUncompressed.Parameters.Add("#Image_k", SqlDbType.VarChar, 10);
commandUncompressed.Parameters.Add("#ImageContents", SqlDbType.Image);
commandUncompressed.CommandText = sqlSaveImage;
connectionUncompressed.Open();
reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
Console.WriteLine(reader["Image_k"].ToString()); // Merely for testing
String format = reader["ImageContents_Compressed"].ToString().Substring(0, 12);
var offset = 13; //"FORMAT-ZIPV3 ".Length;
var s = reader["ImageContents_Compressed"].ToString().Substring(offset);
var bytes = Convert.FromBase64String(s);
if (format == "FORMAT-ZIPV2 ")
{
bytes = ConvertStringToBytes(s); // Not a Base-64 encoded string? External conversion function utilized.
}
using (var zis = new ZipInputStream(new MemoryStream(bytes)))
{
ZipEntry zipEntry = zis.GetNextEntry(); // Doesn't seem to work unless an entry has been referenced
byte[] buffer = new byte[zis.Length];
commandUncompressed.Parameters["#Image_k"].Value = reader["Image_k"].ToString();
commandUncompressed.Parameters["#ImageContents"].Value = zis.Read(buffer, 0, buffer.Length);
commandUncompressed.ExecuteNonQuery();
}
}
}
It appears to be reading the data from the source text column just fine. I just cannot figure out how to get that into the image type parameter. The value for buffer variable shows the length of the byte array, rather than the actual bytes. Maybe that's what the value property typically shows for byte arrays? I'm so close and yet so far away. :/
EDIT: Ok, I'm a knucklehead. I made the following correction, and it works!
zis.Read(buffer, 0, buffer.Length)
commandUncompressed.Parameters["#ImageContents"].Value = buffer;
At this point I am only able to process FORMAT-ZIPV3 data, as I haven't figured out how to decode the FORMAT-ZIP2 strings yet. Following is a sampling of the V2 data. If anyone is able to determine the encoding, let me know. Would it be different if zipped using BZIP instead of ZIP format?
ImageID ImageData
1 FORMAT-ZIPV2 504B03041400020008005157422A2E25FDBAF26701008D6901000E...
2 FORMAT-ZIPV2 504B03041400020008009159422A7FC94BA2B2540500D35705000E...
3 FORMAT-ZIPV2 504B0304140002000800685A422A0CAA51F4473A0600B97206000E...
4 FORMAT-ZIPV2 504B03041400020008001D5D422A770BD3ED201902002C4A02000E...
5 FORMAT-ZIPV2 504B0304140002000800325E422A4B6C2FB4045001001C6E01000E...
6 FORMAT-ZIPV2 504B03041400020008006F72422A5F793AC1A1F00200ECF302000E...
7 FORMAT-ZIPV2 504B0304140002000800D572422A1B348A731DE5000085EB00000E...
8 FORMAT-ZIPV2 504B03041400020008003D73422A8AEBB7F855640300DD1B04000E...
9 FORMAT-ZIPV2 504B03041400020008006368D528C5D0A6BA794900004A2502000E...
10 FORMAT-ZIPV2 504B03041400020008008E5B6C2A2D9E9C33D7AF05005CEC05000E...
In response to a similar question, someone on sqlmonster.com provided a nifty VarBinaryStream class. It works with a column type of varbinary(max).
If your data is stored in a varbinary(max), and is in zip format, you could use that class to instantiate a VarBinaryStream, then instantiate a ZipInputStream around that, and ba-da-boom, you're there. Just read from the ZipInputStream.
In C# it might look like this
using (var imageSrc = new VarBinarySource(connection,
"Table.Name",
"Column",
"KeyColName",
1))
{
using (var s = new VarBinaryStream(imageSrc))
{
using(var zis = new ZipInputStream(s))
{
....
}
}
}
If the images are small, then you probably wouldn't want all this streaming stuff. If the column is a binary(n) or a varbinary(n) where n is less than 8000, just use the SqlBinary type and read in all the data into memory, then instantiate a MemoryStream around that. Simpler. In VB.NET it looks something like this:
Dim bytes as Bytes()
bytes = dr.GetSqlBinary(columnNumber)
Using ms As New MemoryStream(bytes)
Using zis As New ZipInputStream(ms)
...
End Using
End Using
Finally, I'm going to question the wisdom of applying zip compression to .jpg images, and similar. The jpg format is already compressed; compressing it again before putting the data into SQL Server won't cause the data to become appreciably smaller. It only increases processing time. If possible, I'd suggest you reconsider your design for storage of compressed images.
ok, with the update you provided, containing the data format, you can draw some conclusions.
The data is an actual string. Suspecting that it is a Base64-encoded string, I did a small test and used Convert.ToBase64String() on a byte stream that contains a zip file. It looks like this: UEsDBBQAAAAIAJJyYyk3M56F+QIAA...
Aha! you have a base64-encoded (string) version of the byte data for a bonafide zip file. To decode it, strip the prefix and then use FromBase64String() to get the byte array, insert into a MemoryStream, then read it with ZipInputStream.
something like this:
var offset = "FORMAT-ZIPV3 ".Length();
var s = sqlReader["CompressedImage"].ToString().Substring(offset);
var bytes = Convert.FromBase64String(s);
using (var zis = new ZipInputStream(new MemoryStream(bytes)))
{
...
zis.Read(...);
...
}
If the data is "really long", you're going to want to stream it out of that table, rather than just read it into a big string and convert it. I don't know how large text columns can be, but supposing that it could be 500mb, you don't want a 500mb string, and you don't want to do a conversion of a 500mb string with Convert.FromBase64String(). In that case You need to use a Base64Stream, or the FromBase64Transform class in the System.Security.Cryptography namespace.
Editorial comment. It is sort of backwards to zip-compress image data. The images are probably compressed already. But to compound that backwardsness by then doing a base64 encode, thereby expanding the data... ??? That is triple backwards. That makes noooooo sense at all. I understand that's how your vendor supplied it.
Ok, with your furhter update, using this as the format:
ImageID ImageData
1 FORMAT-ZIPV2 504B03041400020008005157422A2E25FDBAF26701008D6901000E...
2 FORMAT-ZIPV2 504B03041400020008009159422A7FC94BA2B2540500D35705000E...
That data is still zipfile data, but it is encoded as simple hex digits. You need to convert that to a byte array. Here's some code to do it.
public static class ConvertEx
{
static readonly String prefix= "FORMAT-ZIPV2 ";
public static string ToHexString(byte[] b)
{
System.Text.StringBuilder sb1 = new System.Text.StringBuilder();
int i = 0;
for (i = 0; i < b.Length; i++)
{
sb1.Append(System.String.Format("{0:X2}", b[i]));
}
return sb1.ToString().ToLower();
}
public static byte[] ToByteArray(string s)
{
if (s.StartsWith(prefix))
{
System.Console.WriteLine("removing prefix");
s = s.Substring(prefix.Length);
}
s= s.Trim(); // whitespace
System.Console.WriteLine("length: {0}", s.Length);
var r= new byte[s.Length/2];
for (int i = 0; i < s.Length; i+=2)
{
r[i/2] = (byte) Convert.ToUInt32(s.Substring(i,2), 16);
}
return r;
}
}
You can use that this way:
string s = GetStringContentFromDatabase()
var decoded = ConvertEx.ToByteArray(s);
using (var ms = new MemoryStream(decoded))
{
// use DotNetZip to read the zip file
// SharpZipLib is something similar...
using (var zip = ZipFile.Read(ms))
{
// print out the list of entries in the zipfile
foreach (var e in zip)
{
System.Console.WriteLine("{0}", e.FileName);
}
}
}
The examples on the SharpZip Wiki use Stream objects - while the sample does use a File, you could easily use a MemoryStream object here and the sample would work the same.

ActionScript: How to push to multidimensional arrays and later retrieve just one 'row'

I am reading a set of latitude & Longitude Coordinates that define a polygone area. They are keyed to an area ID and I retrieve them from a SQL database. So for example, Area ID 153 might have 20 coordinates and area ID 77 might have 11 coordinates. I wish to save these in a 2-D array indexed by the area ID, and where each coordinate pair is combined into one Google LatLng object. At a later point I wish to retrieve just one row i.e. the set of coordinates for one area, and send them to a function that accepts an array of coordinates and draws the polygon on a map. Here's what I have:
private var coordsFromSql:ArrayCollection = new ArrayCollection();
var polyArray:Array = new Array();
for each(var item:COORDINATES in coordsFromSql)
{
// add coordinates to the array for each Area id
polyArray[item.AREA_ID].push( new LatLng(item.LATITUDE, item.LONGITUDE) );
}
So this is where the first problem ocurrs. I don't know how to add a variable number of new items to a 2-D array into a known index. i.e considering polyArray like a 2-D spreadsheet how do I for example add values to 'row' 77 i.e. polyArray[77] ?
If I run the above code, I get runtime error #1010 'A term is undefined and has no properties'
The second part of the question is how do you extract one 'row' as a new array?
Using the above example to call a drawPolygon function, can I do this?
var polyArraySlice:Array = polyArray[77].slice();
drawPolygon(color, polyArraySlice );
It looks like your loading code is close, but not quite. In your for loop you're doing:
polyArray[item.AREA_ID].push(/*...*/)
but you never actually put anything in the array there.
So your load would probably be something like this:
var polyArray:Array = []
for each(var item:COORDINATES in coordsFromSql)
{
// add coordinates to the array for each Area id
var id:Number = item.AREA_ID;
if(polyArray[id] == null) { polyArray[id] = [] }
polyArray[id].push( new LatLng(item.LATITUDE, item.LONGITUDE) );
}
Getting a copy of the one of the individual locations would work just like you had:
var polyArraySlice:Array = polyArray[77].slice();
drawPolygon(color, polyArraySlice );

Resources