Magickwand conversion to TIFF&CCITT group 4 compression gives uncompressed image - imagemagick

Trying to convert a 24 bpp bitmap to black & white TIFF with CCITT group 4 compression.
The result is a TIFF 1 bpp image as expected but it is uncompressed.
I'm using FreePascal which has magickwand bindings and the status is never MagickFalse:
MagickWandGenesis;
wand := NewMagickWand;
try
status := MagickReadImage(wand,PChar(InputFile));
if (status = MagickFalse) then HandleError;
status := MagickSetImageFormat(wand,'TIFF');
if (status = MagickFalse) then HandleError;
// convert to black & white/lineart
status := MagickSetImageType(wand,BilevelType);
if (status = MagickFalse) then HandleError;
// Group4Compression seems defined as 4 which
// apparently doesn't match imagemagick source. Bug:
//http://mantis.freepascal.org/view.php?id=26723
status := MagickSetImageCompression(wand,CompressionType(7)); //was Group4Compression
if (status = MagickFalse) then HandleError;
// Apparently set(image)compresionquality and
// stripimage are necessary to actually compress
status := MagickSetImageCompressionQuality(wand,0);
if (status = MagickFalse) then HandleError;
status := MagickStripImage(wand);
if (status = MagickFalse) then HandleError;
status := MagickWriteImage(wand,PChar(OutputFile));
if (status = MagickFalse) then HandleError;
finally
wand := DestroyMagickWand(wand);
end;
MagickWandTerminus;
Source image at http://filehorst.de/d/bmqjzDuB
Original (faulty) program source code at http://filehorst.de/d/bluhjivq
Original (faulty) output image at http://filehorst.de/d/bhlbjHgp
What am I doing wrong?
Edit: solved; got the solution off-site: the CompressionType enum in the FreePascal bindings were probably out of date - Group4Compression was 4 (IIRC) while it should be 7.
I'll give the bounty to Mark Setchell as his answer was a necessary part of the solution. Source code above updated with correct version.

With the PHP version at least, it seems that setting the compression type does not actually compress the image - see comments at bottom here.
It also shows in all examples I have found that you must subsequently call MagickSetImageCompressionQuality() and StripImage() to actually do the compression - see here.

Related

libtiff: TIFFGetField returns incorrect image' width and height

I have a tiff file with 1736x1160x48b resolution. I'm using libtiff 4.0 dll and I have noticed that the resolution being read from the TIFFGetField function is incorrect. I'm getting 156 width and 104 height which is too low.
TIFF* pTiff;
pTiff = dll.OpenTIFF(szFilePath, (char*)("r"));
if (pTiff == nullptr) {
return LPROCESS_FAILED;
}
uint32_t uWidth = 0;
uint32_t uHeight = 0;
int ret = 0;
ret = dll.TIFFGetField(pTiff, TIFFTAG_IMAGEWIDTH, &uWidth);
ret = dll.TIFFGetField(pTiff, TIFFTAG_IMAGELENGTH, &uHeight );
// ret value is 1 on both lines
Do I need to update my tiff library to 4.5? Is there a fix on the latest version? or is this a format error on the image side? Though when I try to open the image on other photo viewing application, it was able to interpret correctly.
Update: I used libtiff 4.5 latest release and I'm still getting incorrect width and height. My guess is that the image I'm trying to load is not yet fully supported by libtiff. Here is the details of the image from exiftool:

FFmpeg iOS Get UIImage from AVFrame

I am having trouble getting a UIImage out of the frames I am reading into my iOS FFmpeg project. I need to be able to read a frame in, and then convert this to a UIImage in order to display the frame in a UIImageView. My code appears to be reading in the frames, but I am lost as to how to convert them as there is little documentation on how to do this. Can anyone help?
while (!finished) {
if (av_read_frame(_formatContext, &packet) >= 0) {
if (packet.stream_index == _videoStream) {
int ret = avcodec_send_packet(_codecContext, &packet);
if (ret < 0 || ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
printf("av_codec_send_packet error ");
}
while (ret >= 0) {
ret = avcodec_receive_frame(_codecContext, _frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
printf("avcodec_receive_frame error ");
}
finished = true;
}
}
av_packet_unref(&packet);
}
}
You should know about pixel formats like rgb and yuv. Videos almost always uses yuv formats like yuv420p. Then study AVFrame structure, here some info:
AVFormat.format : Current frame's pixel format i.e. AV_PIX_FMT_YUV420P
AVFormat.width : Horizontal length of current frame (hence width) unit: pixels
AVFormat.height : Vertical length of current frame (hence height) unit: pixels
Now where is the actual frame buffer you might ask, it is in AVFormat.data[n]
n can be 0-3. Depending on the format, just first one may contain whole frame or all 4 of them. I.e. yuv420p uses 0, 1, and 2. Their linesizes (aka strides) can be obtained reading corresponding AVFormat.linesize[n] value.
As for yuv420p:
data[0] is Y plane
data[1] is U plane
data[2] is V plane
If you multiply linesize[0] with AVFrame.height, you'll get size of that plane (Y) as number of bytes.
I don't know about UIImage structure (or whatever it is), if it requeris a specific format like RGB, you need to convert your AVFrame to that format using swscale.
Here some examples: https://github.com/FFmpeg/FFmpeg/blob/master/doc/examples/scaling_video.c
In libav (ffmpeg) scaling (resizing) and pixel format conversion are done via same function.
Hope these helps.

Playing Percussion instruments with midi on iOS

I have an app that handles playing multiple midi instruments. Everything works great except for playing percussion instruments. I understand that in order to play percussion in General MIDI you must send the events to channel 10. I've tried a bunch of different things, and I can't figure out how to get it to work, here's an example of how I'm doing it for melodic instruments vs percussion.
// Melodic instrument
MusicDeviceMIDIEvent(self.samplerUnit, 0x90, (UInt8)pitch, 127, 0);
// Percussion Instruments
MusicDeviceMIDIEvent(self.samplerUnit, 0x99, (UInt8)pitch, 127, 0);
The sampler Unit is an AudioUnit and the pitch is given as an int through my UI.
Thanks in advance!
Assuming you have some sort of a General MIDI sound font or similar loaded, you need to set the correct status byte before sending pitch/velocity information. So in case of a Standard MIDI Drum Kit (channel 9), you'd do something like this in Swift:
var status = OSStatus(noErr)
let drumCommand = UInt32( 0xC9 | 0 )
let noteOnCommand = UInt32(0x90 | channel)
status = MusicDeviceMIDIEvent(self._samplerUnit, drumCommand, 0, 0, 0) // set device
status = MusicDeviceMIDIEvent(self._samplerUnit, noteOnCommand, noteNum, velocity, 0) // sends note ON message
No need to undertake anything special for MIDI note off messages.
Ok, so I got it working. I guess the way I load the sound font makes it so the channel stuff doesn't do anything. Instead I had to set the bankMSB property on AUSamplerBankPresetData to kAUSampler_DefaultPercussionBankMSB instead of kAUSampler_DefaultMelodicBankMSB
I added a different font loading method specifically for percussion:
- (OSStatus) loadPercussionWithSoundFont: (NSURL *)bankURL {
OSStatus result = noErr;
// fill out a bank preset data structure
AUSamplerBankPresetData bpdata;
bpdata.bankURL = (__bridge CFURLRef) bankURL;
bpdata.bankMSB = kAUSampler_DefaultPercussionBankMSB;
bpdata.bankLSB = kAUSampler_DefaultBankLSB;
bpdata.presetID = (UInt8) 32;
// set the kAUSamplerProperty_LoadPresetFromBank property
result = AudioUnitSetProperty(self.samplerUnit,
kAUSamplerProperty_LoadPresetFromBank,
kAudioUnitScope_Global,
0,
&bpdata,
sizeof(bpdata));
// check for errors
NSCAssert (result == noErr,
#"Unable to set the preset property on the Sampler. Error code:%d '%.4s'",
(int) result,
(const char *)&result);
return result;
}

Go Resizing Images

I am using the Go resize package here: https://github.com/nfnt/resize
I am pulling an Image from S3, as such:
image_data, err := mybucket.Get(key)
// this gives me data []byte
After that, I need to resize the image:
new_image := resize.Resize(160, 0, original_image, resize.Lanczos3)
// problem is that the original_image has to be of type image.Image
Upload the image to my S3 bucket
err : = mybucket.Put('newpath', new_image, 'image/jpg', 'aclstring')
// problem is that new image needs to be data []byte
How do I transform a data []byte to ---> image.Image and back to ----> data []byte?
Read http://golang.org/pkg/image
// you need the image package, and a format package for encoding/decoding
import (
"bytes"
"image"
"image/jpeg" // if you don't need to use jpeg.Encode, use this line instead
// _ "image/jpeg"
"github.com/nfnt/resize"
)
// Decoding gives you an Image.
// If you have an io.Reader already, you can give that to Decode
// without reading it into a []byte.
image, _, err := image.Decode(bytes.NewReader(data))
// check err
newImage := resize.Resize(160, 0, original_image, resize.Lanczos3)
// Encode uses a Writer, use a Buffer if you need the raw []byte
err = jpeg.Encode(someWriter, newImage, nil)
// check err
The OP is using a specific library/package, but I think that the issue of "Go Resizing Images" can be solved without that package.
You can resize de image using golang.org/x/image/draw:
input, _ := os.Open("your_image.png")
defer input.Close()
output, _ := os.Create("your_image_resized.png")
defer output.Close()
// Decode the image (from PNG to image.Image):
src, _ := png.Decode(input)
// Set the expected size that you want:
dst := image.NewRGBA(image.Rect(0, 0, src.Bounds().Max.X/2, src.Bounds().Max.Y/2))
// Resize:
draw.NearestNeighbor.Scale(dst, dst.Rect, src, src.Bounds(), draw.Over, nil)
// Encode to `output`:
png.Encode(output, dst)
In that case I choose draw.NearestNeighbor, because it's faster, but looks worse. but there's other methods, you can see on https://pkg.go.dev/golang.org/x/image/draw#pkg-variables:
draw.NearestNeighbor
NearestNeighbor is the nearest neighbor interpolator. It is very fast, but usually gives very low quality results. When scaling up, the result will look 'blocky'.
draw.ApproxBiLinear
ApproxBiLinear is a mixture of the nearest neighbor and bi-linear interpolators. It is fast, but usually gives medium quality results.
draw.BiLinear
BiLinear is the tent kernel. It is slow, but usually gives high quality results.
draw.CatmullRom
CatmullRom is the Catmull-Rom kernel. It is very slow, but usually gives very high quality results.
Want to do it 29 times faster? Try amazing vipsthumbnail instead:
sudo apt-get install libvips-tools
vipsthumbnail --help-all
This will resize and nicely crop saving result to a file:
vipsthumbnail original.jpg -s 700x200 -o 700x200.jpg -c
Calling from Go:
func resizeExternally(from string, to string, width uint, height uint) error {
var args = []string{
"--size", strconv.FormatUint(uint64(width), 10) + "x" +
strconv.FormatUint(uint64(height), 10),
"--output", to,
"--crop",
from,
}
path, err := exec.LookPath("vipsthumbnail")
if err != nil {
return err
}
cmd := exec.Command(path, args...)
return cmd.Run()
}
You could use bimg, which is powered by libvips (a fast image processing library written in C).
If you are looking for a image resizing solution as a service, take a look to imaginary

CvVideoWriter WriteFrame for iOS not working

I'm using a pre-compiled version of OpenCV I got from here: http://aptogo.co.uk/2011/09/opencv-framework-for-ios/
I'm working on a Ogre3D Application, and I'm trying to grab screens from the app to stitch together to make a video. I've successfully gotten this to work on Windows using OpenCV's CvVideoWriter.
Unfortunately, I'm running into some problems on the iOS version of this. My code is as follows:
IplImage *mCvCaptureImage, *mCvConvertImage;
mCvCaptureImage = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 3);
mCvConvertImage = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 3);
PixelFormat pf = win->suggestPixelFormat();
PixelBox pb(width, height, 1, pf, mCvCaptureImage->imageData);
win->copyContentsToMemory(pb);
cvConvertImage(mCvCaptureImage, mCvConvertImage, CV_CVTIMG_SWAP_RB);
cvWriteFrame(writer, mCvConvertImage );
cvReleaseImage(&mCvConvertImage);
cvReleaseImage(&mCvCaptureImage);
The cvWriteFrame call is throwing an error due to writer status check. I checked the source code, and this seems to be the problem:
// writer status check
if (![mMovieWriterInput isReadyForMoreMediaData] || mMovieWriter.status != AVAssetWriterStatusWriting ) {
NSLog(#"[mMovieWriterInput isReadyForMoreMediaData] Not ready for media data or ...");
NSLog(#"mMovieWriter.status: %d. Error: %#", mMovieWriter.status,[mMovieWriter.error localizedDescription]);
[localpool drain];
return false;
}
Anyone has any ideas why this is happening and how it can be fixed?
Thanks!
Change the file extension to m4v and delete existing file before calling cvCreateVideoWriter()
( opencv chooses filetype looking at file extension )

Resources