I'm trying to autolevel an image. My code looks like this:
MagickImage image = new MagickImage(stream);
image.AutoLevel(Channels.RGB);
Later in the code I'm sending the image to a web response.
For some reason, this code has no effect on the image. It looks exactly the same as the original. If I change to:
MagickImage image = new MagickImage(stream);
image.Posterize(2);
Then I clearly see the filter applied.
What am I missing with AutoLevel?
Update:
I tried this code:
var image1 = Image.Clone();
Image.AutoLevel(Channels.RGB);
var diff = Image.Compare(image1, ErrorMetric.RootMeanSquared);
and the value of diff is 0.0, while with this code (and using the same image):
var image1 = Image.Clone();
Image.Equalize();
var diff = Image.Compare(image1, ErrorMetric.RootMeanSquared);
the value of diff is 0.315
The effect of AutoLevel might not be noticeable depending on your input image. I did a quick test with the following code:
using (MagickImage imageA = new MagickImage("logo:"))
{
imageA.Write(#"c:\imageA.jpg")
imageA.AutoLevel(Channels.Default);
using (MagickImage imageB = new MagickImage("logo:"))
{
double difference = imageA.Compare(imageB, ErrorMetric.RootMeanSquared);
Assert.AreNotEqual(0.0, difference);
imageB.Write(#"c:\imageB.jpg")
}
}
This tests passed but the value of difference is very small. This means that when you compare the images with your eyes you will probably not be able to find a big difference.
And below is an example that will show you that the AutoLevel method does something when you use another input image.
using (MagickImage imageA = new MagickImage("gradient:gray70-gray30", 150, 100))
{
imageA.Write(#"c:\imageA.jpg")
imageA.AutoLevel(Channels.Default);
using (MagickImage imageB = new MagickImage("gradient:gray70-gray30", 150, 100))
{
double difference = imageA.Compare(imageB, ErrorMetric.RootMeanSquared);
Assert.AreNotEqual(0.0, difference);
imageB.Write(#"c:\imageB.jpg")
}
}
Related
I have an image source that I convert to a transformed bitmap and add a rotate transform for rotation. I have this code via mouse double click. My problem is, sometimes it rotates, sometimes it doesn't (Visually). I put a break point on the mouse click, and I'm sure that it executes the code inside every time I perform a double click. By the way, I'm saving the angle that I use for the rotation that's why when I reload the image and put the saved angle on a render transform inside the image. It rotates to the saved angle.
Here's the code that I am using.
var dropCanvas = _dropTarget as ItemsControl;
var obj = _baseImage.Tag as FloorObjectViewModel;
var floorItem = (dropCanvas.ItemsSource as BindingList<FloorObjectViewModel>).ElementAt((dropCanvas.ItemsSource as BindingList<FloorObjectViewModel>).IndexOf(obj));
floorItem.Angle += 90;
if (floorItem.Angle > 360)
{
floorItem.Angle = 0;
}
TransformedBitmap transformBmp = new TransformedBitmap();
transformBmp.BeginInit();
transformBmp.Source = _baseImage.Source as BitmapSource;
RotateTransform rotateTransform = new RotateTransform();
rotateTransform.CenterX = _baseImage.ActualWidth / 2.0;
rotateTransform.CenterY = _baseImage.ActualHeight / 2.0;
rotateTransform.Angle = floorItem.Angle;
transformBmp.Transform = rotateTransform;
transformBmp.EndInit();
_baseImage.Source = transformBmp;
I want to make it rotate every time I double click it. But it seems something was still executing on background when I perform a double click right after a first double click. (Sorry for being redundant, I hope you guys got my problem)
Any ideas? Thanks!
So, this app I am making for mobile phones with Haxe, and OpenFL.
I have a very long text which I load from a text file, and put it into a very tall TextField. However I want to convert this into a Bitmap, due to performance issues.
Again, however, a very tall Bitmap drawn from a text field just shows blank (maybe too much data?), so I decided to split the bitmap data into "pages" bitmaps, which the user can swipe on screen.
When I add the first "page" to display, it does. But rest of the "pages" just show as a blank image.
Here's my code:
images = new Array();
var contentHeight:Float = 560;
field = new TextField();
var fieldFont = Assets.getFont("fonts/Kreon-Regular.ttf");
var format:TextFormat = new TextFormat(fieldFont.fontName, 26 /*currentZoom*/, 0xffffff);// 0x4F4F4F);
format.align = TextFormatAlign.LEFT;
field.defaultTextFormat = format;
var fieldWidth:Float = 410;
field.embedFonts = true;
field.text = fullText;
field.selectable = false;
field.wordWrap = true;
field.border = false;
field.autoSize = TextFieldAutoSize.LEFT;
field.width = fieldWidth;
//field.x = 0;
//addChild(field);
//loop through lines, if line within reach, increase clip height, else make new bd
var clipY:Float = 0;
var clipHeight:Float = 0;
trace(field.numLines);
var h_:Float = field.getLineMetrics(0).height;
var bd:BitmapData;
var mainBd:BitmapData = new BitmapData(Std.int(field.width), Std.int(field.height), true, 0x00000000);
mainBd.draw(field);
for (i in 0... field.numLines)
{
try {
h_ = field.getLineMetrics(i).height+0.2;
} catch (e:Dynamic) {}
if (clipHeight < contentHeight + h_)//line can be accomodated
{
clipHeight += h_;
}
else { //can't be accomodated, do clipping
bd = new BitmapData(Std.int(field.width), Std.int(clipHeight + 5), true, 0x00000000);
trace("clip: clipY:" + clipY + " height:" + clipHeight);
bd.copyPixels(mainBd, new Rectangle(0, clipY, field.width, clipHeight), new Point(0, clipY));
//bd.draw(field, new Matrix(), new ColorTransform(), BlendMode.NORMAL, new Rectangle(0, clipY, field.width, clipHeight), true);
images.push(new Bitmap(bd, PixelSnapping.AUTO, true));
clipY += clipHeight;
clipHeight = 0;
}
}
addChild(images[1]);
You only add one of your images (addChild(images[1]);) to view.
Also, I'd recommend, you to :
Find the exact problem with displaying it as a single TextField, start with the exact amount of text needed for it to break.
Check if there are some weird Unicode characters in your text, they may just plain break the renderer layouting for example(which should be counted as a bug in renderer, but still the way to fix it is to remove them).
If you can't fix that problem, use multiple textfields instead of bitmaps, since with a large text you have a high chance to exceed memory limits(one page of your text currently takes around 1Mb of memory, which is A LOT.
The way you do this currently wouldn't work in any case since you do the same renderer normally does. If you want to render it to partial bitmaps(and there is a real problem with rendering big texts), you need to divide the text in parts and render each part individually. (Basically the same as using multiple textfields with cacheAsBitmap).
NOTE: Bitmaps shouldn't actually be any faster than TextFields, unless you use a VERY fancy font or a lot of filters. In any case, cacheAsBitmap property should do what you want automagically, without writing all this code. But I'm 99% sure thats not the case and you don't need that.
In Flash, I created a grid of 400 buttons with instance names c0 through c399.
In Actionscript, I'd like to create an array like this:
var myArray:Array = [c0,c1,c2,c3,c4,c5,c6];
all the way up to c399.
I wrote a for-loop to do the trick, but it doesn't seem to be working:
import flash.events.MouseEvent;
//create the array
var myArray:Array = [];
for (var i:int=0;i<399;i++){
var cletter:String = 'c';
var p:String = i.toString();
var newvalue:String = cletter + p;
var shizzle:Object = new SimpleButton();
myArray[i] = shizzle;
}
for each(var btn in myArray){
btn.addEventListener(MouseEvent.CLICK, onBtnClick);
}
function onBtnClick(event:MouseEvent):void{
cellinfo.gotoAndStop(event.target.name);
}
When I publish it, no errors show up and nothing happens when I click the buttons. However, if I use
var myArray:Array = [c0,c1,c2,c3,c4,c5,c6];
it does work! (for the first 7 buttons at least).
Also, when I put:
for (var i:int=1;i<6;i++){
var cletter:String = 'c';
var p:String = i.toString();
var newvalue:Object = cletter + p;
myArray[i] = newvalue;
}
it says:
TypeError: Error #1006: value is not a function. at
PVproject1_fla::MainTimeline/frame1()
I just started working with AS3 + Flash and spent hours looking for a solution. Please help!
Your code is broken in more than one way.
for (var i:int=0;i<399;i++){
var cletter:String = 'c';
var p:String = i.toString();
var newvalue:String = cletter + p; // => this is never used
var shizzle:Object = new SimpleButton(); // => this creates a new (!) button
myArray[i] = shizzle;
}
Each of the 400 newly created buttons is never added to the stage, therefore you can't see it. And since it has no skin or other visual properties, you wouldn't be able to see it if it were.
The second for-loop puts only the names into the array - which causes the "value is not a function" error when you try to access the array values as buttons, because the value is really a string:
for (var i:int=1;i<6;i++){
var cletter:String = 'c';
var p:String = i.toString();
var newvalue:Object = cletter + p; // <= this is a String!
myArray[i] = newvalue;
}
Now apart from a fundamental doubt whether you'd really want to create 400 button instances by hand (I'd think about doing it in ActionScript and using this actual creation to fill the array), you can do the following - but remember: only if the button instances are already on the stage, and the loop is located in a frame script!
for (var i:int=0;i<399;i++){
myArray[i] = this["c"+i]; // no need for all the p and .toString() stuff, btw
}
Another solution is to just watch for them to be added to the stage and capture them at that point, similar to one of the examples you can download here http://flexdiary.blogspot.com/2010/04/sample-code-for-oop-timeline-insideria.html
Doing something like this doesn't work.
foreach (Image img in Canvas1.Children.OfType<Image>())
{
double h = img.Height / 2;
double w = img.Width / 2;
img.Height = h;
img.Width = w;
img.UpdateLayout();
Canvas1.UpdateLayout();
}
Must I transform the BitmapImage which I assign to the Image with some kind
of Transform-class or something like that?
I just tested changing the size of images and it worked for me:
foreach (var item in _grid.Children.OfType<Image>())
{
item.Width = 400; // Update calls are unnecessary
}
One thing that might be wrong with your code is that you access Image.Height/Width without setting it first. If those properties are not set they are on Auto (which is Double.NaN), if you want to retrieve the current values which are calculated by the layout system use ActualHeight/Width.
A few months ago I built some online samples like this one from Jeff Prosise that use the WriteableBitmap class in Silverlight.
Revisiting them today with the latest Silverlight3 installer (3.0.40624.0), the API seems to have changed.
I figured out some of the changes. For example, the WriteableBitmap array accessor has disappeared, but I found it in the new Pixels property, so instead of writing:
bmp[x]
I can write
bmp.Pixels[x]
Are there similar simple replacements for these calls, or has the use pattern itself changed?
bmp = new WriteableBitmap(width, height, PixelFormats.Bgr32);
bmp.Lock();
bmp.Unlock();
Can anybody point me to a working example using the updated API?
Another important detail about switching to the new WriteableBitmap is given in this answer ... because the pixel format is now always pbgra32, you must set an alpha value for each pixel, otherwise you just get an all-white picture. In other words, code that formerly generated pixel values like this:
byte[] components = new byte[4];
components[0] = (byte)(blue % 256); // blue
components[1] = (byte)(grn % 256); // green
components[2] = (byte)(red % 256); // red
components[3] = 0; // unused
should be changed to read:
byte[] components = new byte[4];
components[0] = (byte)(blue % 256); // blue
components[1] = (byte)(grn % 256); // green
components[2] = (byte)(red % 256); // red
components[3] = 255; // alpha
What happens if you don't use Lock and Unlock and just use the WritabelBitmap(int, int) constructor? Do things break?
It would seem that between SL3 Beta and the release this API has changed. See Breaking Changes Document Errata (Silverlight 3)