Cropping texture2d on all sides in xna c# - c#

i am trying to crop a texture2d in xna. i have found the following code which will crop the image on the top and right sides, i have played around with the code and cannot figure a way to crop all sides at a specific interval. below is the code i have been trying to modify:
any help or ideas would be much appreciated.
Rectangle area = new Rectangle(0, 0, 580, 480);
Texture2D cropped = new Texture2D(heightMap1.GraphicsDevice, area.Width, area.Height);
Color[] data = new Color[heightMap1.Width * heightMap1.Height];
Color[] cropData = new Color[cropped.Width * cropped.Height];
heightMap1.GetData(data);
int index = 0;
for (int y = 0; y < area.Y + area.Height; y++) // for each row
{
for (int x = 0; x < area.X + area.Width; x++) // for each column
{
cropData[index] = data[x + (y * heightMap1.Width)];
index++;
}
}
cropped.SetData(cropData);

Here is the code to crop a texture. Note that the GetData method can already select rectangular subsection of the image - there is no need to manually crop.
// Get your texture
Texture2D texture = Content.Load<Texture2D>("myTexture");
// Calculate the cropped boundary
Rectangle newBounds = texture.Bounds;
const int resizeBy = 20;
newBounds.X += resizeBy;
newBounds.Y += resizeBy;
newBounds.Width -= resizeBy * 2;
newBounds.Height -= resizeBy * 2;
// Create a new texture of the desired size
Texture2D croppedTexture = new Texture2D(GraphicsDevice, newBounds.Width, newBounds.Height);
// Copy the data from the cropped region into a buffer, then into the new texture
Color[] data = new Color[newBounds.Width * newBounds.Height];
texture.GetData(0, newBounds, data, 0, newBounds.Width * newBounds.Height);
croppedTexture.SetData(data);
Of course, keep in mind that SpriteBatch.Draw can take a sourceRectangle parameter, so you may not even need to copy the texture data around at all! Just use a subsection of the original texture. For example:
spriteBatch.Draw(texture, Vector2.Zero, newBounds, Color.White);
(Where newBounds is calculated in the same way as in the first code listing.)

Related

Extract circle area from an image in EMGUCV C#

I use:
Emgucv 4.0.1
Opencv 4.1.0
I have a series of circles detected with HoughCircles function that I need to analize one by one.
I need to calculate how much color is in the rounded green circle, so I have to extract only the circle image but I know how to extract only the box that contains much more pixel than the circle. How to extract only the image inside the surrounded green area?
See images in link below.
1) Source Image
2) Boxed image I retrieved with more pixel than i want
3) Image that I would like to extract
// m_ListCircles = list from HoughCircles() circles coordinates.
// cell = cell number to extract.
// pictureBox1 = main picturebox with all circles detected.
// pictureBoxROI = picturebox destination single circles cutted.
int id = (int)m_ListCircles[0 + cell ];
int x = (int)m_ListCircles[1 + cell ];
int y = (int)m_ListCircles[2 + cell ];
int r = (int)m_ListCircles[3 + cell ]; // radius
// box area around the circle
int X0 = x;
int Y0 = y;
int X1 = x + r * 2;
int Y1 = y + r * 2;
// area to copy
int wid = Math.Abs(X0 - X1);
int hgt = Math.Abs(Y0 - Y1);
if ((wid < 1) || (hgt < 1)) return;
// create a rectangle are to copy
Rectangle source_rectangle = new Rectangle(Math.Min(X0, X1),Math.Min(Y0,Y1), wid, hgt);
// assign the area copied to image var
var image = new Image<Bgr, byte>(new Bitmap(pictureBox1.Image));
image.ROI = source_rectangle;
// show image
pictureBoxROI.Image = image.Bitmap;
pictureBoxROI.Refresh();
/*
// tried this but result is always a black image.
Point xyCell = new Point();
xyCell.X = X0;
xyCell.Y = Y0;
Image<Gray, byte> mask = new Image<Gray, byte>(image.Width, image.Height);
CvInvoke.Circle(mask, xyCella, r, new MCvScalar(255, 255, 255), -1,
LineType.AntiAlias, 0);
Image<Bgr, byte> dest = new Image<Bgr, byte>(image.Width, image.Height);
dest = image.And(image, mask);
pictureBoxROI.Image = dest.Bitmap;
pictureBoxROI.Refresh();
*/
You can always create masks of ROI form the found circles and analyze tha images like that
Custom ROI - this shows how to use the mask
You can only have rectangular images. However you can after cutting the rectangle set all the pixels outside of the circle to transparent.
You can determine which pixels are outside of the circle by calculating their distance from the center point of your image using pythagoras.
This is very slow of course, as you must loop over all pixels, but for low pixel counts it's reasonably fast.
try
{
Image rectCroppedImage = originalImage.Clone(CropRect, originalImage.PixelFormat);
double r = rectCroppedImage.Height; // because you are centered on your circle
Bitmap img = new Bitmap(rectCroppedImage);
for (int x = 0; x < img.Width; x++)
{
for (int y = 0; y < img.Height; y++)
{
// offset to center
int virtX = x - img.Width / 2;
int virtY = y - img.Height / 2;
if (Math.Sqrt(virtX * virtX + virtY * virtY) > r)
{
img.SetPixel(x, y, Color.Transparent);
}
}
}
return img; // your circle cropped image
}
catch (Exception ex)
{
}
This could also be achieved by using a mask and "multiplying" your image with a white circle. Such a thing can be achieved for example with image magick. You can find an ImageMagick NuGet packet here: https://github.com/dlemstra/Magick.NET

Changing sprite texture pixels results in gigantic grey square

I have a 32x32 sprite that I am trying to access the pixel data of and modify.
To do this I am simply taking the sprite's texture, creating a new texture based on the old and then changing the pixel values of the new texture. I then create a new sprite with the modified texture and change the SpriteRenderer's sprite parameter to the new sprite.
However, when I actually run my script what I get is a huge grey square, easily 10x the size of the original 32x32 sprite. I'm very new to unity so I'm not sure why this is happening. Any insight would be great.
private Sprite sprite;
private Texture2D texture;
// Use this for initialization
void Start ()
{
sprite = this.gameObject.GetComponent<SpriteRenderer>().sprite;
texture = sprite.texture;
Texture2D newTexture = modifyTexture(texture);
SpriteRenderer sr = this.gameObject.GetComponent<SpriteRenderer>();
sr.sprite = Sprite.Create(newTexture, new Rect(0, 0, newTexture.width, newTexture.height), new Vector2(0, 0), 10);
}
public Texture2D modifyTexture(Texture2D baseTexture)
{
Texture2D newTexture = new Texture2D(baseTexture.width, baseTexture.height);
int x = 0;
while(x < newTexture.width)
{
int y = 0;
while(y < newTexture.height)
{
Color currentPixel = baseTexture.GetPixel(x,y);
Color modifiedPixel = currentPixel;
modifiedPixel.r = (float)(modifiedPixel.r + 0.10);
modifiedPixel.b = (float)(modifiedPixel.b + 0.10);
modifiedPixel.g = (float)(0.10);
newTexture.SetPixel(x, y, modifiedPixel);
y++;
}
x++;
}
Debug.Log(newTexture.GetPixel(5, 5).ToString());
return newTexture;
}
After modifying pixels of a Texture, you must call the Apply function upload the modified pixels to the graphics card.
public Texture2D modifyTexture(Texture2D baseTexture)
{
Texture2D newTexture = new Texture2D(baseTexture.width, baseTexture.height);
int x = 0;
while (x < newTexture.width)
{
int y = 0;
while (y < newTexture.height)
{
Color currentPixel = baseTexture.GetPixel(x, y);
Color modifiedPixel = currentPixel;
modifiedPixel.r = (float)(modifiedPixel.r + 0.10);
modifiedPixel.b = (float)(modifiedPixel.b + 0.10);
modifiedPixel.g = (float)(0.10);
newTexture.SetPixel(x, y, modifiedPixel);
y++;
}
x++;
}
//Upload changes to Graphics graphics card
newTexture.Apply();
Debug.Log(newTexture.GetPixel(5, 5).ToString());
return newTexture;
}

XNA - Culling Performance Issue

This method that draws my tiles seems to be quite slow, Im not sure exactly whats wrong, it belive my culling method isnt working and is drawing stuff offscreen, but im not completeley sure. Here it is:
// Calculate the visible range of tiles.
int left = (int)Math.Floor(cameraPosition.X / 16);
int right = left + spriteBatch.GraphicsDevice.Viewport.Width / 16;
right = Math.Min(right, Width) + 1; // Width -1 originally - didn't look good as tiles drawn on screen
if (right > tiles.GetUpperBound(0))
right = tiles.GetUpperBound(0) + 1; // adding 1 to get the last right tile drawn
int top = (int)Math.Floor(cameraPosition.Y / 16);
int bottom = left + spriteBatch.GraphicsDevice.Viewport.Height/ 16;
bottom = Math.Min(bottom, Height) + 1; // Height -1 originally - didn't look good as tiles drawn on screen
if (bottom > tiles.GetUpperBound(1))
bottom = tiles.GetUpperBound(1) + 1; // adding 1 to get the last bottom tile drawn
// For each tile position
for (int y = top; y < bottom; ++y)
{
for (int x = left; x < right; ++x)
{
// If there is a visible tile in that position, draw it
if (tiles[x, y].BlockType.Name != "Blank")
{
Texture2D texture = tileContent["DirtBlock_" + getTileSetType(tiles,x,y)];
spriteBatch.Draw(texture, new Vector2(x * 16, y * 16), Color.White);
if (isMinimap)
spriteBatch.Draw(pixel, new Vector2(30+x, 30+y), Color.White);
}
}
}
GetTileSetTypes is a function to get what tiles are around it, for different textures, like DirtBlock_North, DirtBlock_Center, etc.
Tile content is just a class with my block textures.
Try changing SpriteBatch.Begin to defered and combining all of the tiles onto one texture.
See this GameDev question for info about why deferred is most likely the fastest option for you.
Also realize that every time you draw a new texture you have to take the old one out of the GPU and put the new one in. This process is called texture swapping and usually isn't an issue but you are swapping textures twice per tile which is likely to impact performance noticeably.
This can be fixed by combining multiple sprites onto one texture and using the source rectangle argument. This allows you to draw multiple sprites without a texture swap. There are a few OSS libraries for this. Sprite Sheet Packer is my personal favorite.
Unfortunantly without the project and a profiler I'm just guessing; however, these are the two biggest gotchas for rendering tilemaps I know of. I can't really see anything wrong from here. Below is the code I use to draw my tile maps and as you see its very similar to yours.
If all else fails I would suggest using a profiler to figure out which bits are running slowly.
//Init the holder
_holder = new Rectangle(0, 0, TileWidth, TileHeight);
//Figure out the min and max tile indices to draw
var minX = Math.Max((int)Math.Floor((float)worldArea.Left / TileWidth), 0);
var maxX = Math.Min((int)Math.Ceiling((float)worldArea.Right / TileWidth), Width);
var minY = Math.Max((int)Math.Floor((float)worldArea.Top / TileHeight), 0);
var maxY = Math.Min((int)Math.Ceiling((float)worldArea.Bottom / TileHeight), Height);
for (var y = minY; y < maxY; y++) {
for (var x = minX; x < maxX; x++) {
_holder.X = x * TileWidth;
_holder.Y = y * TileHeight;
var t = tileLayer[y * Width + x];
spriteBatch.Draw(
t.Texture,
_holder,
t.SourceRectangle,
Color.White,
0,
Vector2.Zero,
t.SpriteEffects,
0);
}
}

Texture2D is turning black

I have a Texture2D that I'm loading from the Content Pipeline. That's working fine, but as soon as I try to use SetData on a completely different Texture2D all of the textures in my game go completely black:
This is in my HUDMeter class, the class that I want to be just red
Texture2D colorGrad = Content.Load<Texture2D>(GradientAsset);
Color[,] pixels = new Color[colorGrad.Width, colorGrad.Height];
Color[] pixels1D = new Color[colorGrad.Width * colorGrad.Height];
pixels = GetRedChannel(colorGrad);
pixels1D = Color2DToColor1D(pixels, colorGrad.Width);
System.Diagnostics.Debug.WriteLine(pixels[32,32]);
Gradient = colorGrad;
Gradient.SetData<Color>(pixels1D);
These are using Riemers tutorial
protected Color[,] GetRedChannel(Texture2D texture)
{
Color[,] pixels = TextureTo2DArray(texture);
Color[,] output = new Color[texture.Width, texture.Height];
for (int x = 0; x < texture.Width; x++)
{
for (int y = 0; y < texture.Height; y++)
{
output[x,y] = new Color(pixels[x,y].G, 0, 0);
}
}
return output;
}
protected Color[,] TextureTo2DArray(Texture2D texture)
{
Color[] colors1D = new Color[texture.Width * texture.Height];
texture.GetData(colors1D);
Color[,] colors2D = new Color[texture.Width, texture.Height];
for (int x = 0; x < texture.Width; x++)
for (int y = 0; y < texture.Height; y++)
colors2D[x, y] = colors1D[x + y * texture.Width];
return colors2D;
}
private Color[] Color2DToColor1D (Color[,] colors, int width)
{
Color[] output = new Color[colors.Length];
for (int x = 0; x < width; x++)
{
for (int y = 0; y < colors.Length / width; y++)
{
output[x + y * width] = colors[x % width, y % (colors.Length/width)];
}
}
return output;
}
And here is the code to draw the sprites, this works fine and is how I always draw sprites:
batch.Draw(meter.Gradient, new Vector2(X, Y), Color.White);
Update:
I've actually found that the sprites that don't use the same file are not black. Does Texture2D.SetData<>() actually change the file itself? what is the use of that?
Update:
I just tried to use the Alpha as well as RGB and it's working. I'm thinking that there's something wrong with one of the conversion methods.
If you do this:
Texture2D textureA = Content.Load<Texture2D>("MyTexture");
Texture2D textureB = Content.Load<Texture2D>("MyTexture");
Both textureA and textureB refer to the same object. So if you call SetData on one of them, it will affect both of them. This is because ContentManager keeps an internal list of resources already loaded, so it doesn't have to keep reloading the same resource.
The solution would be to create a new Texture2D object of the same size, call GetData on the one loaded by ContentManager, and then SetData on the new texture.
Example (not tested):
Color[] buffer = new Color[textureA.Width * textureA.Height];
Texture2D textureB = new Texture2D(textureA.GraphicsDevice,
textureA.Width,
textureA.Height);
textureA.GetData(buffer);
textureB.SetData(buffer);
Dispose() of the new texture when you are finished with it (eg: in your Game.UnloadContent method). But never dispose of the one loaded by ContentManager (because, like I said, it is a shared object; use ContentManager.Unload instead).

How can I use a Shader in XNA to color single pixels?

I have a standard 800x600 window in my XNA project. My goal is to color each individual pixel based on a rectangle array which holds boolean values. Currently I am using a 1x1 Texture and drawing each sprite in my array.
I am very new to XNA and come from a GDI background, so I am doing what I would have done in GDI, but it doesn't scale very well. I have been told in another question to use a Shader, but after much research, I still haven't been able to find out how to accomplish this goal.
My application loops through the X and Y coordinates of my rectangular array, does calculations based on each value, and reassigns/moves the array around. At the end, I need to update my "Canvas" with the new values. A smaller sample of my array would look like:
0,0,0,0,0,0,0
0,0,0,0,0,0,0
0,0,0,0,0,0,0
1,1,1,1,1,1,1
1,1,1,1,1,1,1
How can I use a shader to color each pixel?
A very simplified version of the calculations would be:
for (int y = _horizon; y >= 0; y--) // _horizon is my ending point
{
for (int x = _width; x >= 0; x--) // _width is obviously my x length.
{
if (grains[x, y] > 0)
{
if (grains[x, y + 1] == 0)
{
grains[x, y + 1] = grains[x, y];
grains[x, y] = 0;
}
}
}
}
..each time the update method is called, the calculations are performed and in example of the above loop, an update may look like:
Initial:
0,0,0,1,0,0,0
0,0,0,0,0,0,0
0,0,0,0,0,0,0
1,1,1,0,1,1,1
1,1,1,1,1,1,1
First:
0,0,0,0,0,0,0
0,0,0,1,0,0,0
0,0,0,0,0,0,0
1,1,1,0,1,1,1
1,1,1,1,1,1,1
Second:
0,0,0,0,0,0,0
0,0,0,0,0,0,0
0,0,0,1,0,0,0
1,1,1,0,1,1,1
1,1,1,1,1,1,1
Final:
0,0,0,0,0,0,0
0,0,0,0,0,0,0
0,0,0,0,0,0,0
1,1,1,1,1,1,1
1,1,1,1,1,1,1
Update:
After applying the Render2DTarget code and placing my pixels, I end up with an unwanted border on my pixels, always to the left. How can I remove this?
alt text http://www.refuctored.com/borders.png
alt text http://www.refuctored.com/fallingdirt.png
The some of the code for applying the textures is:
RenderTarget2D target;
Texture2D texture;
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
texture = Content.Load<Texture2D>("grain");
_width = this.Window.ClientBounds.Width - 1;
_height = this.Window.ClientBounds.Height - 1;
target = new RenderTarget2D(this.GraphicsDevice,_width, _height, 1, SurfaceFormat.Color,RenderTargetUsage.PreserveContents);
}
protected override void Draw(GameTime gameTime)
{
this.GraphicsDevice.SetRenderTarget(0, target);
this.GraphicsDevice.SetRenderTarget(0, null);
this.GraphicsDevice.Clear(Color.SkyBlue);
this.spriteBatch.Begin(SpriteBlendMode.None,SpriteSortMode.Deferred,SaveStateMode.None);
SetPixels(texture);
this.spriteBatch.End();
}
private void SetPixels(Texture2D texture)
{
for (int y = _grains.Height -1; y > 0; y--)
{
for (int x = _grains.Width-1; x > 0; x--)
{
if (_grains.GetGrain(x, y) >0)
{
this.spriteBatch.Draw(texture, new Vector2(x,y),null, _grains.GetGrainColor(x, y));
}
}
}
}
This method doesn't use pixel shaders, but if you're looking to use Texture2D's SetData method instead of making a call to SpriteBatch.Draw() for every pixel, you may find this useful. I used an array of uint instead of bool to represent your colors. If you can get away with an 8-bit color texture, you could may be able to speed this up by changing the texture format.
public class Game1 : Microsoft.Xna.Framework.Game
{
// Set width, height
const int WIDTH = 800;
const int HEIGHT = 600;
// Used to randomly fill in initial data, not necessary
Random rand;
// Graphics and spritebatch
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
// Texture you will regenerate each call to update
Texture2D texture;
// Data array you perform calculations on
uint[] data;
// Colors are represented in the texture as 0xAARRGGBB where:
// AA = alpha
// RR = red
// GG = green
// BB = blue
// Set the first color to red
const uint COLOR0 = 0xFFFF0000;
// Set the second color to blue
const uint COLOR1 = 0xFF0000FF;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
// Set width, height
graphics.PreferredBackBufferWidth = WIDTH;
graphics.PreferredBackBufferHeight = HEIGHT;
}
protected override void Initialize()
{
base.Initialize();
// Seed random, initialize array with random picks of the 2 colors
rand = new Random((int)DateTime.Now.Ticks);
data = new uint[WIDTH * HEIGHT];
loadInitialData();
}
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// Create a new texture
texture = new Texture2D(GraphicsDevice, WIDTH, HEIGHT);
}
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// Run-time error without this
// Complains you can't modify a texture that has been set on the device
GraphicsDevice.Textures[0] = null;
// Do the calculations
updateData();
// Update the texture for the next time it is drawn to the screen
texture.SetData(data);
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
// Draw the texture once
spriteBatch.Begin();
spriteBatch.Draw(texture, Vector2.Zero, Color.Purple);
spriteBatch.End();
base.Draw(gameTime);
}
private void loadInitialData()
{
// Don't know where the initial data comes from
// Just populate the array with a random selection of the two colors
for (int i = 0; i < WIDTH; i++)
for (int j = 0; j < HEIGHT; j++)
data[i * HEIGHT + j] = rand.Next(2) == 0 ? COLOR0 : COLOR1;
}
private void updateData()
{
// Rough approximation of calculations
for(int y = HEIGHT - 1; y >= 0; y--)
for (int x = WIDTH - 1; x >= 0; x--)
if (data[x * HEIGHT + y] == COLOR1)
if (y + 1 < HEIGHT && data[x * HEIGHT + (y + 1)] == COLOR0)
{
data[x * HEIGHT + (y + 1)] = data[x * HEIGHT + y];
data[x * HEIGHT + y] = COLOR0;
}
}
}
How about this...
Create two textures (800x600)
Initialize one of them to the initial values.
For each frame you render one texture to the other while updating the values in a pixelshader.
After rendering the resulting texture to the screen, you swap them, so they are ready for your next frame.
Edit:
You will need two instances of RenderTarget2D and create them with RenderTargetUsage.PreserveContents. You could start with SurfaceFormat.Color and use black for 0 and white for 1. (You may also be able to find a 8 bit format to save video memory.)
new RenderTarget2D(_device, 800, 600, 1, SurfaceFormat.Color, RenderTargetUsage.PreserveContents);
You assign them to the rendertaget like this:
_device.SetRenderTarget(0, myRenderTarget);
You use a RenderTarget2D as a texture, like this:
_device.Textures[0] = myRenderTarget.GetTexture();
Hope that helps... I can dig out more from my engine, so just ask.
What you're trying to do (draw pixel by pixel) is what DirectX does. XNA is a layer that is built on top of DirectX so that you don't have to draw pixel by pixel. If that's really what you want to do then you should probably be learning DirectX instead of XNA. You would probably find it much easier...
Add a billboard to your scene (directly in front of the camera, so that it takes up exactly 800x600 pixels).
Bind the binary array as a texture.
In the fragment (/pixel) shader calculate the color of the fragment using the 2D position of the fragment and the texture.

Categories

Resources