Skip to content

Commit aed361d

Browse files
committed
Heuristically calculate the default staff zone
#1210
1 parent 2108b22 commit aed361d

File tree

3 files changed

+75
-8
lines changed

3 files changed

+75
-8
lines changed

deployment/server/samples/mei/mei_template.mei

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<music>
1515
<facsimile type="transcription">
1616
<surface>
17-
<zone lrx="3830" lry="712" rotate="0.0" ulx="623" uly="498"/>
17+
<zone rotate="0.0"/>
1818
</surface>
1919
</facsimile>
2020
<body>

src/Dashboard/UploadFileManager.ts

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class UploadFileManager {
4747
}
4848
}
4949

50-
public createMeiFile(filename: string, lrx: number, lry: number): File {
50+
public createMeiFile(filename: string, width: number, height: number, staffSpace: number): File {
5151
try {
5252
if (!this.meiTemplate) {
5353
throw new Error('Cannot find MEI template');
@@ -73,11 +73,20 @@ class UploadFileManager {
7373
const surface = mei.querySelector('surface');
7474
const surfaceId = 'm-' + uuidv4();
7575
surface.setAttribute('xml:id', surfaceId);
76-
surface.setAttribute('lrx', lrx.toString());
77-
surface.setAttribute('lry', lry.toString());
76+
surface.setAttribute('lrx', width.toString());
77+
surface.setAttribute('lry', height.toString());
78+
7879
const zone = mei.querySelector('zone');
7980
const zoneId = 'm-' + uuidv4();
8081
zone.setAttribute('xml:id', zoneId);
82+
const marginRateV = 0.1;
83+
const marginRateH = 0.15;
84+
const marginV = Math.round(marginRateV * width);
85+
const marginH = Math.round(marginRateH * width);
86+
zone.setAttribute('ulx', marginH.toString());
87+
zone.setAttribute('uly', marginV.toString());
88+
zone.setAttribute('lrx', (width - marginH).toString());
89+
zone.setAttribute('lry', (marginV + 3 * staffSpace).toString());
8190

8291
const mdiv = mei.querySelector('mdiv');
8392
mdiv.setAttribute('xml:id', 'm-' + uuidv4());
@@ -107,7 +116,7 @@ class UploadFileManager {
107116
}
108117
}
109118

110-
public getImgDimension(filename: string): Promise<{ width: number; height: number }> {
119+
public getImgDimension(filename: string): Promise<{ width: number; height: number, staffSpace: number }> {
111120
return new Promise((resolve, reject) => {
112121
const imgFile = this.getFile(filename);
113122

@@ -121,7 +130,51 @@ class UploadFileManager {
121130
reader.onload = (event) => {
122131
const img = new Image();
123132
img.onload = () => {
124-
resolve({ width: img.width, height: img.height });
133+
const canvas = document.createElement('canvas');
134+
const ctx = canvas.getContext('2d', { willReadFrequently: true });
135+
if (!ctx) {
136+
reject(new Error('Could not get 2D context'));
137+
return;
138+
}
139+
140+
canvas.width = img.width;
141+
canvas.height = img.height;
142+
ctx.drawImage(img, 0, 0);
143+
144+
// Binarization
145+
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
146+
const binaryThreshold = 127;
147+
const data = imageData.data;
148+
for (let i = 0; i < data.length; i += 4) {
149+
const grayscale = (data[i] + data[i + 1] + data[i + 2]) / 3;
150+
const binaryValue = grayscale < binaryThreshold ? 0 : 255;
151+
data[i] = binaryValue;
152+
data[i + 1] = binaryValue;
153+
data[i + 2] = binaryValue;
154+
}
155+
ctx.putImageData(imageData, 0, 0);
156+
157+
// Finding staff space
158+
const whiteRunLengths: number[] = [];
159+
let currSpaceCount = 0;
160+
for (let x = 0; x < canvas.width; x++) {
161+
// new currSpaceCount for every column
162+
const column = ctx.getImageData(x, 0, 1, canvas.height).data;
163+
for (let p = 0; p < column.length; p += 4) {
164+
// Check if the pixel is black (0) or white (255)
165+
if (column[p] === 0 && currSpaceCount > 0) {
166+
whiteRunLengths.push(currSpaceCount);
167+
currSpaceCount = 0;
168+
} else {
169+
currSpaceCount++;
170+
}
171+
}
172+
}
173+
174+
// Get the second most common value as staff space
175+
const staffSpace = whiteRunLengths.length > 0 ? this.findSecondMode(whiteRunLengths) : 0;
176+
177+
resolve({ width: img.width, height: img.height, staffSpace: staffSpace });
125178
};
126179
img.onerror = () => {
127180
reject(new Error(`Failed to load image: ${filename}`));
@@ -133,6 +186,20 @@ class UploadFileManager {
133186
});
134187
}
135188

189+
private findSecondMode(arr: number[]): number {
190+
const countMap: Map<number, number> = new Map();
191+
192+
// Count occurrences of each element
193+
arr.forEach(element => {
194+
const count = (countMap.get(element) || 0) + 1;
195+
countMap.set(element, count);
196+
});
197+
198+
const sortedMap = Array.from(countMap.entries()).sort((a, b) => b[1] - a[1]);
199+
200+
return sortedMap[1][0];
201+
}
202+
136203

137204
public getFile(key: string): File {
138205
if (this.allFiles.has(key)) {

src/Dashboard/UploadTools.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@ export function handleMakePair(): void {
107107
// Create new MEI file
108108
fm.getImgDimension(image_filename)
109109
.then((dimensions) => {
110-
const { width, height } = dimensions;
111-
const newMeiFile = fm.createMeiFile(mei_filename, width, height);
110+
const { width, height, staffSpace } = dimensions;
111+
const newMeiFile = fm.createMeiFile(mei_filename, width, height, staffSpace);
112112
fm.addFile(newMeiFile);
113113
})
114114
.catch((error) => {

0 commit comments

Comments
 (0)