Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drawing Histogram needs more accuracy in iPhone

I am working in an app in which I need to draw Histogram of any inputed image. I can draw Histogram successfully but its not so sharp as its in PREVIEW in Mac OS.

As code is too large ,I have uploaded it to GitHub Click here to Download

The RGB Values are read in

  -(void)readImage:(UIImage*)image
{
    CGImageRef imageRef = [image CGImage];
    NSUInteger width = CGImageGetWidth(imageRef);
    NSUInteger height = CGImageGetHeight(imageRef);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    unsigned char *rawData = (unsigned char*) calloc(height * width * 4, sizeof(unsigned char));
    NSUInteger bytesPerPixel = 4;
    NSUInteger bytesPerRow = bytesPerPixel * width;
    NSUInteger bitsPerComponent = 8;
    CGContextRef context = CGBitmapContextCreate(rawData, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    CGColorSpaceRelease(colorSpace);
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
    CGContextRelease(context);

    for (int yy=0;yy<height; yy++)
    {
        for (int xx=0; xx<width; xx++)
        {
            // Now your rawData contains the image data in the RGBA8888 pixel format.
            int byteIndex = (bytesPerRow * yy) + xx * bytesPerPixel;
            for (int ii = 0 ; ii < 1 ; ++ii)
            {
                CGFloat red   = (rawData[byteIndex]     * 1.0) ;
                CGFloat green = (rawData[byteIndex + 1] * 1.0) ;
                CGFloat blue  = (rawData[byteIndex + 2] * 1.0) ;
                // CGFloat alpha = (rawData[byteIndex + 3] * 1.0) / 255.0;
                byteIndex += 4;

                // TYPE CASTING ABOVE FLOAT VALUES TO THAT THEY CAN BE MATCHED WITH ARRAY'S INDEX.

                int redValue = (int)red;
                int greenValue = (int)green;
                int blueValue = (int)blue;


                // THESE COUNTERS COUNT " TOTAL NUMBER OF PIXELS " FOR THAT  Red , Green or Blue Value IN ENTIRE IMAGE.

                fltR[redValue]++;
                fltG[greenValue]++;
                fltB[blueValue]++;                
            }
        }
    }
    [self makeArrays];
    free(rawData);
}

I stored values in c array variables , fltR,fltG,fltB.

I have a class ClsDrawPoint it has members

@property CGFloat x;
@property CGFloat y;

Then prepared an array containing objects of ClsDrawPoint having fltR[]'s index as X value and value for that index as Y value .

array is prepared and graph is drawn in

-(void)makeArrays

method

You may see the result

enter image description here

Currently its not so accurate as it is in PREVIEW in mac for the same image. You can open an image in PREVIEW app in Mac and in Tools>AdjustColor , you will be able to see the Histogram of that image. I think if my graph is accrate , it will be sharper. Kindly check my code and suggest me if you find anyway to make it more accurate.

like image 563
HarshIT Avatar asked Oct 26 '12 11:10

HarshIT


1 Answers

I pulled your sample down from Github and found that your drawRect: method in ClsDraw is drawing lines that are one pixel wide. Strokes are centered on the line, and with a width of 1, the stroke is split into half-pixels which introduces anti-aliasing.

I moved your horizontal offsets by a half-pixel and rendering looks sharp. I didn't mess with vertical offsets, but to make them sharp you would need to round them and then move them to a half-pixel offset as well. I only made the following change:

#define OFFSET_X 0.5

- (void)drawRect:(CGRect)rect
{   
  CGContextRef ctx = UIGraphicsGetCurrentContext();
  if ([arrPoints count] > 0)
  {
    CGContextSetLineWidth(ctx, 1);
    CGContextSetStrokeColorWithColor(ctx, graphColor.CGColor);
    CGContextSetAlpha(ctx, 0.8);
    ClsDrawPoint *objPoint;
    CGContextBeginPath(ctx);

    for (int i = 0 ; i < [arrPoints count] ; i++)
    {
      objPoint = [arrPoints objectAtIndex:i];
      CGPoint adjustedPoint = CGPointMake(objPoint.x+OFFSET_X, objPoint.y);
      CGContextMoveToPoint(ctx, adjustedPoint.x, 0);
      CGContextSetLineCap(ctx, kCGLineCapRound);
      CGContextSetLineJoin(ctx, kCGLineJoinRound);
      CGContextAddLineToPoint(ctx, adjustedPoint.x, adjustedPoint.y);
      CGContextMoveToPoint(ctx, adjustedPoint.x, adjustedPoint.y);
      CGContextStrokePath(ctx);
    }
  }
}

Notice the new OFFSET_X and the introduction of adjustedPoint.

You might also consider using a CGPoint stuffed into an NSValue instance for your points instead of your custom class ClsDrawPoint, unless you plan to add some additional behavior or properties. More details available here.

like image 169
MattP Avatar answered Sep 29 '22 16:09

MattP